如何克服 Entity Framework Caching/Tracking 使用 MassTransit?
How to Overcome Entity Framework Caching/Tracking Using MassTransit?
请原谅我的无知,但看起来 MassTransit 对单例和作用域依赖项的处理实际上是一样的。至少在 Entity Framework 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 应用程序。
我被引导到对原始问题的评论中的答案,但我想我 post 其他人的详细信息。底线是我需要使用 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));
}));
}
请原谅我的无知,但看起来 MassTransit 对单例和作用域依赖项的处理实际上是一样的。至少在 Entity Framework 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 应用程序。
我被引导到对原始问题的评论中的答案,但我想我 post 其他人的详细信息。底线是我需要使用 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));
}));
}