是否可以在linq中使用反射到实体? [英] Is it possible to use reflection with linq to entity?

查看:61
本文介绍了是否可以在linq中使用反射到实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过创建一种扩展方法来对代码进行一些清理,以一般地处理过滤.

I'm trying to clean up my code a little by creating an extension method to generically handle filtering.

这是我要清除的代码.

var queryResult = (from r in dc.Retailers select r);
if (!string.IsNullOrEmpty(firstName))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(firstName.Trim(), ex.FirstName.Trim()) > 0);
if (!string.IsNullOrEmpty(lastName))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(lastName.Trim(), ex.LastName.Trim()) > 0);
if (!string.IsNullOrEmpty(companyName))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(companyName.Trim(), ex.CompanyName.Trim()) > 0);
if (!string.IsNullOrEmpty(phone))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(phone.Trim(), ex.Phone.Trim()) > 0);
if (!string.IsNullOrEmpty(email))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(email.Trim(), ex.Email.Trim()) > 0);
if (!string.IsNullOrEmpty(city))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(city.Trim(), ex.City.Trim()) > 0);
if (!string.IsNullOrEmpty(zip))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(zip.Trim(), ex.Zip.Trim()) > 0);
if (!string.IsNullOrEmpty(country))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(country.Trim(), ex.Country.Trim()) > 0);
if (!string.IsNullOrEmpty(state))
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(state.Trim(), ex.State.Trim()) > 0);

这显然是重复的.因此,我尝试创建一种扩展方法,该扩展方法使用反射通过属性进行过滤.这是方法.

It's clearly very repetitious. So I tried to create an extension method that filtered by a property using reflection. Here is the method.

public static void FilterByValue<T>(this IQueryable<T> obj, string propertyName, string propertyValue)
{
    if (!string.IsNullOrEmpty(propertyValue))
    {
        obj =
            obj.Where(
                ex =>
                    SqlFunctions.PatIndex(propertyValue.Trim(), (string)typeof(T).GetProperty(propertyName,
                        BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase).GetValue(ex)) > 0
                    );
    }
}

就像这样被称呼:

var queryResult = (from r in dc.Retailers select r);
queryResult.FilterByValue("firstname", firstName);

但是,当执行linq时出现错误,指出linq中无法识别出"GetValue".

But, I get an error when the linq executes, stating that "GetValue" isn't recognized in linq to entity.

那么,还有其他方法可以清理掉它吗,还是我必须让它丑陋?

So, is there any other way to clean this up, or do I have to leave it ugly?

推荐答案

从技术上讲,是可以的,但是您需要自己构造Expression才能传递给Where.

Technically, yes, you could do it, but you'd need to construct the Expression yourself to pass to Where.

也就是说,您应该考虑接受Expression<Func<T, string>>作为参数,而不是接受该属性作为字符串值,以便为验证所选对象有效提供编译时支持.

That said, rather than accepting the property as a string value you should consider instead accepting an Expression<Func<T, string>> as a parameter so that you have compile time support for verifying that the selected object is valid.

我们将从一个表示通用部分的表达式开始;它代表一个带有*两个*参数的函数,即对象和给定属性的值.然后,我们可以用在实际方法的参数中定义的属性选择器替换该第二个参数的所有实例.

We'll start out with an expression that represents the generic portion; it'll represent a function with*two* parameters, the object and the value of the given property. We can then replace all instances of that second parameter with the property selector that we've defined in the actual method's parameters.

public static IQueryable<T> FilterByValue<T>(
    this IQueryable<T> obj,
    Expression<Func<T, string>> propertySelector,
    string propertyValue)
{
    if (!string.IsNullOrEmpty(propertyValue))
    {
        Expression<Func<T, string, bool>> expression =
            (ex, value) => SqlFunctions.PatIndex(propertyValue.Trim(),
                value.Trim()) > 0;

        var newSelector = propertySelector.Body.Replace(
            propertySelector.Parameters[0],
            expression.Parameters[0]);

        var body = expression.Body.Replace(expression.Parameters[1], 
            newSelector);
        var lambda = Expression.Lambda<Func<T, bool>>(
            body, expression.Parameters[0]);

        return obj.Where(lambda);
    }
    else
        return obj;
}

此方法使用一个函数将给定表达式中一个表达式的所有实例替换为另一个实例.的实现是:

And this method uses a function to replace all instances of one expression with another in a given expression. The implementation of that is:

public class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

如果您真的要接受属性名称作为字符串,则只需将newSelector的定义替换为以下内容:

If you really want to accept the property name as a string then just replace the definition of newSelector with the following:

var newSelector = Expression.Property(expression.Parameters[0], propertyName);

这篇关于是否可以在linq中使用反射到实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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