我有一个使用 EF 5 Code First 进行 Db 操作的 MVC 应用程序。EF 上还有通用存储库模式。
在向 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。
您知道如何成功创建动态查询吗?