gpt4 book ai didi

c# - 如果我的上下文已释放,在收到来自 Azure 服务总线监听器/使用者的消息后如何使用 EF Core?

转载 作者:行者123 更新时间:2023-12-02 23:38:05 26 4
gpt4 key购买 nike

我有一个网站 Angular 前端,后端有 WebAPI 以及所有 Controller ,我还有一个服务(C# 类),我将其作为单例调用,作为长时间运行的任务来监听传入的 Azure 服务总线消息。

仅供引用 - 我无法将任何作用域服务 (DbContext) 传递给单例 (ServiceBusConsumer),因此我无法将我的数据库上下文传递给此服务。

问题 - 一旦我收到传入的服务总线消息,我如何调用我的数据库并使用它?

这是我监听和接收消息的服务。

启动.cs

services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();

Program.cs -> 在 Main() 中我启动服务

var bus = services.GetRequiredService<IServiceBusConsumer>();
bus.RegisterOnMessageHandlerAndReceiveMessages();

ServiceBusConsumer.cs

public class ServiceBusConsumer : IServiceBusConsumer
{
private readonly IConfiguration _config;
private readonly ServiceBusClient _queueClient;
private readonly ServiceBusProcessor _processor;

// private readonly DataContext _context;

public ServiceBusConsumer(IConfiguration config,
// DataContext context)
{
_config = config;
// _context = context;
_queueClient = new ServiceBusClient(_config["ServiceBus:Connection"]);
_processor = _queueClient.CreateProcessor(_config["ServiceBus:Queue"], new ServiceBusProcessorOptions());
}

public void RegisterOnMessageHandlerAndReceiveMessages() {
_processor.ProcessMessageAsync += MessageHandler;
_processor.ProcessErrorAsync += ErrorHandler;
_processor.StartProcessingAsync();
}

private async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
JObject jsonObject = JObject.Parse(body);
var eventStatus = (string)jsonObject["EventStatus"];

await args.CompleteMessageAsync(args.Message);

// _context is disposed
// want to connect to DB here but don't know how!
// var ybEvent = _context.YogabandEvents.Where(p => p.ServiceBusSequenceNumber == args.Message.SequenceNumber).FirstOrDefault();

}

private Task ErrorHandler(ProcessErrorEventArgs args)
{
var error = args.Exception.ToString();
return Task.CompletedTask;
}
}

错误

Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\nObject name: 'DataContext'.

这是Program.cs

public class Program
{
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
{
var context = services.GetRequiredService<DataContext>();


var userManager = services.GetRequiredService<UserManager<User>>();
var roleManager = services.GetRequiredService<RoleManager<Role>>();


var bus = services.GetRequiredService<IServiceBusConsumer>();
bus.RegisterOnMessageHandlerAndReceiveMessages();

}
catch (Exception ex)
{
var logger = loggerFactory.CreateLogger<Program>();
logger.LogError(ex, "An error occured during migration");
}
}

host.Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

这里是 Startup.cs -> 只是ConfigureServices 方法

public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingEvents));
services.AddAutoMapper(typeof(MappingMembers));
services.AddAutoMapper(typeof(MappingUsers));
services.AddAutoMapper(typeof(MappingYogabands));
services.AddAutoMapper(typeof(MappingReviews));

// objects being passed back to the UI. Before I was passing User/Photo/etc and they
// had loops/refrences back to the user objects
services.AddControllers().AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Error;
});


services.AddDbContext<DataContext>(x =>
// x.UseSqlite(_config.GetConnectionString("DefaultConnection"), y => y.UseNetTopologySuite()));
x.UseSqlServer(_config.GetConnectionString("SqlServerConnection"), y => y.UseNetTopologySuite()));


services.Configure<AuthMessageSenderOptions>(_config.GetSection("SendGrid"));
services.Configure<AuthMessageSenderOptionsNew>(_config.GetSection("SendGrid"));
services.Configure<ConfirmationOptions>(_config.GetSection("Confirmation"));

services.Configure<CloudinarySettings>(_config.GetSection("CloudinarySettings"));

services.AddApplicationServices();
services.AddIdentityServices(_config);
services.AddSwaggerDocumentation();

services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200");
});
});
}

这里是AddApplicationServices()

public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
// scoped - better option when you want to maintain state within a request
// services.AddScoped<IEventConsumer, EventConsumer>();
services.AddScoped<IServiceBusProducer, ServiceBusProducer>();
services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();
services.AddScoped<IEmailSender, EmailSender>();
services.AddScoped<IEmailSender, EmailSenderNew>();

services.AddScoped<IEmailService, EmailService>();
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>)));
services.AddScoped<LogUserActivity>();

services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
var errors = actionContext.ModelState
.Where(e => e.Value.Errors.Count > 0)
.SelectMany(x => x.Value.Errors)
.Select(x => x.ErrorMessage).ToArray();

var errorResponse = new ApiValidationErrorResponse
{
Errors = errors
};

return new BadRequestObjectResult(errorResponse);
};
});

return services;
}

最佳答案

您的问题似乎出在 DI 上。您的 ServiceBusConsumer 服务是单例,但您注入(inject) DbContext 作为构造函数。这通常是建议,但在这种情况下,它不起作用。
您在构造函数中注入(inject) DbContext 并“保存”到它的“链接”。但随后它会被处理掉,因此“链接”将不起作用。

相反,您应该注入(inject)一个 DbContextFactory。通过工厂,您可以按需创建 DbContext 实例。

private readonly IDbContextFactory<DataContext> _contextFactory;

public ServiceBusConsumer(IConfiguration config, IDbContextFactory<DataContext> contextFactory)
{
// Add this line
_contextFactory = contextFactory;
}

private async Task MessageHandler(ProcessMessageEventArgs args)
{
// With the new C# 8 syntax you can do
using var db = _contextFactory.CreateDbContext();
// Otherwise, wrap it up
using (var db = _contextFactory.CreateDbContext())
{
}
}

这是一个文档链接,其中展示了如何使用它:https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor

您只需注册即可:

public void ConfigureServices(IServiceCollection services)
{
// Add this line to register a context factory
services.AddDbContextFactory<DataContext>(
options =>
.UseSqlServer(_config.GetConnectionString("SqlServerConnection"), y => y.UseNetTopologySuite()));
}

您不能使用与 Controller 相同的 DI,因为它们通常不是单例,因此不会遇到此问题。 AFAIK DbContextFactory 正是为此目的而创建的(考虑到 Blazor)。如果您需要的服务不是 DbContext,则需要在构造函数中注入(inject)服务提供者,然后直接请求服务,尽管 Microsoft 不建议这样做。

关于c# - 如果我的上下文已释放,在收到来自 Azure 服务总线监听器/使用者的消息后如何使用 EF Core?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67596293/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com