【发布时间】:2016-02-02 14:27:52
【问题描述】:
我有一个通用存储库,其中所有数据库表都有一个要删除逻辑的列。例如:
View my simple database diagram
我需要将存储库持有的 Sale 表中的所有查询添加到条件否定删除列,包括何时包含表(SaleItem、Item、Customer)。
[Table("Sale")]
public partial class Sale
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Sale()
{
SaleItems = new HashSet<SaleItem>();
}
public long ID { get; set; }
public long? CostumerID { get; set; }
public virtual Costumer Costumer { get; set; }
public bool Deleted { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<SaleItem> SaleItems { get; set; }
public class SaleRepository : BaseRepository<Sale>
{
public async Task<Sale> FindOneAndRelatedAsync(object id)
{
return await FindAsync(id,
(s => s.Costumer),
(s => s.SaleItems),
(s => s.SaleItems.Select(si => si.Item))
);
}
public class BaseRepository<T> : IBaseRepository<T> where T : class
{
private string deletedName = "Deleted";
public async virtual Task<T> FindAsync(object id, params Expression<Func<T, object>>[] includes)
{
return await FindAsync(id, false, includes);
}
public async virtual Task<T> FindAsync(object id, bool hasDeleted = false, params Expression<Func<T, object>>[] includes)
{
var query = DbSet.AsQueryable();
query = includeRelationships(includes, query);
query = query.Where(idPredicateExpression(id));
if (!hasDeleted)
query = query.Where(deletedPredicateExpression());
return await query.FirstOrDefaultAsync();
}
protected Expression<Func<T, bool>> deletedPredicateExpression()
{
return columnPredicateExpression(deletedName, false);
}
protected Expression<Func<T, bool>> columnPredicateExpression(string columnName, object keyword)
{
var arg = Expression.Parameter(typeof(T), "p");
var method = keyword.GetType() == typeof(string) ? typeof(string).GetMethod("Contains", new[] { typeof(string) }) : keyword.GetType().GetMethod("Equals", new[] { keyword.GetType() });
var body = Expression.Call(
Expression.Property(arg, columnName),
method,
Expression.Constant(keyword));
return Expression.Lambda<Func<T, bool>>(body, arg);
}
protected IQueryable<T> includeRelationships(Expression<Func<T, object>>[] includes, IQueryable<T> query)
{
if (includes != null)
{
query = includes.Aggregate(query, (current, include) => current.Include(include));
}
return query;
}
从上面的代码中可以看出,我可以创建一个表达式来使用 deletedPredicateExpression() 方法为 Sale 实体添加条件 WHERE。它完美地工作。但是 includeRelationships() 方法包含的实体(SaleItem、Item 和 Costumer)必须包含条件 WHERE。
当关系为 1:n 时,作为 Costumer 实体。我可以执行插入下面的代码。
protected IQueryable<T> includeRelationships(Expression<Func<T, object>>[] includes, IQueryable<T> query)
{
if (includes != null)
{
int i = 0;
query = includes.Aggregate(query, (current, include) => current.Include(include));
foreach (var include in includes)
{
var test = test(include.Body.Type);
query = query.Where(test).AsQueryable();
}
}
return query;
}
protected Expression<Func<T, bool>> test(Type typeEntity)
{
string columnName = typeEntity.Name +"." + deletedName;
string[] props = columnName.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "m");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var val1 = Expression.Constant(false);
Expression e1 = Expression.Equal(expr, val1);
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
return (Expression<Func<T, bool>>)Expression.Lambda(delegateType, e1, arg);
}
如果我的关系是 N:N,或者当我需要访问集合内的对象时,例如在 SaleItems 下的实体 Item 中,则不能包含子句 WHERE 因为它没有对象 SaleItems 或 Item 的属性 Deleted。
是否存在过滤所有实体以始终生成查询的方法,考虑到默认情况下 WHERE Deleted == 0 的情况?
【问题讨论】:
标签: generics c#-4.0 lambda ef-code-first repository-pattern