我的应用程序的部分数据层是类似于 System.Data.DataRowExtensions.UnboxT
的转换器缓存, 在 Linq-to-Data 中使用。缓存所有已知类型的前提我生成一个简单的转换器并缓存它。转换器将对象转换为 T,或者在 DBNull.Value
的情况下, 返回 default(T)
(与 UnboxT
不同,后者会在非可空值类型上引发异常。)我不能使用 UnboxT 的原因是我们的开发人员不喜欢在从数据行分配值之前检查 DBNull,他们只是希望它完成他们。
我们还有一个工厂来生成从 DataRow 实例化对象的助手委托(delegate),在委托(delegate)中添加逻辑会很烦人。
即它生成这样的东西::
datarow =>
new MyObject()
{
property1 = DBConverterCache<TProperty1>.Converter(datarow[columnName1]),
property2 = DBConverterCache<TProperty2>.Converter(datarow[columnName2]),
/*etc...*/
};
更何况,我还有一个烦恼。对象层中的对象可能无法与数据库中的对象正确匹配。这是一个问题,因为您不能将东西拆箱为“错误”类型。 IE。对象层中的属性是 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 接口(interface)来添加更多可转换对象的假设。这是一个 hack,但它避免了对返回类型进行装箱。 IConvertible.ToType 这样的方法可以做到这一点。
当然这也行得通:
value => value == DBNull.Value ? default(T) : (T)(value as dynamic);
也许更好,因为我不必根据我可以将其设为我的默认转换器的类型专门化调用。唯一的问题是我不知道如何使用 Expression.Dynamic,而且我无法创建将动态作为参数的表达式。我想我可以将它绑定(bind)到静态方法或上面的 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();
}
似乎工作得很好。
关于c# - 使用 Expression.Dynamic 为值类型生成转换器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3909540/