Контекст был удален. Выполнить асинхронный - PullRequest
1 голос
/ 10 апреля 2020

Существует проблема с асинхронным c, выполняющим запрос к веб-API с использованием структуры объекта. Общий вид таков, что запрос отправляется в API, а ActionFilter перехватывает запрос функции контроллера, отправляет клиенту статус ok с ключом ответа, выполняет запрос asyn c и после отправки данных SignalR. ActionFilter запускается в асинхронном режиме c выполняется следующим образом:

HostingEnvironment.QueueBackgroundWorkItem(async (ct) =>
{
    var response = await actionContext.ActionDescriptor.ExecuteAsync(actionContext.ControllerContext,
    actionContext.ActionArguments, ct);
    var data = new JavaScriptSerializer().Serialize(response);
    await connectionContext.Connection.Send(connectionId, $"{requestKey};{data}");
});

Контроллер:

[HttpPost]
[Route("")]
public ICollection<TradeAccountModel> GetAll()
{
    using (var ls = _lifetimeScope.BeginLifetimeScope())
    {
        return _tradeAccountService.GetAll();
    }
}

Служба:

public ICollection<TradeAccountModel> GetAll()
{
    using (_tradeAccountRepository.BeginTransaction())
    {
        return _tradeAccountRepository.Get().Select(acc => acc.ToModel());
    }
}

Репозиторий использует шаблон UOW. И когда репозиторий пытается получить данные из БД, возникает ошибка: System.InvalidOperationException: операция не может быть завершена, поскольку удален DbContext.

TDataRepository содержит общие операций и расширений BaseDataRespository, таких как GetById и т. д.

public interface ITradeRepository: ITDataRepository<TradeAccount>
{
}

internal class TradeRepository : T1DataRepository<TradeAccount>,
    ITradeRepository
{

}



IEnumerable<TEntity> ITDataRepository<TEntity>.Get()
    {
        return base.Get<TEntity>();
    }

BaseDataRespository имеет метод BeginTransaction

public IDisposable BeginTransaction()
    {
        if (_scope == null)
        {
            _scope = new TransactionScope(
                TransactionScopeOption.Required,
                new TransactionOptions()
                {
                    IsolationLevel = IsolationLevel.ReadCommitted,
                    Timeout = TimeSpan.FromSeconds(300)
                },
                TransactionScopeAsyncFlowOption.Enabled);
        }

        return _scope;
    }

Создание контекста с помощью BaseDataRespository

private TransactionScope _scope;
    private readonly Lazy<DataContext> _contextFactory;

    private DataContext Context => _contextFactory.Value;

    public BaseDataRepository()
    {
        _contextFactory = new Lazy<DataContext>(()=> 
        {
            var ctx = CreateContext();

            ctx.FireBuild += Build;

            return ctx;
        });
    }

1 Ответ

2 голосов
/ 10 апреля 2020

Если ваш DbContext зарегистрирован как служба с областью действия / служба с запросом HTTP, вам следует воздерживаться от передачи DbContext из вашего конвейера запросов в фоновую задачу.

Это связано с тем, что docs :

Планирует задачу, которая может выполняться в фоновом режиме, независимо от любого запроса.

Любые сервисы с областью действия, которые также реализуют IDisposable, будут автоматически удаляется после завершения запроса.

Вы должны активировать новый DbContext, используя независимую область действия в конвейере действий / запросов контроллера, и передать его фоновой задаче.

This Документы предназначены для. Net Core, но могут дать вам представление о том, как сервисы с областью действия могут использоваться в фоновом режиме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...