有没有办法让以下数据库查询构建器通用?
private IQueryable<Foo> ByName(IQueryable<Foo> dbQuery, Query query)
{
string[] searchTerms = query.Data.Replace(" ","").ToLower().Split(',');
if (query.Exclude)
{
return dbQuery.Where(x => searchTerms.All(
y => y != x.Name.Replace(" ", "").ToLower()));
}
return dbQuery.Where(x => searchTerms.Any(
y => y == x.Name.Replace(" ", "").ToLower()));
}
我为Foo的许多不同属性提供了相同的功能。 ByCounty,ByTown,ByStreet等等。
我已经编写了一些返回linq的函数,如下所示
public Expression<Func<Foo, bool>> FoosAreWithinDistanceFromGeocode(
double distance, Geocode geocode)
{
double distanceSquare = distance * distance;
return foo => ( SqlFunctions.Square((double)(
foo.Address.Geocode.Easting - geocode.Easting)) +
SqlFunctions.Square((double)(fooAddress.Geocode.Northing -
geocode.Northing)) ) <= distanceSquare;
}
但是我似乎无法找到Linq-to-SQL的东西是否不能使用泛型,或者是否可以将属性作为泛型和类似的东西传递。
编辑:我通常会针对单个搜索字词开展此工作。
Where [query.Data == "Foo1"]
return dbQuery.Where(SearchMatch("Name", query.Data));
public Expression<Func<Foo, bool>> SearchMatch(string propertyName, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
var prop = Expression.Property(foo, propertyName);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
任何人都有任何想法如何让它适用于字符串数组?
答案 0 :(得分:5)
您需要定义一个公开您要访问的属性的接口,如下所示:
public interface IHaveName
{
string Name { get; }
}
然后,在您的课程上,您将实现界面:
public class Foo : IHaveName
如果您正在使用从DBML文件生成的类,则这些类将使用partial
keyword标记,因此实现该接口就像创建新文件一样简单,并插入:
public partial class Foo : IHaveName
由于该属性已在.dbml文件生成的 other .cs文件中声明为public,因此该接口是隐式实现的。
最后,您需要重写ByName
方法,以获取具有实现接口IHaveName
的约束的泛型类型参数:
private IQueryable<T> ByName<T>(IQueryable<T> dbQuery, Query query)
where T : IHaveName
{
// Everything else is the same.
对于您的其他属性(以及使用它们的方法),您可以将它们聚合到一个界面中,或根据您的需要将它们分开。
根据您的编辑,如果您想动态创建表达式,则不必放弃编译时安全性:
public Expression<Func<Foo, bool>> SearchMatch(
Expression<Func<Foo, string>> property, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
然后你这样打电话:
var expression = SearchMatch(f => f.Name, "searchTerm");
这可确保您传递给SearchMatch
的属性实际存在于Foo
上。请注意,如果要为其他标量属性类型创建此泛型,请执行以下操作:
public Expression<Func<Foo, bool>> SearchMatch<T>(
Expression<Func<Foo, T>> property, T searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
答案 1 :(得分:-1)
如果我明白你想要实现什么反思可能对你有所帮助。至少如果你玩的很好。这是一个简化但有效的例子
internal class Program
{
private class Data
{
public string Name { get; set; }
public string Address { get; set; }
public override string ToString()
{
return String.Format("My name is {0} and I'm living at {1}", Name, Address);
}
}
static Expression<Func<Data,bool>> BuildExpression(PropertyInfo prop, IQueryable<string> restrict)
{
return (data) => !restrict.Any(elem => elem == prop.GetValue(data, null));
}
static IQueryable<Data> FilterData(IQueryable<Data> input, Expression<Func<Data, bool>> filter)
{
return input.Where(filter);
}
public static void Main (string[] args)
{
List<Data> list = new List<Data>()
{
new Data {Name = "John", Address = "1st Street"},
new Data {Name = "Mary",Address = "2nd Street"},
new Data {Name = "Carl", Address = "3rd Street"}
};
var filterByNameExpression = BuildExpression(typeof (Data).GetProperty("Name"),
(new List<string> {"John", "Carl"}).AsQueryable());
var filterByAddressExpression = BuildExpression(typeof(Data).GetProperty("Address"),
(new List<string> { "2nd Street"}).AsQueryable());
IQueryable<Data> filetedByName = FilterData(list.AsQueryable(), filterByNameExpression);
IQueryable<Data> filetedByAddress = FilterData(list.AsQueryable(), filterByAddressExpression);
Console.WriteLine("Filtered by name");
foreach (var d in filetedByName)
{
Console.WriteLine(d);
}
Console.WriteLine("Filtered by address");
foreach (var d in filetedByAddress)
{
Console.WriteLine(d);
}
Console.ReadLine();
}
Hovewer,我几乎可以肯定它不适用于LINQ-to-SQL。解决此问题的一种方法是在将IQueryable
传递给此类过滤方法之前实现ToList
(例如,通过调用它们上的{{1}})。