Entity framework 如何使用依赖项注入处理资源
我正在使用StructureMap解析对存储库类的引用。My repository接口实现IDisposable,例如Entity framework 如何使用依赖项注入处理资源,entity-framework,dependency-injection,structuremap,dispose,Entity Framework,Dependency Injection,Structuremap,Dispose,我正在使用StructureMap解析对存储库类的引用。My repository接口实现IDisposable,例如 public interface IMyRepository : IDisposable { SomeClass GetById(int id); } 使用实体框架实现接口: public MyRepository : IMyRepository { private MyDbContext _dbContext; public MyDbContext()
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();
}
}
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);
}
}
无论如何,正如前面提到的,我正在使用StructureMap来解析IMyRepository。因此,我应该在何时、何地以及如何调用dispose方法?您可以这样声明IMyRepository,而不是将dispose添加到IMyRepository中:
public interface IMyRepository: IDisposable
{
SomeClass GetById(int id);
}
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();
}
}
}
这样,您可以确保所有存储库有时都会调用Dispose,并且可以在存储库对象上使用C#“using”模式:
using (IMyRepository rep = GetMyRepository(...))
{
... do some work with rep
}
如果你想把它做好,我建议你做一些改变: 1-存储库中没有数据上下文的私有实例。如果您使用多个存储库,那么最终将使用多个上下文 2-要解决上述问题,请将上下文包装在工作单元中。通过ctor将工作单元传递到存储库:
public MyRepository(IUnitOfWork uow)
3-使工作单元实现IDisposable。工作单元应该在请求开始时“重新设置”,因此应该在请求完成时进行处理。存储库不应该实现IDisposable,因为它不直接使用资源——它只是减轻了资源的负担。DataContext/工作单元应实现IDisTable
4-假设您使用的是web应用程序,您不需要显式调用dispose-我重复,您不需要显式调用dispose方法。StructureMap有一个名为
HttpContextBuildPolicy.DisposeAndClearAll()的方法代码>。这样做的目的是对实现IDisposable的任何HTTP作用域对象调用“Dispose”方法。将此调用插入应用程序\u EndRequest
(Global.asax)。另外,我相信有一个更新的方法,叫做RelaseAtHelpSistDebug对象或一些东西——记不起这个名字。 < P> <强>警告< /强>:请注意,我的观点已经改变,你应该考虑过时的建议。请阅读最后的更新
虽然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();
}
}
}
此示例的行为与上一个示例完全相同,但请参见客户机
类如何不再实现IDisposable
,因为它在Do
方法中创建和处理资源
注入工厂是实现这一点的最明确的方式(最不出人意料的途径)。这就是我喜欢这种风格的原因。这样做的缺点是您经常需要定义更多的类(用于工厂),但我个人并不介意
RPM1984要求提供更具体的示例
我不会让存储库实现IDisposable
,而是让一个工作单元实现IDisposable
,控制/包含存储库,并让一个工厂知道如何创建新的工作单元。考虑到这一点,上述代码将如下所示:
public interface IMyRepository: IDisposable
{
SomeClass GetById(int id);
}
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
类,该类封装了一个IDataMapper
,它是通向底层LINQ提供程序(如LINQ到SQL或实体框架)的网关。在sumary,设计如下:
在客户机中注入一个INorthwindUnitOfWorkFactory
该工厂的特定实现创建了一个具体的NorthwindUnitOfWork
类,并向其中注入了一个特定于O/RM的IDataMapper
类
NorthwindUnitOfWork
实际上是围绕IDataMapper
的类型安全包装,而NorthwindUnitOfWork
请求IDataMapper
存储库,并将提交更改和处置的请求转发给映射程序
IDataMapper
返回Repository
类,存储库实现IQueryable
以允许客户端在存储库上使用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.
}
剩下的是在OrthwindUnitofWorkFactory中的的具体实现和IDataMapper
的具体实现。以下是实体框架的一个示例:
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();
}
}
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);
}
}
}
公共密封类EntityFrameworkDataMapper:IDataMapper
{
私有只读对象上下文;
公共EntityFrameworkDataMapper(ObjectContext上下文)
{
this.context=上下文;
}
公共作废保存()
{
this.context.SaveChanges();
}
公共空间处置()
{