I have a MVC application with all Ninject stuff wired up properly. Within the application I wanted to add functionality to call a WCF service, which then sends bulk messages (i.e. bulk printing) to RabbitMQ queue .
A 'processor' app subscribes to messages in the queue and process them. This is where I also want to update some stuff in the database, so I want all my services and repositories from the MVC app to be available too.
The processor app implements the following:
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;
}
}
}
}
All Ninject repository bindings are specified within RepositoryModule, which is also used within MVC app and look like this:
Bind<IReviewRepository>().To<ReviewRepository>().InCallScope();
The processor class
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
}
}
Everything works fine until I update the database from the MVC app or database directly. The processor app doesn't know anything about those changes and the next time it tries to process something, it works on a 'cached' DbContext. I'm sure it's something to do with not disposing the DbContext properly, but I'm not sure what scope should be used for a console app (tried all sort of different scopes to no avail).
The only solution I can think of at the moment is to call WCF service back from the processor app and perform all the necessary updates within the service, but I would want to avoid that.
UPDATE: Adding update logic
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);
}
}
UpdateReview method in ReviewService:
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);
}
}
}
Context itself is bound like this:
Bind<MyContext>().ToSelf().InCallScope();
From the description of scopes I thought that Transient scope was the right choice, but as I said earlier I tried all sorts including RequestScope, TransientScope, NamedScope and even Singleton (although I knew it wouldn't be desired behaviour), but none of them seem to be disposing the context properly.