为了让我的实体持续无知并使我的存储库可测试,我实现了一个类似的存储库模式:
public interface IJobRepository : IRepository<Job>
{
Job GetJobById(int jobId); //Special case where I'm eager loading other entities
void SaveJob(Job job, Job originalJob);
}
public class JobRepository : IJobRepository
{
private readonly IContext _context;
public JobRepository()
{
_context = new CustomObjectContext();
}
public JobRepository(UnitOfWork unitOfWork)
{
_context = unitOfWork.Context;
}
//Basic GetAll, GetById, Add and Delete methods from IRepository<T> Interface here
//omitted for brevity
public Job GetJobById(int jobId)
{
var job = _context.Jobs.Include("Company").Include("Location").
Include("PlantInfo").Where(j => j.Jobid == jobId).SingleOrDefault();
_context.DisposeContext();
return job;
}
public void SaveJob(Job job, Job originalJob)
{
if (job.Jobid > 0)
{
// Update
_context.Jobs.Attach(originalJob);
_context.PlantInfoes.Attach(originalJob.PlantInfo);
_context.Jobs.ApplyCurrentValues(job);
_context.PlantInfoes.ApplyCurrentValues(job.PlantInfo);
Note: ApplyCurrentValues is an extension method I'm using on the ObjectSet
}
else
{
// Create
_context.Jobs.AddObject(job);
}
_context.Save();
}
}
public class UnitOfWork
{
private readonly IContext _context;
public UnitOfWork()
{
_context = new CustomObjectContext();
}
public UnitOfWork(IContext context)
{
_context = context;
}
public string Save()
{
return _context.Save();
}
internal IContext Context
{
get { return _context; }
}
}
public interface IContext
{
IObjectSet<Job> Jobs { get; }
IObjectSet<Company> Companies { get; }
IObjectSet<Location> Locations { get; }
IObjectSet<PlantInfo> PlantInfoes { get; }
string Save();
}
我的ObjectContext继承自IContext ...所以我的理解是我只会使用存储库上的重载构造函数来促进单元测试或在我想使用相同上下文的情况下使用它(不希望基于我在SO "Entity Framework and Connection Pooling"找到的这篇文章 - 这是对的吗?
此外,假设仅在存储库被垃圾收集时才处理上下文,我必须明确地处理上下文以避免“IEntityChangeTracker的多个实例不能引用实体对象”。在保存之前附加实体时的异常。
那就是说,管理DataContext的最佳做法是什么,以保持您的实体持久无知和存储库可测试?
注意:这是一个asp.net webapplication; UnitOfWork和IContext实现基于Julia Lerman Ch24的“编程实体框架”第二版中的示例。
提前致谢!
答案 0 :(得分:0)
首先,我会确保无论我的“耗材”对象(存储库或工作单元,取决于您的设置)都实现IDisposable
。当您的consumbed对象被处理掉时,您将处置您的基础上下文。
例如,如果您使用UnitOfWork
作为耗材对象(在您的应用程序中创建和调用的对象),它将类似于:
public class UnitOfWork : IDisposable
{
// All the other stuff you had before plus:
public void Dispose ()
{
if (_context != null)
{
_context.Dispose ();
}
}
}
(注意:如果您的存储库是直接使用的存储库,也可以这样做)
然后,您的应用程序中有一些选项。如果您要直接使用UnitOfWork
,可以使用它:
public void SomeMethodThatAccessesYourData ()
{
using (var unitOfWork = new UnitOfWork (/*Load in the context*/))
{
// Access your data here.
}
}
或者,在Web窗体或MVC对象中,您可以使用构造函数注入并在处理Web窗体或MVC对象时将其丢弃:
// If you're using MVC:
public class MyController : Controller
{
private UnitOfWork _unitOfWork;
public MyController (UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public override Dispose (bool Disposing)
{
if (Disposing && _unitOfWork != null)
{
_unitOfWork.Dispose ();
}
}
}
同样的想法代表网络表单Page
。
使用构造函数重载的主要原因是Inversion of Control (IOC)。当与IoC Container一起使用时,它有助于单元测试和生产代码。 WebForms不适合IoC,但使用MVC非常容易。
修改强>
我真的没有看到与您的存储库和工作单元的关联。通常,您从工作单元访问存储库,或者在其他实现中,您从目标存储库请求工作单元。在你的实现中(我理解的不是你自己的),似乎没有必要。
修改2
如果UoW对您的应用程序来说太过分了,并且您知道可以使用IoC来注入IContext,并且您没有很多存储库,那么您可以执行以下操作:
public IRepository<T> : IDisposable { }
public IJobRepository : IRepository<Job> { /* All the stuff you put here */ }
public JobRepository : IJobRepository
{
private IContext _context;
...
public void Dispose ()
{
if (_context != null)
{
_context.Dispose ();
}
}
public JobRepository (IContext context)
{
_context = context;
}
}
然后,您如何使用它取决于您的具体任务。我不喜欢直接使用IRepository,但这个答案太长了。