我一直在尝试创建存储库模式以及依赖注入,但看起来我错过了一些简单的步骤。这是我的代码
public class HomeController
{
private readonly ILoggingRepository _loggingRepository;
public HomeController(ILoggingRepository loggingRepository)
{
_loggingRepository = loggingRepository;
}
public void MyMethod()
{
string message = "MyMessage Called";
_loggingRepository .LogMessage(message);
}
}
// ILoggingRepository.cs
public interface ILoggingRepository
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
public void LogMessage(string message)
{
using (var dbContext = new DbContext())
{
var serviceLog = new Log() { Message = message, Logged = DateTime.UtcNow };
dbContext.Logs.Add(serviceLog);
dbContext.SaveChanges();
}
}
}
到目前为止,这完全可以正常工作,但是当我进行多个存储库调用时会出现问题。
现在我知道Entity framework 6.0有内置的工作单元表示,所以我没有创建UnitofWork接口或类 但是当我在两个不同的交易中做这样的事情时,问题出现了。让我们说
Area area = _areaRepository.GetArea(); // Line 1
area.Name = "NewArea"; // Line 2
_areaRepository.SaveArea(area); // Line 3
现在因为_areaRepository在第3行创建了一个新的DbContext,它不会更改区域的名称,因为它不考虑EntityState.Modified 我必须明确设置,这是不正确的。
所以我想我需要在单个交易中完成所有这些,我在这里做错了吗? 实现这一目标的正确和最佳方法是什么,我是否应该将DbContext注入存储库?
答案 0 :(得分:0)
这就是我一直这样做的方式: 如果不使用Repository或Unit of Work层,因为Entity Framework db Context已经实现了这些模式。所以,我只有一个服务层:
public interface IBaseService<VO, ENT>{
IQueryable<VO> GetAll();
VO Get(object id);
}
public abstract class BaseService<VO, ENT> : IBaseService<VO, ENT>{
MyContext db;
public BaseService(MyContext db){
this.db = db;
}
public IQueryable<VO> GetAll(){
return db.Set<ENT>().ProjectTo<VO>();
}
}
服务类在构造函数中注入了dbContext。这些类位于服务库中。然后,如何解析dbContext和服务是项目谁将使用它们的问题。 ProjectTo方法是Automapper Nuget的IQueryable扩展。例如:
Windows服务需要同一线程中的所有服务实例共享相同的dbContext。所以,在windows服务项目中,我使用Ninject https://www.nuget.org/packages/Ninject/4.0.0-beta-0134,这个库是一个依赖项解析器,我用它来配置如何构建依赖项,创建一个内核,如下所示:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InThreadScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InThreadScope();
我正在创建一个Web项目,您需要安装一个aditional nuget(Ninject.WebCommon,Ninject.Web.COmmon.WebHost,Ninject.MVC5)来为绑定配置提供.InRequestScope()方法,如这样:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InRequestScope();
您需要在应用启动时设置这些内核。在一个web项目中,在global.asax中,在一个Windows服务项目中,应该在Service构造函数中:
您可以访问www.ninject.org/learn.html了解有关ninject的更多信息。但是,有像Autofac或Caste Windsor这样的东西,它取决于你。如果您想继续使用存储库模式,只需使用Ninject将它们注入服务层,就像我使用dbContext一样。
答案 1 :(得分:-1)
最好的方法是拥有一个DbContext
实例,将其注入每个存储库实现。这样,您将拥有数据库上下文的单个实例,因此EF将能够检测实体对象的更改。
如果您需要在示例中使用隔离的dbContexts
,则需要将对象的状态显式设置为Modified
。
根据项目类型,您应该在特定范围内设置上下文。例如,对于Web应用程序,一个选项是使用每个Web请求的实例(每个生命周期范围)。检查this url,您可以在其中查看不同实例范围的详细说明。
using
语句只是创建一个新范围,在代码块之后执行Dispose()
方法。 EF在后台做了很多工作来维护对象的UoW
和状态,但在你的情况下,使用时,你没有使用这个功能。
答案 2 :(得分:-2)
首先,DbContext 是一个存储库。如果要将其包装在自定义存储库中,它们应具有相同的生命周期。
其次,您的工作单位是您的控制者。存储库应限定为工作单元。
这意味着您的存储库需要是Disposable,因为DbContext是。
类似于:
public interface ILoggingRepository : IDisposable
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
MyDbContext db;
public LoggingRepository(MyDbContext db)
{
this.db = db;
}
public void Dispose()
{
db.Dispose();
}
public void LogMessage(string message)
{
var serviceLog = new MonitoringServiceLog() { Message = message, Logged = DateTime.UtcNow };
db.MonitoringServiceLogs.Add(serviceLog);
db.SaveChanges();
}
}
如果你的ILoggingRepository不是一个数据库,它可能是一个文件或其他一些昂贵的创建或打开的文件,需要关闭。