[seasar-dotnet:468] S2Container.NET S2Dao.NET Nullableな列挙型プロパティへのデータのマッピングへの対応要望

t.hada t_hada @ atlascorp.co.jp
2007年 3月 1日 (木) 14:10:49 JST


お世話になります。
羽田と申します。

S2Dao.NET 1.0.3利用時に列挙型のプロパティにデータをマップすることができ
ていたので、
.NET2.0のNullableな列挙体のプロパティにデータをマップしてみたところ、
Nullableな列挙体に変換できないとのエラーが発生してしまいました。

原因を調査しましたところ、Seasar.Extension.ADO.ValueTypesは
明示的に列挙型向けの実装をしておらず、
通常の列挙型への変換はSystem.Reflection.PropertyInfoの動作として
行われているらしいことがわかりました。

列挙型の利用はステータス系のプロパティに関する生産性を
向上させると思いますので対応していただけると嬉しいです。

Nullableな列挙型についてはNullableEnumType<TEnum>のようなものを実装し、
列挙型ごとに個別登録すれば対応できるような気もするのですが、
列挙型やNullable型であれば汎用的な実装が提供可能と思いますので、
一応、要望としてあげさせていただきます。

検討よろしくお願いします。


お願いするだけではなんですので、
一応、技術的な参考としての実装は作成してみました。

大雑把に説明すれば列挙型、Nullableな列挙型に対応する汎用的なEnumType、
NullableEnumTypeを用意いたしまして、ValueTypes.GetValueType(Type type)にて
特別視して解決しています。
一部、明示的にDbTypesを指定する必要がある場所につきましては、
列挙型の元となるデータ型を元にIValueTypeを解決し元となるデータ型の
IValueTypeへ委譲しています。

= Seasar.Extension.ADO.ValueTypes.EnumType

using System;
using System.Data;

namespace Seasar.Extension.ADO.Types
{
public class EnumType : PrimitiveBaseType, IValueType
{
private Type enumType;
private Type underlyingType;
private IValueType underlyingValueType;

public EnumType(Type enumType)
{
this.enumType = enumType;
this.underlyingType = Enum.GetUnderlyingType(this.enumType);
this.underlyingValueType = ValueTypes.GetValueType(this.underlyingType);
}

public override void BindValue(IDbCommand cmd, string columnName, object
value)
{
object convertedValue = value == null ?
null :
Convert.ChangeType(value, this.underlyingType);
this.underlyingValueType.BindValue(cmd, columnName, convertedValue);
}

protected override object GetBindValue(object value)
{
if(value == null) {
return null;
}
return Convert.ChangeType(value, this.underlyingType);
}

protected override object GetValue(object value)
{
if(value == DBNull.Value) {
return null;
} else {
return Enum.ToObject(this.enumType, value);
}
}
}
}


= Seasar.Extension.ADO.ValueTypes.NullableEnumType

using System;
using System.Data;

namespace Seasar.Extension.ADO.Types
{
public class NullableEnumType : NullableBaseType
{
private Type enumType;
private Type underlyingType;
private IValueType underlyingValueType;

public NullableEnumType(Type enumType)
{
this.enumType = enumType;
this.underlyingType = Enum.GetUnderlyingType(this.enumType);
this.underlyingValueType = ValueTypes.GetValueType(this.underlyingType);
}

public override void BindValue(IDbCommand cmd, string columnName, object
value)
{
object convertedValue = value == null ?
null:
Convert.ChangeType(value, this.underlyingType);

this.underlyingValueType.BindValue(cmd, columnName, convertedValue);
}

protected override object GetBindValue(object value)
{
if(value == null) {
return DBNull.Value;
}
return Convert.ChangeType(value, this.underlyingType);
}

protected override object GetValue(object value)
{
if(value == DBNull.Value) {
return null;
}
return Enum.ToObject(this.enumType, value);
}
}
}



= Seasar.Extension.ADO.ValueTypes.ValueTypes

// ValueTypes.GetValueTypeへの変更分です。
public static IValueType GetValueType(Type type)
{
if(type == null) return OBJECT;

IValueType valueType = GetValueType0(type);
if(valueType != null)
{
return valueType;
}
else if(type.IsEnum)
{
return new EnumType(type);
}
#if !NET_1_1
else if(IsNullableType(type))
{
Type nullableTType = GetNullableTType(type);
if(nullableTType.IsEnum)
{
return new NullableEnumType(nullableTType);
}
}
#endif
return OBJECT;
}

#if !NET_1_1
private static bool IsNullableType(Type type)
{
return type.IsGenericType &&
type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}

private static Type GetNullableTType(Type type)
{
return type.GetGenericArguments()[0];
}
#endif


Nullableな列挙型へのマップ時に発生したエラーは下記のようになります。

System.ArgumentException : 型 'System.Byte' のオブジェクトを型
'System.Nullable`1[列挙型]' に変換できません。

場所 System.RuntimeType.CheckValue(Object value, Binder binder,
CultureInfo culture, BindingFlags invokeAttr)
場所 System.Reflection.MethodBase.CheckArguments(Object[] parameters,
Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags
invokeAttr, Binder binder, Object[] parameters, CultureInfo culture,
Boolean skipVisibilityChecks)
場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags
invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
場所 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object
value, BindingFlags invokeAttr, Binder binder, Object[] index,
CultureInfo culture)
場所 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object
value, Object[] index)
場所
Seasar.Dao.Impl.AbstractBeanMetaDataDataReaderHandler.CreateRow(IDataReader
reader, IColumnMetaData[] columns)
場所
Seasar.Dao.Impl.BeanListMetaDataDataReaderHandler.Handle(IDataReader
dataReader, IList list)
場所
Seasar.Dao.Impl.BeanGenericListMetaDataDataReaderHandler.Handle(IDataReader
dataReader)
場所 Seasar.Extension.ADO.Impl.BasicSelectHandler.Execute(IDbCommand cmd)
場所 Seasar.Extension.ADO.Impl.BasicSelectHandler.Execute(IDbConnection
connection, Object[] args, Type[] argTypes)
場所 Seasar.Extension.ADO.Impl.BasicSelectHandler.Execute(Object[] args,
Type[] argTypes)
場所 Seasar.Dao.Impl.SelectDynamicCommand.Execute(Object[] args)
場所 Seasar.Dao.Interceptors.S2DaoInterceptor.Invoke(IMethodInvocation
invocation)
場所 Seasar.Framework.Aop.Proxy.AopProxy.Invoke(IMessage msg)
場所
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&
msgData, Int32 type)

以上よろしくお願いします。

-- 
/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
株式会社アトラス
羽田 拓央
mail:   t_hada @ atlascorp.co.jp
/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/




seasar-dotnet メーリングリストの案内