我的应用程序的数据层的一部分是类似于Lincc-to-Data中使用的System.Data.DataRowExtensions.UnboxT
的转换器缓存。为所有已知类型缓存的前提是生成一个简单的转换器并将其缓存。转换器将对象强制转换为T,或者在DBNull.Value
的情况下返回default(T)
(与UnboxT
不同,在非空值类型上引发异常)。我不能使用UnboxT的原因是我们的开发人员不喜欢在从数据行分配值之前检查DBNull,而他们只是希望为他们完成。
我们还有一个工厂来生成辅助委托,该委托将实例化DataRow中的对象,并且在委托中添加逻辑会很烦人。
即它生成这样的东西:
datarow =>
new MyObject()
{
property1 = DBConverterCache<TProperty1>.Converter(datarow[columnName1]),
property2 = DBConverterCache<TProperty2>.Converter(datarow[columnName2]),
/*etc...*/
};
更何况,我还有另一个烦恼。对象层中的对象可能与数据库中的对象不正确匹配。这是一个问题,因为您无法将内容拆箱为“错误”类型。即对象层中的属性是
Int32
,数据库中的列是Int64
。为了解决这个问题,我对转换器的IConvertibles结构基本上做到了:value => value == DBNull.Value ? default(T) : (value as IConvertible).To<Type*>(null);
* in the case of Nullable<T> or Enum it casts to underlying type, then casts up to T
这很丑陋,因为我们必须使用反射来生成对
ToType
的调用,这依赖于这样的假设:它们永远不会扩展IConvertible接口来添加更多可转换的对象。这是一个hack,但是避免了将返回类型装箱。像IConvertible.ToType这样的方法。当然,这同样适用:
value => value == DBNull.Value ? default(T) : (T)(value as dynamic);
甚至更好,因为我不必根据类型来专门调用,我可以将其设置为默认转换器。我不知道如何使用Expression.Dynamic的唯一问题,而且我无法创建将动态作为参数的表达式。我想我可以将其绑定到静态方法或上面的lambda表达式,但是如果可能的话,我想将所有内容都作为表达式树来做。
最佳答案
好吧,我有了一些好的旧反射器,使它可以工作。
public static Converter<Object, T> CreateDynamicConverter<T>()
{
var param = Expression.Parameter(typeof(object));
var expression = Expression.Lambda<Converter<object, T>>(
Expression.Condition(
Expression.Equal(
param,
Expression.Constant(
DBNull.Value
)
),
Expression.Default(
typeof(T)
),
Expression.Dynamic(
Binder.Convert(
CSharpBinderFlags.ConvertExplicit,
typeof(T),
typeof(MyApplicationNameHere)
),
typeof(T),
param
)
),
param
);
return expression.Compile();
}
似乎工作正常。