我正在使用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方法?

最佳答案

警告:请注意,我的观点已更改,您应该考虑以下img建议已过时。请阅读最后的更新。

尽管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类,该类包装了作为基础LINQ提供程序(例如LINQ to SQL或Entity Framework)网关的IDataMapper。总体而言,设计如下:
  • INorthwindUnitOfWorkFactory注入(inject)客户端。
  • 该工厂的特定实现会创建一个具体的NorthwindUnitOfWork类,并将特定于O / RM的IDataMapper类注入(inject)其中。
  • NorthwindUnitOfWork实际上是IDataMapper周围的类型安全包装器,并且NorthwindUnitOfWorkIDataMapper请求存储库,并转发请求以提交更改并将其分配给映射器。
  • 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是一个具体的类,其中包含特定存储库的属性,例如CustomersOrders等:
    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/

    10-12 17:25