How to Overcome Entity Framework Caching/Tracking Using MassTransit?

.net-core dependency-injection entity-framework-core masstransit rabbitmq

Question

Forgive my ignorance, but it appears that MassTransit treats singleton and scoped dependencies effectively the same. At least in the case of an Entity Framework DbContext and, therefore, the UserManager registered via the extension method AddEntityFrameworkStores.

This means that any entities loaded with tracking or otherwise added to the context are cached for the duration of the application lifetime. That, in turn, means that changes to these entities via anything outside of the context (say ad-hoc scripts) won't be recognized until the service is recycled.

Is there a best practice for addressing this limitation? For context, consider the following snippet from startup:

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

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

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

    // ...
}

And the following consumer:

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());
    }
}

In the example above, the consumer (registered transient) serves a different hash code on each request. All of the others serve the same ones (suggesting the same instances), despite the DB context and user manager being registered as scoped.

In case it matters, MassTransit is running from a console application as opposed to a web application.

1
0
8/20/2018 10:08:28 PM

Accepted Answer

I was directed to the answer in comments on the original question, but thought I'd post the details for anyone else. Bottom line is that I needed to correctly configure the dependency injection using MassTransit.Extensions.DependencyInjection (see http://masstransit-project.com/MassTransit/usage/containers/msdi.html).

I had started down that path, but missed a key piece when configuring endpoints: e.LoadFrom(provider). Missing that piece effectively turned my consumers into singletons, regardless of how they were actually configured.

Lastly, I needed to configure my DbContext using AddDbContext instead of AddDbContextPool.

Here's a snippet of the bare minimum I needed to make this happen:

    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));
        }));
    }
0
8/22/2018 3:42:55 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow