Fix*_*xer 26 structuremap dispose entity-framework dependency-injection
我正在使用StructureMap来解析对我的存储库类的引用.我的存储库接口实现了IDisposable,例如
public interface IMyRepository : IDisposable
{
SomeClass GetById(int id);
}
Run Code Online (Sandbox Code Playgroud)
使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
无论如何提到我正在使用StructureMap来解析IMyRepository.那么何时,何地以及如何调用我的处理方法?
Ste*_*ven 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();
}
}
Run Code Online (Sandbox Code Playgroud)
虽然此代码明确地处理了依赖项,但它可能会引起读者的注意,因为资源通常只应由资源所有者处理.显然,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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
此示例与前一个示例具有完全相同的行为,但是看看Client该类不再需要实现IDisposable,因为它在Do方法中创建和处置资源.
注入工厂是最明确的方式(最不惊讶的路径).这就是为什么我更喜欢这种风格.缺点是你经常需要定义更多的课程(对你的工厂来说),但我个人并不介意.
我没有存储库实现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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在我使用的设计中,并在此处描述,我使用了一个具体的NorthwindUnitOfWork类,它包装了一个IDataMapper底层LINQ提供程序的网关(例如LINQ to SQL或Entity Framework).在sumary中,设计如下:
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.
}
Run Code Online (Sandbox Code Playgroud)
的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();
}
}
Run Code Online (Sandbox Code Playgroud)
剩下的是具体实施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);
}
}
Run Code Online (Sandbox Code Playgroud)
而且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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处找到有关此模型的更多信息.
更新2012年12月
这是我原来回答两年后写的更新.过去两年我尝试设计我正在研究的系统的方式发生了很大的变化.虽然它过去适合我,但我不喜欢在处理工作单元模式时再使用工厂方法.相反,我只是直接向消费者注入一个工作单元实例.然而,这种设计是否适合您,取决于您的系统设计方式.如果您想了解更多相关信息,请查看我的新Stackoverflow答案:每个网络请求一个DbContext ...为什么?
RPM*_*984 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或其他东西 - 不记得名字.
| 归档时间: |
|
| 查看次数: |
9123 次 |
| 最近记录: |