我有一个MVC应用程序,它使用EF 5 Code First进行Db操作。 EF上还有Generic Repository模式。
在向DB层发送查询之前,我会进行一些操作,例如创建动态linq查询并将其与另一个查询组合。
Expression<Func<AssetItemInfo, bool>> dynamicFilter = DynamicLinqFactory<AssetItemInfo>.GetFilter(cmd.sSearch, searchColumns);
Expression<Func<AssetItemInfo, bool>> deleteFilter = c => c.CurrentStatus != AssetStatus.Deleted;
var body = Expression.AndAlso(dynamicFilter.Body, deleteFilter.Body);
Expression<Func<AssetItemInfo, bool>> filter = Expression.Lambda<Func<AssetItemInfo, bool>>(body, dynamicFilter.Parameters[0]);
我在DB层的Get方法如下所示。
public virtual PaginatedList<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "",
int? page = null,
int? take = null
) {
IQueryable<TEntity> query = dbSet;
if(filter != null) {
query = query.Where(filter);
}
if(includeProperties != null) {
foreach(var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) {
query = query.Include(includeProperty);
}
}
if(orderBy != null) {
return orderBy(query).ToList().ToPaginatedList(page.Value, take.Value);
}
else {
if(!page.HasValue) page = 0;
if(!take.HasValue) take = 0;
return query.ToList().ToPaginatedList(page.Value, take.Value);
}
}
正如您所看到的,有一个带Expression<Func<TEntity, bool>>
类型的过滤器参数。因此,如果我编写手动过滤器,它的效果非常好。
但我想前进并在给定模型的所有属性上创建动态过滤器搜索关键字。
为此,我使用下面的方法。
public class DynamicLinqFactory<TEntity> where TEntity : class, IDomainEntity {
public static Expression<Func<TEntity, bool>> GetFilter(string filter, IEnumerable<string> filterTargets = null) {
ParameterExpression c = Expression.Parameter(typeof(TEntity), "c");
Type[] ContainsTypes = new Type[1];
ContainsTypes[0] = typeof(string);
MethodInfo myContainsInfo = typeof(string).GetMethod("Contains", ContainsTypes);
if(filterTargets == null) {
filterTargets = typeof(TEntity).GetProperties().Where(p => !p.GetMethod.IsVirtual && !p.Name.EndsWith("ID")).Select(p=>p.Name).ToList();
}
List<Expression> myFilterExpressions =
filterTargets.Select<string, Expression>(s =>
Expression.Call(
Expression.Call(
Expression.Property(c, typeof(TEntity).GetProperty(s)),
"ToString",
null,
null
),
myContainsInfo,
Expression.Constant(filter)
)
).ToList();
Expression OrExpression = null;
foreach(Expression myFilterExpression in myFilterExpressions) {
if(OrExpression == null) {
OrExpression = myFilterExpression;
}
else {
OrExpression = Expression.Or(myFilterExpression, OrExpression);
}
}
Expression<Func<TEntity, bool>> predicate = Expression.Lambda<Func<TEntity, bool>>(OrExpression, c);
return predicate;
}
}
上面的方法消除名称中包含“ID”后缀的虚拟成员和成员,并生成下面采样的动态表达式。
.Lambda #Lambda1<System.Func`2[Radore.Models.Asset.AssetItem,System.Boolean]>(Radore.Models.Asset.AssetItem $c) {
.Call (.Call ($c.UpdateDate).ToString()
).Contains("tes") | .Call (.Call ($c.UpdatedBy).ToString()).Contains("tes") | .Call (.Call ($c.ServiceTag).ToString()).Contains("tes")
| .Call (.Call ($c.Price).ToString()).Contains("tes") | .Call (.Call ($c.CurrentStatus).ToString()).Contains("tes") | .Call (.Call ($c.CreatedDate).ToString()
).Contains("tes") | .Call (.Call ($c.CreatedBy).ToString()).Contains("tes") | .Call (.Call ($c.Name).ToString()).Contains("tes")
&& (System.Int32)$x.CurrentStatus != 3
}
但是当我尝试运行应用程序时,我得到了以下错误。
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
我删除了ToString()方法和简化查询,如下所示。
.Lambda #Lambda1<System.Func`2[Radore.Models.Asset.AssetItem,System.Boolean]>(Radore.Models.Asset.AssetItem $c) {
.Call (.Call ($c.Name).ToString()).Contains("tes") && (System.Int32)$c.CurrentStatus != 3
}
但是这次我收到错误告诉我c
没有绑定Linq。
您是否知道如何成功创建动态查询?
答案 0 :(得分:0)
最后,我可以编写一个为给定实体和搜索字符串创建LINQ查询的方法。 此方法使用数据类型为字符串的属性。
IEnumerable参数还允许您指定将包含在LINQ查询中的属性。
public static Expression<Func<TEntity, bool>> GetFilter(string filter, IEnumerable<string> filterTargets = null) {
ParameterExpression c = Expression.Parameter(typeof(TEntity), "c");
Type[] ContainsTypes = new Type[1];
ContainsTypes[0] = typeof(string);
MethodInfo myContainsInfo = typeof(string).GetMethod("Contains", ContainsTypes);
if(filterTargets == null) {
filterTargets = typeof(TEntity).GetProperties().Where(p => p.PropertyType == typeof(string)).Select(p=>p.Name).ToList();
}
List<Expression> myFilterExpressions =
filterTargets.Select<string, Expression>(s =>
Expression.Call(
Expression.Property(c, typeof(TEntity).GetProperty(s)),
myContainsInfo,
Expression.Constant(filter)
)
).ToList();
Expression OrExpression = null;
foreach(Expression myFilterExpression in myFilterExpressions) {
if(OrExpression == null) {
OrExpression = myFilterExpression;
}
else {
OrExpression = Expression.Or(myFilterExpression, OrExpression);
}
}
Expression<Func<TEntity, bool>> predicate = Expression.Lambda<Func<TEntity, bool>>(OrExpression, c);
return predicate;
}