如果我的上下文被释放,在我收到来自Azure Service Bus侦听器/消费者的消息后,如何使用EF Core? [英] How to use EF Core after I've received a message from my Azure Service bus listener/consumer, if my context is disposed?

查看:102
本文介绍了如果我的上下文被释放,在我收到来自Azure Service Bus侦听器/消费者的消息后,如何使用EF Core?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有所有控制器的网站角度前端和后端WebAPI,我还有一个服务(C#类),我将其作为一个长期运行的任务调用以侦听传入的Azure服务总线消息。

仅供参考-我无法将任何作用域服务(DbContext)传递给单一实例(ServiceBusConsumer),因此无法将我的DB上下文传递给此服务。

问题-收到传入的Service Bus消息后,如何调用数据库并使用它?

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

Startup.cs

services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();

Program.cs->;in Main()I启动服务

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

ServiceBusConsumer er.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;
    }
}

错误

无法访问已释放的上下文实例。此错误的常见原因是处理从依赖项注入中解析的上下文实例,然后尝试在应用程序的其他位置使用相同的上下文实例。如果对上下文实例调用‘Dispose’或将其包装在Using语句中,则可能会发生这种情况。如果您正在使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。 对象名称:‘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服务是单例的,但是您注入了一个DbContext作为构造函数。这通常是建议,但在这种情况下,它不起作用。
您在构造函数中注入一个DbContext,并将";保存&q;链接&q;到它。但随后它被释放,因此";link";将不起作用。

相反,您应该注入一个DbConextFactory。使用工厂,您可以按需创建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://docs.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()));
}

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

这篇关于如果我的上下文被释放,在我收到来自Azure Service Bus侦听器/消费者的消息后,如何使用EF Core?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆