2

当使用 EF(5.0 Beta Code First)作为我的 ORM/DAL 时,我一直在研究新项目的最佳实践。关键考虑因素是可测试性、松散耦合设计和跨存储库的工作单元/事务支持。

我知道在 EF DbContext 中是 UoW 而 DbSet 是存储库,并且在服务层中,您可以跨由 DbContexts SaveChanges() 方法协调的多个存储库进行更改。这是我的示例配置:

public interface IMyContext
{
    IDbSet<Foo> Foos{ get; }

    IDbSet<Bar> Bars { get; }

    int SaveChanges();

    void Dispose();
}

public class EFMyContext : DbContext, IMyContext
{
    private IDbSet<Foo> _foos;
    private IDbSet<Bar> _bars;

    public EFMyContext()
        : base("name=MyConnectionString")
    {
        Database.SetInitializer<EFMyContext>(null);
    }

    public IDbSet<Foo> Foos
    {
        get
        {
            if (_foos == null)
            {
                _foos = this.Set<Foo>();
            }

            return _foos;
        }
    }

    public IDbSet<Bar> Bars
    {
        get
        {
            if (_bars == null)
            {
                _bars = this.Set<Bar>();
            }

            return _bars;
        }
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new FooConfiguration());
        modelBuilder.Configurations.Add(new BarConfiguration());

        base.OnModelCreating(modelBuilder);
    }

到目前为止一切都很好。如果我想使用这是像这样的上层

public class MyService : IMyService
{
    private IMyContext _context;

    public MyService(IMyContext context)
    {
        _context = context;
    }

    public void DoSomethingWithMyContext()
    {
        // fancy implementation code
    }
}

在这里我遇到了问题,因为我直接依赖于 EntityFramework.dll,因为 MyContext 公开了 IDBSet 类型的属性。这似乎不对,与这个问题非常相似。

所以我的问题是,如何抽象出对 EF 的直接依赖?我考虑过引入我自己的存储库和工作单元来传递实时上下文,但这将如何影响更改跟踪和所有其他简洁的 EF 功能?

我正在研究的另一件事是管理上下文生命周期的 IoC 机制。那时我可以取消 UoW 吗?

抱歉含糊不清,我正处于研究饱和点,需要一个明确的起点才能开始实施。

4

3 回答 3

2

问自己一个问题:我的架构应该解决什么问题?除非您能提供无懈可击的答案,否则为什么从您的服务程序集中引用 EntityFramework.dll 是错误的,您就是在浪费时间。

引用EntityFramework.dll其实并没有错。为什么应该这样?您的服务不会公开与 EF 相关的任何内容,因此它是完全可模拟的。如果您还想对服务代码进行单元测试,则必须确保您的服务以抽象方式与 EF 一起运行。在这种情况下,存储库可能是一种可行的方法,但它将是重型专业存储库 - 而不是通常提出的通用废话。存储库将隐藏与 EF 相关的所有内容,您的服务将不知道您的应用程序中存在任何类似 EF 的内容。它还回答了您关于 EF 功能的问题 - 如果您想让它可测试,您的服务必须完全独立于这些功能。例如,您不应该让您的服务代码依赖于 LINQ-to-Entities 或延迟加载。它只是导致测试中的麻烦

即使您实现了存储库,如果它们与您的服务在同一个程序集中并且该程序集引用 EntityFramework.dll,那么仍然没有任何问题。

于 2012-04-26T13:43:20.437 回答
1

服务层中,您实际上并不需要使用IQueryable<T>.

因为这样您将与支持LINQ的ORM紧密耦合。所有LINQ查询都应该封装在具体的DAL中。

更好的是,您可以在服务层中定义与DAL无关的存储库:

public interface IRepository<T>
{
    T GetById(int id);
    IEnumerable<T> GetAll();

    // Filter is some DTO that will be mapped to 
    // concrete LINQ query inside your DAL
    IEnumerable<T> GetFiltered(Filter<T> filter);

    void Add(T item);
    void Update(T item);
    void Remove(T item);
}

更新:如果您喜欢 LINQ,您可以为 ObjectContext 和 ObjectSet 定义自己的接口:

public interface IObjectContext : IUnitOfWork
{
    IObjectSet<T> Set<T>() where T : class;
}

public interface IObjectSet<T> : IObjectQuery<T>
    where T : class
{
    void Remove(T entity);
    void Add(T entity);
}

public interface IObjectQuery<out T> : IQueryable<T>, IQueryableWrapper
     where T : class
{
    IObjectQuery<T> Include(string path);
}

然后您可以为 EF 编写适配器。您还应该将查询转换层添加到您的适配器,因为 EF 无法理解表达式树中的接口。这是 QueryTranslator 的示例:https ://stackoverflow.com/a/1846030/1275399

于 2012-04-26T13:51:15.240 回答
0

您很可能没有 IMyContext 上的 DbSet 属性,而仅在实现上拥有 dbset。IMyContext 应该只用于了解保存上下文(并将其处理掉),仅此而已。因此,在存储库中,您可以拥有一个知道如何与 sql 对话的策略,比如使用 ef 并让它处理其中的 dbsets 部分。仅当您确实计划退出 EF 并在将来用其他东西替换它时,您才需要这样做。

您可以在此处找到有关此方法的更多详细信息

于 2014-12-01T11:03:39.910 回答