entity-framework - 如何使用依赖注入(inject)处理资源

标签 entity-framework dependency-injection structuremap dispose

我正在使用 StructureMap 来解析对我的存储库类的引用。我的存储库接口(interface)实现了 IDisposable,例如

public interface IMyRepository : IDisposable
{
  SomeClass GetById(int id);
}

使用 Entity Framework 的接口(interface)实现:
public MyRepository : IMyRepository
{
    private MyDbContext _dbContext;

    public MyDbContext()
    {
        _dbContext = new MyDbContext();
    }

    public SomeClass GetById(int id)
    {
        var query = from x in _dbContext
                    where x.Id = id
                    select x;
        return x.FirstOrDefault();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

无论如何,如前所述,我正在使用 StructureMap 来解析 IMyRepository。所以时间、地点和方式 我应该调用我的 dispose 方法吗?

最佳答案

警告 : 请注意,我的观点已经改变,你应该认为 followimg 建议已经过时了。请阅读最后的更新。

虽然 DI 框架可以为您管理对象的生命周期,有些甚至可以在您使用完对象后为您处理对象,但它使对象处理过于隐含。 IDisposable创建接口(interface)是因为需要确定性地清理资源。因此,在 DI 的上下文中,我个人喜欢使这种清理非常明确。当您明确表示时,您基本上有两个选择: 1. 配置 DI 以返回 transient 对象并自己处理这些对象。 2. 配置工厂并指示工厂创建新实例。

我更喜欢第二种方法而不是第一种方法,因为特别是在进行依赖注入(inject)时,您的代码并不像它应该的那样干净。看看这个代码的例子:

public sealed class Client : IDisposable
{
    private readonly IDependency dependency;

    public Client(IDependency dependency)
    {
        this. dependency = dependency;
    }

    public void Do()
    {
        this.dependency.DoSomething();
    }

    public Dispose()
    {
        this.dependency.Dispose();
    }
}

虽然这段代码明确地处理了依赖关系,但它可能会引起读者的注意,因为资源通常只能由资源的所有者处理。显然,Client当它被注入(inject)时成为资源的所有者。

正因为如此,我更喜欢使用工厂。看看这个例子:
public sealed class Client
{
    private readonly IDependencyFactory factory;

    public Client(IDependencyFactory factory)
    {
        this.factory = factory;
    }

    public void Do()
    {
        using (var dependency = this.factory.CreateNew())
        {
            dependency.DoSomething();
        }
    }
}

此示例与前一个示例具有完全相同的行为,但请查看 Client类不必实现 IDisposable不再,因为它在 Do 中创建和处置资源方法。

注入(inject)工厂是执行此操作的最明确的方式(最不令人惊讶的路径)。这就是为什么我更喜欢这种风格。这样做的缺点是你经常需要定义更多的类(为你的工厂),但我个人并不介意。

RPM1984 要求提供更具体的示例。

我不会让存储库实现 IDisposable ,但有一个实现 IDisposable 的工作单元,控制/包含存储库,并拥有一个知道如何创建新工作单元的工厂。考虑到这一点,上面的代码看起来像这样:
public sealed class Client
{
    private readonly INorthwindUnitOfWorkFactory factory;

    public Client(INorthwindUnitOfWorkFactory factory)
    {
        this.factory = factory;
    }

    public void Do()
    {
        using (NorthwindUnitOfWork db = 
            this.factory.CreateNew())
        {
            // 'Customers' is a repository.
            var customer = db.Customers.GetById(1);

            customer.Name = ".NET Junkie";

            db.SubmitChanges();
        }
    }
}

在我使用的设计中,并描述了 here , 我用的是混凝土 NorthwindUnitOfWork包装 IDataMapper 的类这是通往底层 LINQ 提供程序(例如 LINQ to SQL 或 Entity Framework )的网关。总之,设计如下:
  • INorthwindUnitOfWorkFactory注入(inject)客户端。
  • 该工厂的特定实现创建了一个具体的 NorthwindUnitOfWork类并注入(inject)特定于 O/RM 的 IDataMapper类进去。
  • NorthwindUnitOfWork实际上是围绕 IDataMapper 的类型安全包装器和 NorthwindUnitOfWork请求 IDataMapper用于存储库并转发请求以提交更改并处理到映射器。
  • IDataMapper返回 Repository<T>类和存储库实现 IQueryable<T>允许客户端通过存储库使用 LINQ。
  • IDataMapper的具体实现持有对 O/RM 特定工作单元的引用(例如 EF 的 ObjectContext )。出于这个原因,IDataMapper必须实现 IDisposable .

  • 这导致了以下设计:
    public interface INorthwindUnitOfWorkFactory
    {
        NorthwindUnitOfWork CreateNew();
    }
    
    public interface IDataMapper : IDisposable
    {
        Repository<T> GetRepository<T>() where T : class;
    
        void Save();
    }
    
    public abstract class Repository<T> : IQueryable<T>
        where T : class
    {
        private readonly IQueryable<T> query;
    
        protected Repository(IQueryable<T> query)
        {
            this.query = query;
        }
    
        public abstract void InsertOnSubmit(T entity);
    
        public abstract void DeleteOnSubmit(T entity);
    
        // IQueryable<T> members omitted.
    }
    
    NorthwindUnitOfWork是一个具体类,包含特定存储库的属性,例如 Customers , Orders , 等等:
    public sealed class NorthwindUnitOfWork : IDisposable 
    {
        private readonly IDataMapper mapper;
    
        public NorthwindUnitOfWork(IDataMapper mapper)
        {
            this.mapper = mapper;
        }
    
        // Repository properties here:    
        public Repository<Customer> Customers
        {
            get { return this.mapper.GetRepository<Customer>(); }
        }
    
        public void Dispose()
        {
            this.mapper.Dispose();
        }
    }
    

    剩下的是INorthwindUnitOfWorkFactory的具体实现。以及IDataMapper的具体实现.这是 Entity Framework 的一个:
    public class EntityFrameworkNorthwindUnitOfWorkFactory
        : INorthwindUnitOfWorkFactory
    {
        public NorthwindUnitOfWork CreateNew()
        {
            var db = new ObjectContext("name=NorthwindEntities");
            db.DefaultContainerName = "NorthwindEntities";
            var mapper = new EntityFrameworkDataMapper(db);
            return new NorthwindUnitOfWork(mapper);
        }
    }
    

    EntityFrameworkDataMapper :
    public sealed class EntityFrameworkDataMapper : IDataMapper
    {
        private readonly ObjectContext context;
    
        public EntityFrameworkDataMapper(ObjectContext context)
        {
            this.context = context;
        }
    
        public void Save()
        {
            this.context.SaveChanges();
        }
    
        public void Dispose()
        {
            this.context.Dispose();
        }
    
        public Repository<T> GetRepository<T>() where T : class
        {
            string setName = this.GetEntitySetName<T>();
    
            var query = this.context.CreateQuery<T>(setName);
            return new EntityRepository<T>(query, setName);
        }
    
        private string GetEntitySetName<T>()
        {
            EntityContainer container =
                this.context.MetadataWorkspace.GetEntityContainer(
                this.context.DefaultContainerName, DataSpace.CSpace);
    
            return (
                from item in container.BaseEntitySets
                where item.ElementType.Name == typeof(T).Name
                select item.Name).First();
        }
    
        private sealed class EntityRepository<T>
            : Repository<T> where T : class
        {
            private readonly ObjectQuery<T> query;
            private readonly string entitySetName;
    
            public EntityRepository(ObjectQuery<T> query,
                string entitySetName) : base(query)
            {
                this.query = query;
                this.entitySetName = entitySetName;
            }
    
            public override void InsertOnSubmit(T entity)
            {
                this.query.Context.AddObject(entitySetName, entity);
            }
    
            public override void DeleteOnSubmit(T entity)
            {
                this.query.Context.DeleteObject(entity);
            }
        }
    }
    

    您可以找到有关此型号的更多信息 here .

    2012 年 12 月更新

    这是我最初回答两年后写的更新。过去两年,我尝试设计我正在开发的系统的方式发生了很大变化。虽然它过去很适合我,但在处理工作单元模式时,我不再喜欢使用工厂方法。相反,我只是将一个工作单元实例直接注入(inject)消费者。然而,这种设计是否适合您,很大程度上取决于您的系统设计方式。如果您想了解更多相关信息,请查看我的这个较新的 Stackoverflow 答案:One DbContext per web request…why?

    关于entity-framework - 如何使用依赖注入(inject)处理资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4483659/

    相关文章:

    c# - StructureMap 和通过带有接口(interface)的构造函数进行延迟初始化

    asp.net-mvc - StructureMap、NHibernate 和多个数据库

    c# - 如何使用linq修改表格布局?

    javascript - Angular 2 : Unhandled Promise rejection: No provider for AuthService! ;区域: Angular

    entity-framework - Entity Framework 5 - 保存更改后立即刷新 DbContext

    java - 使用Guice : No implementation bounded

    c# - .NET Core DI 中的异步提供程序

    c# - IOC 在服务层注册存储库,在表示层注册服务

    wcf - Entity Framework - 架构升级、多个 DBMS 和代码优先

    entity-framework - 修改 Entity Framework 迁移是否安全,以便以不同的方式执行而不是删除并重新创建索引?