public interface IMyRepository : IDisposable { SomeClass GetById(int id); }
使用实体框架实现接口:
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。那么什么时候,我应该怎么称呼我的处置方式?
虽然DI框架可以为您管理对象的生命周期,有些甚至可以在使用它们之后为您处理对象,但它使对象处理太隐含。创建IDisposable界面是因为需要确定性地清理资源。所以在DI的背景下,我个人喜欢把这个清理很清楚。当您明确表达时,您基本上有两个选择:1.配置DI以返回临时对象,并自己处理这些对象。 2.配置工厂并指示工厂创建新实例。
我喜欢第一种方法,因为特别是在进行依赖注入时,你的代码并不像现在那么干净。通过以下代码查找实例:
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(); } }
虽然这个代码明确地排除了依赖,但是它可能会给读者带来一些眉毛,因为资源通常只能由资源所有者处理。显然,当注入资源时,客户成为资源的所有者。
因此,我赞成使用工厂。在这个例子中查找例子:
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方法中创建和处理资源。
注入工厂是最明显的方式(最不惊奇的道路)。这就是为什么我喜欢这种风格。这样做的缺点是,您经常需要定义更多的课程(对于您的工厂),但我个人并不介意。
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注入到客户端。
>该工厂的特定实现创建一个具体的NorthwindUnitOfWork类,并向其注入一个特定于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是一个具体的类,其中包含特定存储库的属性,如客户,订单等:
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的具体实现。这是一个实体框架:
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月更新
这是我原来回答两年后写的一个更新。过去两年,我试图设计我正在开发的系统的方式发生了很大变化。虽然它过去适合我,但我不喜欢在处理工作单位时使用工厂方法。相反,我只需直接向消费者注入“工作单位”实例。然而,这种设计是否可行,取决于您的系统设计方式。如果您想了解更多关于这一点的信息,请查看我的这个较新的Stackoverflow答案:One DbContext per web request…why?