当查询仅由属性不同时,如何解决LINQ to Entity查询重复?

时间:2017-03-19 06:27:29

标签: c# asp.net-mvc entity-framework linq linq-to-entities

我有两个DbSets,Foo和Bar。 Foo具有标识字符串属性FooName,而Bar具有标识字符串属性BarName。

我正在设计一个非常简单的搜索功能,用户的查询字词可以等于或包含在识别名称中。

所以我有两种方法(大大简化):

public ActionView SearchFoo(string query) 
{
    var equalsQuery = db.Foo.Where(f => f.FooName.Equals(query));
    var containsQuery = db.Foo.Where(f => f.FooName.Contains(query)).Take(10); // Don't want too many or else a search for "a" would yield too many results

    var result = equalsQuery.Union(containsQuery).ToList();
    ... // go on to return a view
}


public ActionView SearchBar(string query) 
{
    var equalsQuery = db.Bar.Where(f => f.BarName.Equals(query));
    var containsQuery = db.Bar.Where(f => f.BarName.Contains(query)).Take(10); // Don't want too many or else a search for "a" would yield too many results

    var result = equalsQuery.Union(containsQuery).ToList();
    ... // go on to return a view
}

显然我想要一些像这样的辅助方法:

public IList<T> Search<T>(string query, DbSet<T> set) 
{
    var equalsQuery = set.Where(f => ???.Equals(query));
    var containsQuery = set.Where(f => ???.Contains(query)).Take(10); // Don't want too many or else a search for "a" would yield too many results

    var result = equalsQuery.Union(containsQuery).ToList();
    ... // go on to return a view
}

我最初尝试在搜索参数中添加Func<T, string>,我可以分别使用f => f.FooNameb => b.BarName,但LINQ to Entities在执行期间不支持lambda表达式的查询。

我一直在摸索如何提取这种重复。

4 个答案:

答案 0 :(得分:1)

您可以使用Expression<Funt<T,string>>

实现此目的
public IList<T> Search<T>(string query, DbSet<T> set, Expression<Func<T, string>> propExp)
{
    MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    ConstantExpression someValue = Expression.Constant(query, typeof(string));
    MethodCallExpression containsMethodExp = 
             Expression.Call(propExp.Body, method, someValue);
    var e = (Expression<Func<T, bool>>)
              Expression.Lambda(containsMethodExp, propExp.Parameters.ToArray());
    var containsQuery = set.Where(e).Take(10);

    BinaryExpression equalExpression = Expression.Equal(propExp.Body, someValue);
    e = (Expression<Func<T, bool>>)
                 Expression.Lambda(equalExpression, propExp.Parameters.ToArray());

    var equalsQuery =  set.Where(e);

    var result = equalsQuery.Union(containsQuery).ToList();
}

然后你打电话给它:

Search ("myValue", fooSet, foo=>foo.FooName);

如果您可以使用静态方法,则可以将其作为扩展方法:

public static IList<T> Search<T>(this DbSet<T> set, 
                                  string query, Expression<Func<T, string>> propExp)

并称之为:

FooSet.Search ("myValue", foo=>foo.FooName);

答案 1 :(得分:0)

您可以创建界面:

public interface IName
{ 
    string Name { get; set; }
} 

然后在两个实体中隐式实现IName接口。

 public class Bar : IName { ... }
 public class Foo : IName { ... }

然后将您的方法更改为:

public IList<T> SearchByName<T>(string query, DbSet<T> set) 
      where T: class, IName
{
    var equalsQuery = set.Where(f => f.Name.Equals(query));
    var containsQuery = set.Where(f => f.Name.Contains(query)).Take(10); // Don't want too many or else a search for "a" would yield too many results

    var result = equalsQuery.Union(containsQuery).ToList();
    ... // go on to return a view
}

答案 2 :(得分:0)

您可以覆盖 ToString()方法并在查询中使用该方法

public class foo
{
    public string FooName
    {
        get;
        set;
    }

    public override string ToString()
    {
        return FooName;
    }
}

public class Bar
{
    public string BarName
    {
        get;
        set;
    }

    public override string ToString()
    {
        return BarName;
    }
}

public IList<T> Search<T>(string query, DbSet<T> set)
{
    var equalsQuery = set.AsEnumerable().Where(f => f.ToString().Equals(query));
    var containsQuery = set.AsEnumerable().Where(f => f.ToString().Contains(query)).Take(10); 
    var result = equalsQuery.Union(containsQuery).ToList(); . . . // go on to return a view
}

答案 3 :(得分:0)

这是一种方法。 首先,您需要一个辅助方法来为您生成Expression

private Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue, string operatorName)
{
    var parameterExp = Expression.Parameter(typeof(T));
    var propertyExp = Expression.Property(parameterExp, propertyName);
    MethodInfo method = typeof(string).GetMethod(operatorName, new[] { typeof(string) });
    var someValue = Expression.Constant(propertyValue, typeof(string));
    var methodExp = Expression.Call(propertyExp, method, someValue);

    return Expression.Lambda<Func<T, bool>>(methodExp, parameterExp);
}

这是您使用此方法的方法,propertyName FooName BarName

public IList<T> Search<T>(string propertyName, string query, DbSet<T> set) 
{
    var equalsQuery = set.Where(GetExpression<T>(propertyName, query, "Equals"));
    var containsQuery = set.Where(GetExpression<T>(propertyName, query, "Contains")).Take(10); // Don't want too many or else a search for "a" would yield too many results

    var result = equalsQuery.Union(containsQuery).ToList();
    return result;
}