在使用 Azure 服务总线消息后尝试连接到我的数据库时处理上下文实例错误 [英] Dispose context instance error when trying to connect to my DB after Azure service bus message is consumed

查看:16
本文介绍了在使用 Azure 服务总线消息后尝试连接到我的数据库时处理上下文实例错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在侦听传入的 Azure 服务总线消息.按照文档并接收消息,我解析消息正文,然后我想连接到我的数据库以编辑条目然后保存.但是我在尝试拨打电话时收到以下错误

I'm listening for an incoming Azure service bus message. Following the documentation and receiving the message, I parse the message body and then I want to connect to my DB to edit an entry and then save. But I'm getting this error below when trying to make the call

var ybEvent = await _unitOfWork.Repository<YogabandEvent>().GetEntityWithSpec(spec);

错误

无法访问已处理的上下文实例.此错误的一个常见原因是处理从依赖注入解析的上下文实例,然后尝试在应用程序的其他地方使用相同的上下文实例.如果您在上下文实例上调用Dispose",或将其包装在 using 语句中,则可能会发生这种情况.如果你使用依赖注入,你应该让依赖注入容器来处理上下文实例. 对象名称:'DataContext'.

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. Object name: 'DataContext'.

这是完整的服务,其中包含侦听和接收传入 Azure 消息的方法.错误在 MessageHandler() 的最后一行

Here is the full service with the method that listens for and picks up incoming Azure messages. Error is on the last line of MessageHandler()

仅供参考 - 如果我删除 DB 调用中的await",对于已处理的上下文,我仍然会遇到相同的错误.

FYI - If I remove the 'await' on the DB call, I still get the same error for a disposed context.

问题 - 我该如何解决?

public class ServiceBusConsumer : IServiceBusConsumer
{
    private readonly IConfiguration _config;
    private readonly ServiceBusClient _queueClient;
    private readonly ServiceBusProcessor _processor;
    private readonly IUnitOfWork _unitOfWork;
    private readonly IEventConsumer _eventConsumer;

    public ServiceBusConsumer(IConfiguration config, IEventConsumer eventConsumer, IUnitOfWork unitOfWork)
    {
        _config = config;
        _unitOfWork = unitOfWork;
        _eventConsumer = eventConsumer;
        _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);
        
        var spec = new YogabandEventWithMessageIdSpecification(args.Message.SequenceNumber);
        // error here...
        var ybEvent =  await _unitOfWork.Repository<YogabandEvent>().GetEntityWithSpec(spec);

        // do something then save
    }

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

这是我的工作单元

public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : class // : BaseEntity
    {
        if(_repositories == null) 
            _repositories = new Hashtable();

        var type = typeof(TEntity).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(GenericRepository<>);
            var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _context);

            _repositories.Add(type, repositoryInstance);
        }

        return (IGenericRepository<TEntity>) _repositories[type];
    }

我尝试直接在处理程序中调用我的通用存储库,但仍然因处理错误而失败.

I tried to call my generic repo directly inside the handler but that still fails with the dispose error.

这是我在处理程序中更改的调用,现在我调用 gen repo 而不是工作单元

Here is the call I changed in the handler, now I call the gen repo instead of the unit of work

var ybEvent = await _eventsRepo.GetEntityWithSpec(spec);

这是我的通用存储库中的 GetEntityWIthSpec()

Here is GetEntityWIthSpec() from my generic repo

public async Task<T> GetEntityWithSpec(ISpecification<T> spec)
{
    return await ApplySpecification(spec).FirstOrDefaultAsync();
}
private IQueryable<T> ApplySpecification(ISpecification<T> spec)
    {
        return SpecificationEvaluator<T>.GetQuery(_context.Set<T>().AsQueryable(), spec);
    }

仅供参考 - 这是我发起回购调用的方式

FYI - here is how I init my repo call

private readonly IGenericRepository<YogabandEvent> _eventsRepo;

then I inject it into the constructor

public ServiceBusConsumer(IConfiguration config, IEventConsumer eventConsumer, IUnitOfWork unitOfWork, IGenericRepository<YogabandEvent> eventsRepo)
    {
        _config = config;
        _unitOfWork = unitOfWork;
        _eventConsumer = eventConsumer;
        _eventsRepo = eventsRepo;
        _queueClient = new ServiceBusClient(_config["ServiceBus:Connection"]);
        _processor = _queueClient.CreateProcessor(_config["ServiceBus:Queue"], new ServiceBusProcessorOptions());
    }

在 Main() 中启动 ServiceBusConsumer 的代码

Code that starts the ServiceBusConsumer it's in Main()

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 
            {

                // do some work here


                // https://stackoverflow.com/questions/48590579/cannot-resolve-scoped-service-from-root-provider-net-core-2
                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 class UnitOfWork : IUnitOfWork
{
    private readonly DataContext _context;
    private Hashtable _repositories;

    public UnitOfWork(DataContext context)
    {
        _context = context;
    }

    public async Task<int> Complete()
    {
        return await _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }

    public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : class // : BaseEntity
    {
        if(_repositories == null) 
            _repositories = new Hashtable();

        var type = typeof(TEntity).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(GenericRepository<>);
            var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _context);

            _repositories.Add(type, repositoryInstance);
        }

        return (IGenericRepository<TEntity>) _repositories[type];
    }
}

推荐答案

UnitOfWork 中移除这个处置:

Remove this dispose from UnitOfWork:

    public void Dispose()
    {
        _context.Dispose();
    }

简单规则:如果您还没有创建对象 - 不要处理.作用域处置时会自动处置.

Simple rule: if you have not created object - do not dispose. It will be disposed automatically when scope diposed.

还要考虑删除这个样板文件.DbContext 已经是 UoW,DbSet 已经是存储库.

Also consider to remove this boilerplate. DbContext is already UoW, DbSet is already repository.

这篇关于在使用 Azure 服务总线消息后尝试连接到我的数据库时处理上下文实例错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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