有没有可靠的方法将Ninject绑定服务范围扩展到NServicebus消息处理程序?

时间:2013-02-19 21:07:04

标签: ninject nservicebus

我希望将我的Entity Framework上下文绑定为每个NServicebus消息的作用域。下面的代码会成功吗?

Bind<IDbContext>().To<MyContext>()
    .InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);

背景

我有一个NServicebus服务,它有几个IMessageHandler,可以从MSMQ队列中读取IEvents。

每个处理程序转换消息并通过位于实体框架上下文的特定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);

2 个答案:

答案 0 :(得分:2)

请参阅我的博客文章,其中介绍了除NSB 4.0之外的范围

http://www.planetgeek.ch/2013/01/16/nservicebus-unitofworkscope-with-ninject/

如果你有3.0,你可以查看当前的开发分支并将扩展方法移植到你的代码中。您只需要更改范围名称。

答案 1 :(得分:0)

我不熟悉EF背景,所以如果下面的答案没有任何意义,请不要理会。

如果EF上下文类似于NH ISession,那么我认为更好的选择是使用与NH implementation相同的方式使用UoW。
您可以阅读有关UoW here的更多信息。