Entity Framework コンテキストをバインドして、NServicebus メッセージごとにスコープを設定したいと考えています。次のコードはそれを正常に実行できますか?
Bind<IDbContext>().To<MyContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);
バックグラウンド
MSMQ キューから IEvents を読み取る複数の IMessageHandlers を持つ NServicebus サービスがあります。
各ハンドラーはメッセージを変換し、Entity Framework コンテキスト上にある特定の IRepository を介して MS SQL データベースに保存します。
各ハンドラが必要とするリポジトリは、NServicebus.ObjectBuilder.Ninject を使用して ninject を介して注入されます。
public class Product
{
public string Code { get; set; }
public Category Category { get; set; }
}
public class Category
{
public string Code { get; set; }
}
public class SampleContext : IDbContext
{
IDbSet<Product> Products { get; }
IDbSet<Category> Categories{ get; }
}
public class ProductRepository : IProductRepository
{
private IDbContext _context;
public ProductRepository(IDbContext ctx) { _context = ctx; }
public void Add(Product p)
{
_context.Products.Add(p);
_context.SaveChanges();
}
}
public class CategoryRepository : ICategoryRepository
{
private IDbContext _context;
public CategoryRepository (IDbContext ctx) { _context = ctx; }
public Category GetByCode(string code)
{
return _context.Categories.FirstOrDefault(x => x.Code == code);
}
}
public class AddProductMessageHandler : IMessageHandler<IAddProductEvent>
{
private IProductRepository _products;
private ICategoryRepository _categories;
public AddProductMessageHandler(IProductRepository p, ICategoryRepository c)
{
_products = p;
_categories = c;
}
public void Handle(IAddProductEvent e)
{
var p = new Product();
p.Code = e.ProductCode;
p.Category = _categories.GetByCode(e.CategoryCode);
_products.Add(p);
}
}
問題
EF コンテキストが Transient スコープ (既定) でバインドされている場合、ハンドラー内のバインドされた各リポジトリには、コンテキストの独自のインスタンスがあります。
Bind<IDbContext>().To<SampleContext>();
これにより、あるリポジトリからオブジェクトをロードし、別のリポジトリで保存すると問題が発生します。
同様に、Singleton スコープにバインドされている場合、同じコンテキストがすべてのリポジトリで使用されますが、追跡された変更でゆっくりといっぱいになり、すべての RAM を使い果たします (そして起動がますます遅くなります)。
Bind<IDbContext>().To<SampleContext>().InSingletonScope();
質問
理想的には、各メッセージ ハンドラーに、(そのハンドラーの) 必要なすべてのリポジトリがエンティティの読み込みと保存に使用する 1 つの EF コンテキストを持たせたいと考えています。
コンテキストを現在のメッセージ Id プロパティにスコープすることは、これを行う安全/信頼できる/良い方法ですか?
Bind<IDbContext>().To<SampleContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);