我正在使用 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 方法吗?
最佳答案
警告 : 请注意,我的观点已经改变,你应该认为 followimg 建议已经过时了。请阅读最后的更新。
虽然 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
包装 IDataMapper
的类这是通往底层 LINQ 提供程序(例如 LINQ to SQL 或 Entity Framework )的网关。总之,设计如下:INorthwindUnitOfWorkFactory
注入(inject)客户端。 NorthwindUnitOfWork
类并注入(inject)特定于 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
是一个具体类,包含特定存储库的属性,例如 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/