I have two DbSets, Foo and Bar. Foo has an identifying string property, FooName, and Bar has an identifying string property, BarName.
我有两个DbSets,Foo和Bar。 Foo有一个标识字符串属性FooName,而Bar有一个标识字符串属性BarName。
I am designing a very simple search feature, where a user's query term can either be equal to, or contained in the identifying name.
我正在设计一个非常简单的搜索功能,其中用户的查询字词可以等于或包含在识别名称中。
So I have two methods (heavily simplified):
所以我有两种方法(大大简化):
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
}
Clearly I want some helper method like so:
显然我想要一些辅助方法,如下所示:
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
}
I originally tried to add a Func<T, string>
to the Search parameters, where I could use f => f.FooName
and b => b.BarName
respectively, but LINQ to Entities doesn't support a lambda expression during the execution of the query.
我最初尝试在搜索参数中添加一个Func
I've been scratching my head as to how I can extract this duplication.
我一直在摸索如何提取这种重复。
4 个解决方案
#1
1
You can achieve this with Expression<Funt<T,string>>
你可以用Expression
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();
}
Then you'll call it:
那你就叫它:
Search ("myValue", fooSet, foo=>foo.FooName);
if you can have a static method, then you could have it as an extension method:
如果您可以使用静态方法,那么您可以将其作为扩展方法:
public static IList<T> Search<T>(this DbSet<T> set,
string query, Expression<Func<T, string>> propExp)
And call it:
并称之为:
FooSet.Search ("myValue", foo=>foo.FooName);
#2
0
Here's one way to do it. First you need a helper method to generate the Expression
for you:
这是一种方法。首先,您需要一个帮助方法来为您生成表达式:
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);
}
This is how you can use this method, propertyName
would be FooName and BarName:
这是你如何使用这个方法,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;
}
#3
0
You can create interface:
你可以创建界面:
public interface IName
{
string Name { get; set; }
}
Then implicitly implement IName
interface in both entities.
然后在两个实体中隐式实现IName接口。
public class Bar : IName { ... }
public class Foo : IName { ... }
And then change your method as:
然后将您的方法更改为:
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
}
#4
0
You could overide your ToString() method and use that in the query
您可以覆盖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
}
#1
1
You can achieve this with Expression<Funt<T,string>>
你可以用Expression
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();
}
Then you'll call it:
那你就叫它:
Search ("myValue", fooSet, foo=>foo.FooName);
if you can have a static method, then you could have it as an extension method:
如果您可以使用静态方法,那么您可以将其作为扩展方法:
public static IList<T> Search<T>(this DbSet<T> set,
string query, Expression<Func<T, string>> propExp)
And call it:
并称之为:
FooSet.Search ("myValue", foo=>foo.FooName);
#2
0
Here's one way to do it. First you need a helper method to generate the Expression
for you:
这是一种方法。首先,您需要一个帮助方法来为您生成表达式:
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);
}
This is how you can use this method, propertyName
would be FooName and BarName:
这是你如何使用这个方法,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;
}
#3
0
You can create interface:
你可以创建界面:
public interface IName
{
string Name { get; set; }
}
Then implicitly implement IName
interface in both entities.
然后在两个实体中隐式实现IName接口。
public class Bar : IName { ... }
public class Foo : IName { ... }
And then change your method as:
然后将您的方法更改为:
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
}
#4
0
You could overide your ToString() method and use that in the query
您可以覆盖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
}