使用 Expression.Dynamic 生成值类型的转换器

发布于 2024-09-27 08:14:30 字数 1401 浏览 2 评论 0原文

我的应用程序的数据层的一部分是类似于 Linq-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,DB中的列是Int64。为了解决这个问题,我对 struct IConvertibles 转换器基本上执行以下操作::

value => value == DBNull.Value ? default(T) : (value as IConvertible).To<Type*>(null);

* 在 Nullable的情况下或 Enum 它转换为基础类型,然后转换为 T

这很丑陋,因为我们必须使用反射来生成对 ToType 的调用,这依赖于它们永远不会扩展的假设IConvertible 接口添加更多可转换对象。这是一个 hack,但它避免了对返回类型的装箱。像 IConvertible.ToType 这样的方法就是这样做的。

当然,这也同样有效:

value => value == DBNull.Value ? default(T) : (T)(value as dynamic);

也许甚至更好,因为我不必根据类型专门化调用,我可以将其设为默认转换器。唯一的问题是我不知道如何使用 Expression.Dynamic,并且无法创建以动态作为参数的表达式。我想我可以将它绑定到静态方法或上面的 lambda 表达式,但如果可能的话,我希望将所有操作都作为表达式树进行。

Part of the data layer for my application is a converter cache similar to System.Data.DataRowExtensions.UnboxT, used in Linq-to-Data. The premise of the cache for all known types I generate a simple converter and cache it. The converter casts the object as T, or in the case of DBNull.Value, returns default(T) (unlike UnboxT which throws an exception on non-nullable value types.) The reason I can't use UnboxT is that our developers don't like checking for DBNull before assigning a value from the datarow and they just want it done for them.
We also have a factory to generate helper delegate that instantiates objects off of a DataRow, and it'd be annoying to add logic in the delegate.
i.e. it generates something like this this::

datarow =>
    new MyObject()
    {
        property1 = DBConverterCache<TProperty1>.Converter(datarow[columnName1]),
        property2 = DBConverterCache<TProperty2>.Converter(datarow[columnName2]),
        /*etc...*/

    };

Even more so, I have another annoyance. Objects in the object layer may not match correctly to ones in the database. This is a problem because you can't unbox things to the "wrong" type. I.e. the property in the object layer is an Int32, the column in the DB is an Int64. To fix this I have for the struct IConvertibles the converter basically does this::

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

This is ugly as we have to use reflection to generate the call to ToType, which relies on the assumption that they will never expand the IConvertible interface to add more objects that are convertible. It's a hack but it avoids boxing the return type. Which a method like IConvertible.ToType does.

of course this works just as well:

value => value == DBNull.Value ? default(T) : (T)(value as dynamic);

Maybe even better, as I don't have to specialize the call based on the type I can just make that my default converter. The only issue I haven't a clue as to how to use Expression.Dynamic, and I can't create an expression that takes dynamic as a parameter. I suppose I could just bind it to a static method or the above lambda expression, but I'd like to do everything as expression trees, if possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

脸赞 2024-10-04 08:14:30

好吧,我用一些旧的反光镜让它工作了。

    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();
    }

似乎工作得很好。

Well I got this to work, with some good old reflector.

    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();
    }

Seems to be working just fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文