包含在(半)通用存储库中
本文关键字:存储 包含 | 更新日期: 2023-09-27 17:53:25
我正试图弄清楚如何使用新的EF Code First的东西,我有麻烦弄清楚如何适应Include
功能到一个半通用的存储库类。(我说半泛型,是因为类不是泛型的,只有方法。所以我有一个存储库包装聚合,本质上,你可以与所有实体的一部分,聚合,而不仅仅是一个单一的实体进行交互。
我的情况是我有一个实体,它有一个子实体,总是需要加载,然后其他子实体只能选择性地加载,我想在我的存储库方法上包含一个简单的bool参数。像这样:
public S GetByID<S>(int entityID, bool loadChildren = false) where S : class
{
DbSet<S> set = _context.Set<S>();
if (loadChildren)
set = FullInclude<S>(set);
else
set = DefaultInclude<S>(set);
return set.Find(entityID);
}
protected virtual DbSet<S> DefaultInclude<S>(DbSet<S> set) where S : class
{
// base implementation just returns the set
// derived versions would attach Includes to the set before returning it
return set;
}
protected virtual DbSet<S> FullInclude<S>(DbSet<S> set) where S : class
{
// base implementation just returns the set
// derived versions would attach Includes to the set before returning it
return set;
}
这是我的基础存储库,我希望能够在派生类中重写那些XyzInclude
方法。但我需要用非泛型实现覆盖它们,这显然在语言级别不起作用。
使用Linq2Sql非常容易,因为我只需配置一个DataLoadOptions
对象并将其附加到上下文。随着DbSet
的Include
API关闭,我对如何最好地做到这一点感到困惑。我正在寻找关于策略模式或类似模式的简单实现的建议。
EDIT:我想我的问题的本质是关于用非泛型派生版本重写泛型方法。我在寻找一种模式或技术,让我能做到这一点。扩展方法是我正在考虑的一件事,但如果有人有一个更"纯粹"的解决方案,我更喜欢一个。
除非你使类完全泛型,否则不可能,:
public class BaseRepo<S>
{
protected virtual DbSet<S> DefaultInclude(DbSet<S> set) {return set;}
}
public class ProductRepo : BaseRepo<Product>
{
protected override DbSet<Product> DefaultInclude(DbSet<Product> set)
{
return set.Include("...");
}
}
我只是想用我最终得到的解决方案来重新审视这个问题。我的存储库不是完全通用的,但是所有的方法都是通用的,所以我需要一种方法来存储任何实体类型的包含,并且能够在为该类型调用方法时将它们拉出来。
我在这里借用了Ladislav的答案,我可能会重新考虑这个设计,只让派生存储库上定义的每个单独的方法定义它们自己的包含,因为包含什么和不包含什么有足够多的不同组合,在几个地方重复一小段代码定义相同的包含可能是值得的。但无论如何,这是目前的设计,它工作…
基础存储库:
public abstract class Repository : IQueryableRepository, IWritableRepository
{
private readonly DbContext _context;
private readonly Dictionary<Type, LambdaExpression[]> _includes = new Dictionary<Type, LambdaExpression[]>();
protected Repository(DbContextBase context)
{
_context = context;
RegisterIncludes(_includes);
}
protected abstract void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes);
protected S GetSingle<S>(Expression<Func<S, bool>> query, bool getChildren = false) where S : class
{
IQueryable<S> entities = _context.Set<S>().AsNoTracking();
if (query != null)
entities = entities.Where(query);
entities = ApplyIncludesToQuery<S>(entities, getChildren);
return entities.FirstOrDefault();
}
private IQueryable<S> ApplyIncludesToQuery<S>(IQueryable<S> entities, bool getChildren) where S : class
{
Expression<Func<S, object>>[] includes = null;
if (getChildren && _includes.ContainsKey(typeof(S)))
includes = (Expression<Func<S, object>>[])_includes[typeof(S)];
if (includes != null)
entities = includes.Aggregate(entities, (current, include) => current.Include(include));
return entities;
}
}
派生的存储库只需要在一个地方定义它们的包含,然后当你调用查询方法时,你只需要指定你是否想要包含孩子(参见上面的getChildren
)。
public class DerivedRepository : Repository
{
public DerivedRepository(DbContext context)
: base(context) { }
protected override void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes)
{
includes.Add(typeof(ParentType), new Expression<Func<ParentType, object>>[] {
p => p.SomeChildReference.SomeGrandchild,
p => p.SomeOtherChildReference
});
}
}