我重新发布了这个问题,因为我觉得它有点模糊。 New Post
我目前正在使用2分钟计时器的Windows服务。我首先使用EF代码和存储库模式进行数据访问。我正在使用Ninject注入我的依赖项。我在NinjectDependencyResolver类中有以下绑定:
ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["Database"];
Bind<IDatabaseFactory>().To<DatabaseFactory>()
.InSingletonScope()
.WithConstructorArgument("connectionString", connectionStringSettings.Name);
Bind<IUnitOfWork>().To<UnitOfWork>().InSingletonScope();
Bind<IMyRepository>().To<MyRepository>().InSingletonScope();
当我的服务每2分钟运行一次时,我做了类似的事情:
foreach (var row in rows)
{
var existing = myRepository.GetById(row.Id);
if (existing == null)
{
existing = new Row();
myRepository.Add(existing);
unitOfWork.Commit();
}
}
我开始在日志中看到错误说:
已成功提交对数据库的更改,但更新对象上下文时发生错误。 ObjectContext可能处于不一致状态。内部异常消息:AcceptChanges无法继续,因为对象的键值与ObjectStateManager中的另一个对象冲突。在调用AcceptChanges之前,请确保键值是唯一的。
在Windows服务中使用Ninject时使用InSingeltonScope是否正确?我相信我尝试使用不同的范围,如InTransientScope,但我只能使用InSingeltonScope来处理数据访问。错误消息是否与Scope有关或者它是否无关?
答案 0 :(得分:3)
假设该服务不是对数据库进行操作的唯一进程,则不应使用Singleton。在这种情况下会发生的情况是,您正在重用具有过期实例的DBContext。
更好的方法是以类似于web / wcf请求的方式处理服务的每个计时器执行,并为请求创建新的作业处理器。
var processor = factory.CreateRowsProcessor();
processor.ProcessRows(rows);
public class RowsProcessor
{
public Processor(UoW uow, ....)
{
...
}
public void ProcessRows(Rows[] rows)
{
foreach (var row in rows)
{
var existing = myRepository.GetById(row.Id);
if (existing == null)
{
existing = new Row();
myRepository.Add(existing);
unitOfWork.Commit();
}
}
}
}
根据问题的不同,将循环放在外面并为每一行设置一个新的处理器可能更好。
阅读http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/以获取有关工厂的更多信息。如果需要将UoW注入多个类,还可以查看命名范围扩展的InCallScope。 http://www.planetgeek.ch/2010/12/08/how-to-use-the-additional-ninject-scopes-of-namedscope/
答案 1 :(得分:2)
InSingletonScope
将为您的服务的整个生命周期创建单一上下文=一个上下文。它是very bad solution。因为上下文包含来自所有先前时间事件的所有对象,所以它的内存消耗增加,并且有可能将错误视为您当前正在接收的错误(但错误实际上可能与您的单例上下文无关,但很可能不是)。例外情况表明,您有两个不同的对象,这些对象具有由上下文跟踪的相同密钥标识符 - 这是不允许的。
而不是使用singleton uow,存储库和上下文使用单件工厂,并且每次甚至从工厂请求新的实例。在时间事件处理结束时处理上下文。