我有一个MVC应用程序,所有Ninject内容都已正确连接。在应用程序中,我想添加调用WCF服务的功能,然后WCF服务将批量消息(即批量打印)发送到RabbitMQ队列。
A'处理器' app订阅队列中的消息并处理它们。这也是我想要更新数据库中的一些内容的地方,所以我希望我的MVC应用程序中的所有服务和存储库也可用。
处理器应用实现以下功能:
public abstract class KernelImplementation
{
private IKernel _kernel;
public IKernel Kernel
{
get
{
if (_kernel != null)
return _kernel;
else
{
_kernel = new StandardKernel(new RepositoryModule(),
new DomainModule(),
new ServiceModule(),
new MessageModule());
return _kernel;
}
}
}
}
所有Ninject存储库绑定都在RepositoryModule中指定,它也在MVC应用程序中使用,如下所示:
Bind<IReviewRepository>().To<ReviewRepository>().InCallScope();
处理器类
public class Processor : KernelImplementation
{
private readonly IReviewPrintMessage _reviewPrintMessage;
public Processor()
{
_reviewPrintMessage = Kernel.Get<IReviewPrintMessage>();
[...]
_bus.Subscribe<ReviewPrintContract>("ReviewPrint_Id",
(reviewPrintContract) => _reviewPrintMessage.ProcessReviewPrint(reviewPrintContract));
//calling ProcessReviewPrint where I want my repositories to be available
}
}
一切正常,直到我直接从MVC应用程序或数据库更新数据库。处理器应用程序对这些更改一无所知,并且在下次尝试处理某些更改时,它会在“缓存”上运行。的DbContext。我确定它与未正确处理DbContext有关,但我不确定应该为控制台应用程序使用什么范围(尝试各种不同的范围无济于事)。
我现在能想到的唯一解决方案是从处理器应用程序中调用WCF服务并在服务中执行所有必要的更新,但我希望避免这种情况。
更新:添加更新逻辑
Simplified ReviewPrintMessage:
public class ReviewPrintMessage : IReviewPrintMessage
{
private readonly IReviewService _reviewService;
public ReviewPrintMessage(IReviewService reviewService)
{
_reviewService = reviewService;
}
public void ProcessReviewPrint(ReviewPrintContract reviewPrintContract)
{
var review =
_reviewService.GetReview(reviewPrintContract.ReviewId);
[...]
//do all sorts of stuff here
[...]
_reviewService.UpdateReview(review);
}
}
ReviewService中的UpdateReview方法:
public void UpdateTenancyAgreementReview(TenancyAgreementReview review)
{
_tenancyAgreementReviewRepository.Update(review);
_unitOfWork.Commit();
}
RepositoryBase:
public abstract class EntityRepositoryBase<T> where T : class
{
protected MyContext _dataContext;
protected EntityRepositoryBase(IDbFactory dbFactory)
{
this.DbFactory = dbFactory;
_dbSet = this.DataContext.Set<T>();
}
[...]
public virtual void Update(T entity)
{
try
{
DataContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
throw new EntityException(string.Format("Failed to update entity '{0}'", typeof(T).Name), exception);
}
}
}
上下文本身就是这样绑定的:
Bind<MyContext>().ToSelf().InCallScope();
从范围的描述中我认为Transient范围是正确的选择,但正如我之前所说,我尝试了各种类型,包括RequestScope,TransientScope,NamedScope甚至Singleton(尽管我知道它不是理想的行为) ,但他们似乎都没有正确处理上下文。
答案 0 :(得分:0)
您需要的是每个交易一个DbContext
个实例。
现在其他&#34;应用程序&#34;像web应用程序或wcf-service可能每个请求都做一个事务(因此使用InRequestScope()
之类的东西。还要注意,这些应用程序为每个请求创建一个对象图。但是,这是一个你不知道的概念。控制台应用程序。
此外,范围仅影响对象的实例化。一旦它们被实例化,范围确定对它们没有任何影响。
因此解决问题的一种方法是为每个事务创建(相关)对象树/图,然后你可以使用InCallScope()
(InCallScope真正意味着&#34;每次实例化一个对象图&#34 34;,见here)。
这意味着您需要IReviewPrintMessage
的工厂(查看ninject.extensions.factory)并在每次要执行IReviewPrintMessage
时创建IReviewPrintMessage.ProcessReviewPrint
的实例
现在您已经重新创建了&#34;每个请求模式&#34;。
但是,关于CompositionRoot,建议不要这样做。
替代方案:您也可以根据需要重新创建DbContext
。不是在任何地方传递它(DbContext
作为几乎每种方法的附加参数)而是使用SynchronizationContext
本地存储(或者如果您不使用TPL / async await
:a { {1}})。我已经更详细地描述了这种方法here