如何使用MassTransit克服实体框架缓存/跟踪?

时间:2018-08-20 21:03:59

标签: dependency-injection .net-core rabbitmq entity-framework-core masstransit

请原谅我的无知,但似乎MassTransit有效地对待单例和范围依赖性。至少在实体框架DbContext以及因此UserManager通过扩展方法AddEntityFrameworkStores注册的情况下。

这意味着在应用程序生存期内将缓存所有加载了跟踪或以其他方式添加到上下文的实体。相应地,这意味着直到上下文被回收为止,通过上下文之外的任何内容(例如临时脚本)对这些实体所做的更改都不会被识别。

是否有解决此限制的最佳实践?对于上下文,请考虑以下启动代码段:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddDbContextPool<SomeDbContext>(options =>
        options.UseSqlServer(configuration.GetConnectionString("SomeConnectionString")));

    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddEntityFrameworkStores<SomeDbContext>()
        .AddDefaultTokenProviders();

    // ...
}

以及以下消费者:

public class SomeConsumer : IConsumer<ISomeRequest>
{
    private readonly SomeDbContext _dbContext;
    private readonly UserManager<IdentityUser> _userManager;

    public SomeConsumer(SomeDbContext dbContext, UserManager<IdentityUser> userManager)
    {
        _dbContext = dbContext;
        _userManager = userManager;
    }

    public async Task Consume(ConsumeContext<ISomeRequest> context)
    {
        var identityUser1 = await _dbContext.Users.FindAsync(1);
        var identityUser2 = await _userManager.FindByIdAsync(1);

        var consumerHashCode = GetHashCode();
        var dbContextHashCode = _dbContext.GetHashCode();
        var userManagerHashCode = _userManager.GetHashCode();
        var identityUser1HashCode = identityUser1.GetHashCode();
        var identityUser2HashCode = identityUser2.GetHashCode();

        context.Respond(new SomeResponse());
    }
}

在上面的示例中,使用者(注册的瞬态)在每个请求上提供不同的哈希码。尽管数据库上下文和用户管理器已注册为作用域,但所有其他服务器都使用相同的服务(建议使用相同的实例)。

万一重要,MassTransit是从控制台应用程序而不是Web应用程序运行。

1 个答案:

答案 0 :(得分:0)

我被引导去回答有关原始问题的评论,但以为我会将其他人的详细信息发布了。最重要的是,我需要使用MassTransit.Extensions.DependencyInjection(请参阅http://masstransit-project.com/MassTransit/usage/containers/msdi.html)正确配置依赖项注入。

我已经走了这条路,但是在配置端点e.LoadFrom(provider)时错过了关键要素。错过那一块实际上将我的消费者变成单身人士,而不管他们的实际配置如何。

最后,我需要使用AddDbContext而不是AddDbContextPool配置DbContext。

这是实现这一目标所需的最低限度的摘要:

    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        services.AddDbContext<SomeDbContext>(options =>
            options.UseSqlServer(configuration.GetConnectionString("SomeConnectionString")));

        services.AddIdentity<IdentityUser, IdentityRole>()
            .AddEntityFrameworkStores<SomeDbContext>()
            .AddDefaultTokenProviders();

        // ...

        services.AddScoped<SomeConsumer>();

        services.AddMassTransit(x =>
        {
            x.AddConsumer<SomeConsumer>();
        });

        services.AddSingleton(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
        {
            var host = cfg.Host("localhost", "/", h => { });// However you want to get your host
            var queueName = "web-service-endpoint";
            cfg.ReceiveEndpoint(host, queueName, e => e.LoadFrom(provider));
        }));
    }