我正在使用StructureMap来解析对我的存储库类的引用。我的存储库接口实现了IDisposable,例如
public interface IMyRepository : IDisposable
{
SomeClass GetById(int id);
}
使用Entity Framework的接口实现:
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方法吗?
答案 0 :(得分:22)
警告:请注意我的观点已经改变,您应该考虑跟进建议过时。请在最后阅读更新。
虽然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();
}
}
虽然这段代码明确地处理了依赖关系,但它可能会引起读者的注意,因为资源通常只能由资源所有者处理。显然,Client
成为资源的所有者,当它被注入时。
因此,我赞成使用工厂。在这个例子中寻找例子:
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
,因为它在{{1}内创建和处理资源方法。
注入工厂是最明确的方式(最少惊喜的路径)。这就是为什么我更喜欢这种风格。缺点是你经常需要定义更多的课程(对你的工厂来说),但我个人并不介意。
<小时/> RPM1984要求一个更具体的例子。
我不会拥有存储库实现Do
,而是拥有一个实现IDisposable
的工作单元,控制/包含存储库并拥有一个知道如何创建新工作单元的工厂。考虑到这一点,上面的代码看起来像这样:
IDisposable
在我使用的设计中,并描述了here,我使用了一个具体的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();
}
}
}
类,它包装了一个NorthwindUnitOfWork
,它是底层LINQ提供者的网关(例如LINQ to SQL或实体框架)。在sumary中,设计如下:
IDataMapper
。INorthwindUnitOfWorkFactory
类,并在其中注入了一个O / RM特定的NorthwindUnitOfWork
类。IDataMapper
实际上是NorthwindUnitOfWork
周围的类型安全包装,IDataMapper
请求NorthwindUnitOfWork
存储库并转发请求以提交更改并将其置于映射器。IDataMapper
返回IDataMapper
个类,并且存储库实现Repository<T>
以允许客户端在存储库上使用LINQ。IQueryable<T>
的具体实现包含对O / RM特定工作单元的引用(例如EF的IDataMapper
)。因此,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
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
您可以找到有关此模型的更多信息here。
2012年12月更新
这是我原来回答两年后写的更新。过去两年我尝试设计我正在研究的系统的方式发生了很大的变化。虽然它过去适合我,但我不喜欢在处理工作单元模式时再使用工厂方法。相反,我只是直接向消费者注入一个工作单元实例。然而,这种设计是否适合您,取决于您的系统设计方式。如果您想了解更多相关信息,请查看我的新Stackoverflow答案:One DbContext per web request…why?
答案 1 :(得分:11)
如果你想做对,我会建议你做一些改变:
1 - 存储库中没有数据上下文的私有实例。如果您使用多个存储库,那么您将最终得到多个上下文。
2 - 要解决上述问题 - 将背景包装在工作单元中。通过ctor:public MyRepository(IUnitOfWork uow)
3 - 使工作单元具有IDisposable。工作单元应在请求开始时“新建”,因此应在请求完成时予以处理。存储库不应该实现IDisposable,因为它不直接使用资源 - 它只是减轻它们。 DataContext / Unit of Work应该实现IDispoable。
4 - 假设您使用的是Web应用程序,则无需显式调用dispose - 我重复一遍,您不需要明确调用您的dispose方法。 StructureMap有一个名为HttpContextBuildPolicy.DisposeAndClearAll();
的方法。这样做是在任何实现IDisposable的HTTP范围对象上调用“Dispose”方法。将此调用贴在Application_EndRequest
(Global.asax)中。此外 - 我相信有一个更新的方法,称为ReleaseAllHttpScopedObjects或其他东西 - 不记得名称。
答案 2 :(得分:0)
您可以像这样声明IMyRepository,而不是将Dispose添加到IMyRepository:
public interface IMyRepository: IDisposable
{
SomeClass GetById(int id);
}
这样,您确保所有存储库有时会调用Dispose,并且您可以在Repository对象上使用C#“using”模式:
using (IMyRepository rep = GetMyRepository(...))
{
... do some work with rep
}