我正在使用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
周围的类型安全包装器,并且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/