Linq表达式作为参数 [英] Linq expressions as params

查看:180
本文介绍了Linq表达式作为参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从我的存储库调用一个 IQueryable< T> 方法,但是我想在我的ORM中加载子对象。为了将持久性逻辑保留在持久层中,我想传递一个表达我想要加载的属性的表达式列表。



我的 IRepository< ; TClass> 方法如下所示:

  IQueryable< TClass> AsQueryable(); 

我想让它看起来像:

  IQueryable< TClass> AsQueryable(params --something here  -  [] includeProperties); 

...所以我可以这样称呼:

  var q = myFooRepository.AsQueryable(x => x.Property1,x => x.Property2,...); 

并在后端反汇编代理,以热切加载指定的属性(ies)。 >

我应该怎么用这个?

解决方案

您的 AsQueryable< TClass> 应该具有属性表达式作为参数,并且需要具有以下签名:

  public static IQueryable< TClass> AsQueryable< TClass>(此TClass obj,params Expression< Func< TClass,object>> [] propertyExpressions)



请注意,我们使用 Func< TClass,object> ,这是接受TClass作为输入并返回对象的函数。这可以让我们打个电话:

  IQueryable< TClass> tClassQueryable = tClassObj.AsQueryable(x => x.Property1,x => x.Property2); 

另请注意,我没有选择一个对象作为 Func< TClass,object> 的TResult。由于函数委托的TResult通用参数是协方差的,所以即使使用不同的属性类型也可以传递表达式。所以在上面的例子中你的Property1和Property2不需要是相同的类型。



就是这样,就你的问题我猜,但是这里有一点额外的:



如果偶然需要评估传递的表达式以便将其与您的ORM一起使用(例如,您只需要属性名称,但是您要将其作为表达式传递为避免硬编码名称并保留编译时间检查),您需要这样的东西:

  public static IQueryable< TClass> AsQueryable< TClass>(此TClass obj,params Expression< Func< TClass,object>> [] propertyExpressions)
{
foreach(propertyExpressions中的var propertyExpression)
{
MemberExpression memberExpression = propertyExpression.Body as MemberExpression;

if(memberExpression == null)
{
//这是值类型属性所必需的。
UnaryExpression unaryExpression =(UnaryExpression)propertyExpression.Body;
memberExpression = unaryExpression.Operand作为MemberExpression;
}

if(memberExpression == null)
throw new ArgumentException(string.Format(Expression{0}不是成员表达式,propertyExpression.ToString ()));

PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
if(propertyInfo == null)
throw new ArgumentException(MemberExpression.Member不是PropertyInfo);


//在这一点上,我们有PropertyInfo,您可以与您的OR Mapper一起使用,以进一步实现将迫切加载属性
//的逻辑。可以通过以下方式检索属性名称:
string propertyName = propertyInfo.Name;

//在这里做你的ORM的东西
}
}

上面的代码确保传递的表达式是属性表达式,并从中提取PropertyInfo。


I want to call an IQueryable<T> method from my repository, but I want to eagerly load child objects in my ORM. To keep the persistence logic in the persistence layer, I want to pass a list of expressions representing the properties I want to eagerly load.

My IRepository<TClass> method looks like this:

IQueryable<TClass> AsQueryable();

I would like to make it look like :

IQueryable<TClass> AsQueryable(params --something here--[] includeProperties);

...so that I can call it like:

var q = myFooRepository.AsQueryable(x => x.Property1, x => x.Property2, ...);

and disassemble the delegate on the back end to eagerly load the specified property(ies).

What should I be using for this?

解决方案

Your AsQueryable<TClass> should have property expressions as parameters and it would need to have the following signature:

public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)

Note that we are using Func<TClass, object> which is a function acepting TClass as input and returning an object. That allows us to make a call as follows:

IQueryable<TClass> tClassQueryable = tClassObj.AsQueryable(x => x.Property1, x => x.Property2);

Also note that I did not choose an object as the TResult of Func<TClass, object> by chance. Since the TResult generic parameter of the function delegate is covariant, that allows us to pass expressions even with different property types. So your Property1 and Property2 in the example above do not need to be of same type.

That would be it, regarding your question I guess, but here is a little extra:

If you by chance need to evaluate the passed expressions in order to use them with your ORM (e.g. you just need property names but you want to pass them as expressions in order to avoid hardocoding names and preserve compile-time checks), you would need something like this:

  public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)
    {
        foreach (var propertyExpression in propertyExpressions)
        {
            MemberExpression memberExpression = propertyExpression.Body as MemberExpression;

            if (memberExpression == null)
            {
                // this is needed for value types properties.
                UnaryExpression unaryExpression = (UnaryExpression)propertyExpression.Body;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }

            if (memberExpression == null)
                throw new ArgumentException(string.Format("Expression '{0}' is not a member expression.", propertyExpression.ToString()));

            PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
            if (propertyInfo == null)
                throw new ArgumentException("MemberExpression.Member is not a PropertyInfo.");


            // at this point we have PropertyInfo which you can use with your OR Mapper to further implement logic which will eager load the property
            // e.g. property name can be retrieved with:
            string propertyName = propertyInfo.Name;

            // do your ORM stuff here
        }
    }

The code above ensures that the passed expressions are property expressions and it extracts PropertyInfo from it.

这篇关于Linq表达式作为参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆