【发布时间】:2019-04-08 19:13:33
【问题描述】:
我以 Dapper 作为我们的 ORM 开始了我的项目,因为我们希望快速、简单地启动和运行。我现在正处于我们想要升级的阶段,所以我们不会在带有连接等的代码中编写大量 SQL。我已经决定我要使用实体框架(我真的不想讨论你是否应该或者不应该将存储库与 EF 一起使用)。但是,我没有时间一次性重构我所有的存储库。我正在使用接口,但到目前为止,接口对于每个 POCO 对象都是相当特定的,使用 GetFoo、UpdateFoo、GetFooByBarId 等方法,这并不是很好。我想将 CRUD 操作泛化为常用方法名称(Select、Upsert、Delete)。
这给我带来了第一个问题,如果我更改存储库方法的名称,那么我必须去更改所有项目中的所有引用并部署所有听起来不太有趣或不太安全的应用程序我。我希望过渡是渐进的。
如果我愿意,我还想保留将来使用 Dapper 或任何其他 ORM 的能力,而不会永远限制自己使用 EF。
我现在已经开始创建 EF 存储库,我的项目结构是这样的:
- Core(POCO 模型和其他通用代码)。
- DAL(存储库接口)
- DAL.Dapper(存储库的 Dapper 实现)
- DAL.EntityFramework(存储库的 EF 实现)
- 混合使用 repo 实现的各种项目/应用程序
我已经在我的基础 DAL 项目中创建了一个基础存储库,如下所示:
public interface IBaseRepository<T> where T : class
{
Task<T> Get(object id);
Task<IQueryable<T>> GetMany(Expression<Func<T, bool>> where, params RepositoryFlags[] flags);
Task<IQueryable<T>> GetMany(Expression<Func<T, bool>> where, Expression<Func<T, T>> fields, params RepositoryFlags[] flags);
Task<int> Upsert(T item);
Task Delete(T item);
}
问题在于 GetMany 方法适用于 EF,但不适用于 Dapper(将表达式转换为 SQL 并不是我真正想要了解的内容)。我希望能够仅将这些方法用于 EF 存储库。我花了一段时间来考虑如何做到这一点,随后还没有完全致力于新的 EF 实现,因为我不想强迫自己在 Dapper 存储库中实现我不需要的方法。
我刚刚想到了一个新想法,我认为它可以让我能够通过 DAL 基本接口共享常用方法,而且还可以让我在需要时拥有额外的 ORM 特定功能,并让所有这些功能都可以使用依赖注入。
DAL 项目
IBaseRepository
public interface IBaseRepository<T>
where T : class
{
Task<T> Get(object id);
Task<int> Upsert(T item);
Task Delete(T item);
}
IExtendedBaseRepository
public interface IExtendedBaseRepository<TModel, TExtensions> : IBaseRepository<TModel>
where TModel : class
where TExtensions : class
{
TExtensions Extensions { get; set; }
}
DAL.Dapper 项目
FooRepository
public class FooRepository : IBaseRepository<Foo>
{
// CRUD implementations...
// ...
}
DAL.EntityFramework 项目
FooExtensions
public class SupplementRepositoryExtensions
{
private readonly TapContext context;
public SupplementRepositoryExtensions(TapContext context)
{
this.context = context;
}
public IEnumerable<Foo> GetAll()
{
...
}
public Task<IQueryable<Foo>> GetMany(Expression<Func<Foo, bool>> where)
{
...
}
}
FooRepository
public class FooRepository : EFBaseRepository<Foo>, IExtendedBaseRepository<Foo, FooExtensions>
{
public FooExtensions Extensions { get; set; }
public FooRepository(DbContext context, FooExtensions extensions) : base(context)
{
this.Extensions = extensions;
}
}
(EFBaseRepository 包含 EF CRUD 实现)
应用程序 1 中的 DI
services.AddTransient<IBaseRepository<Foo>, DAL.Dapper.Repostories.FooRepository>();
应用程序 2 中的 DI
services.AddTransient<FooExtensions>();
services.AddTransient<IExtendedBaseRepository<Foo, DAL.EntityFramework.RepositoryExtensions.FooExtensions>, DAL.EntityFramework.Repositories.FooRepository>();
在应用程序 2 中的使用
public class FooService
{
private readonly IExtendedBaseRepository<Foo, FooExtensions> fooRepository;
public FooService(IExtendedBaseRepository<Foo, FooExtensions> fooRepository)
{
this.fooRepository = fooRepository;
}
public async Task Bar()
{
var foos = await this.fooRepository.Extensions.GetAll();
...
}
}
我相信这给了我我想要的灵活性,同时保持 ORM 之间的一定程度的通用性。我只是在寻找关于这个架构的意见。有什么好处吗?我是否忽略了任何明显的问题,或者这是一个可怕的反模式还是什么?接受建议或改进。
【问题讨论】:
标签: c# asp.net asp.net-core repository data-access-layer