本文介绍了一个变异predicate的前pression树的目标另一种类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我目前工作的应用程序,有两种每个业务对象:以ActiveRecord的类和DataContract的一种。因此,例如,将有:

In the application I 'm currently working on, there are two kinds of each business object: the "ActiveRecord" kind and the "DataContract" kind. So for example, there would be:

namespace ActiveRecord {
    class Widget {
        public int Id { get; set; }
    }
}

namespace DataContract {
    class Widget {
        public int Id { get; set; }
    }
}

数据库访问层需要家庭之间转换的护理:你可以告诉它更新 DataContract.Widget ,它会奇迹般地创建一个的ActiveRecord .Widget 具有相同的属性值并保存替代。

The database access layer takes care of translating between families: you can tell it to update a DataContract.Widget and it will magically create an ActiveRecord.Widget with the same property values and save that instead.

在试图重构这个数据库访问层时,这个问题浮出水面。

The problem surfaced when attempting to refactor this database access layer.

我要添加类似下面的数据库访问层的方法:

I want to add methods like the following to the database access layer:

// Widget is DataContract.Widget

interface IDbAccessLayer {
    IEnumerable<Widget> GetMany(Expression<Func<Widget, bool>> predicate);
}

以上是一个简单的一般用途的得的方法定制predicate。唯一关注的一点是,我在离pression树正在传递一个lambda,而不是因为里面的 IDbAccessLayer 我查询了的IQueryable&LT; ActiveRecord.Widget&GT; ;要做到这一点有效的(个人认为LINQ到SQL)我需要在离pression树传递所以这种方法要求这一点。

The above is a simple general-use "get" method with custom predicate. The only point of interest is that I am passing in an expression tree instead of a lambda because inside IDbAccessLayer I am querying an IQueryable<ActiveRecord.Widget>; to do that efficiently (think LINQ to SQL) I need to pass in an expression tree so this method asks for just that.

的障碍:该参数需要从防爆pression&LT神奇地转化; Func键&LT; D​​ataContract.Widget,布尔&GT;&GT; 防爆pression&LT;&Func键LT; ActiveRecord.Widget,布尔&GT;方式&gt;

The snag: the parameter needs to be magically transformed from an Expression<Func<DataContract.Widget, bool>> to an Expression<Func<ActiveRecord.Widget, bool>>.

我想里面有什么 GetMany 做的是:

What I 'd like to do inside GetMany is:

IEnumerable<DataContract.Widget> GetMany(
    Expression<Func<DataContract.Widget, bool>> predicate)
{
    var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
        predicate.Body,
        predicate.Parameters);

    // use lambda to query ActiveRecord.Widget and return some value
}

这将不会在一个典型的场景中,因为工作,例如,如果

This won't work because in a typical scenario, for example if:

predicate == w => w.Id == 0;

...恩pression树包含有类型的属性 MemberAccessEx pression 实例的MemberInfo 描述 DataContract.Widget.Id
还有无论是在除权pression树和它的参数集合 ParameterEx pression 实例( predicate.Parameters )来说明 DataContract.Widget ;所有这一切都将导致错误,因为在可查询的身体不包含类型的widget而是 ActiveRecord.Widget

...the expression tree contains a MemberAccessExpression instance which has a property of type MemberInfo that describes DataContract.Widget.Id.There are also ParameterExpression instances both in the expression tree and in its parameter collection (predicate.Parameters) that describe DataContract.Widget; all of this will result in errors since the queryable body does not contain that type of widget but rather ActiveRecord.Widget.

搜索了一下后,我发现<一个href=\"http://msdn.microsoft.com/en-us/library/system.linq.ex$p$pssions.ex$p$pssionvisitor%28v=VS.100%29.aspx\"><$c$c>System.Linq.Ex$p$pssions.Ex$p$pssionVisitor (它的来源可以在这里找到 几个重要事项一起把这个长的问题变得更长时间

After taking into account the correct answers to the problem (two of them below; one short, one with code) the problem was solved; I put the code along with a few important notes in a separate answer to keep this long question from becoming even longer.

感谢大家对你的答案和评论!

Thanks to everyone for your answers and comments!

推荐答案

看来你正在生成的参数e pression两次,VisitMember()位置:

It seems you're generating the parameter expression twice, in VisitMember() here:

var converted = Expression.MakeMemberAccess(
    base.Visit(node.Expression),
    activeRecordType.GetProperty(node.Member.Name));

...因为base.Visit()将在VisitParameter最终我想象,在GetMany()本身:

...since base.Visit() will end up in VisitParameter I imagine, and in GetMany() itself:

var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
    visitor.Visit(predicate.Body),
    predicate.Parameters.Select(p => visitor.Visit(p));

如果你在身体使用ParameterEx pression,它必须是相同的实例(而不仅仅是相同类型和名称)作为一个申报拉姆达。
我有问题之前,这种情况下,虽然我认为结果是,我只是没有能够创造出前pression,它只是抛出一个异常。在任何情况下,你可以尝试重用参数实例,看看是否有帮助。

If you're using a ParameterExpression in the body, it has to be the same instance (not just the same type and name) as the one declared for the Lambda. I've had problems before with this kind of scenario, though I think the result was that I just wasn't able to create the expression, it would just throw an exception. In any case you might try reusing the parameter instance see if it helps.

这篇关于一个变异predicate的前pression树的目标另一种类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-26 23:02