Блок работы с .Net Core, зарегистрированный в Autofac, выдает System.ObjectDisposedException - PullRequest
0 голосов
/ 10 мая 2019

Я дважды пытался в рамках одного вызова HttpRequest через Unit of Work мой репозиторий, но во второй раз я получил исключение System.ObjectDisposedException. Кто-нибудь может мне помочь?

Мой DBContext

    private readonly string connectionString;

    public SwapDealContext(IConfigurationManager configurationManager)
        : base()
    {
        this.connectionString = configurationManager.DbConnectionString;
    }

    public SwapDealContext(DbContextOptions<SwapDealContext> options)
        : base(options)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(this.connectionString);
        }
    }
    public virtual DbSet<User> Users { get; set; }

Модуль AutoFac:

public class DataAccessAutoFacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Assembly assembly = typeof(DataAccessAutoFacModule).Assembly;
        base.Load(builder);
        builder.RegisterAssemblyTypes(assembly).AsSelf().AsImplementedInterfaces().InstancePerLifetimeScope();

        builder.RegisterType<UnitOfWork.UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
        builder.RegisterType<UnitOfWorkFactory>().As<IUnitOfWorkFactory>().InstancePerLifetimeScope();
    }
}

Интерфейсы:

public interface IUnitOfWorkFactory
{
    IUnitOfWork CreateUnitOfWork();
}
public interface IUnitOfWork : IDisposable
{        
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));

    T GetRepository<T>()
        where T : class;
}

Implemantations:

public class UnitOfWork : IUnitOfWork
{
    private readonly DbContext dbContext;
    private readonly Dictionary<string, object> repositories;
    private readonly ILifetimeScope lifetimeScope;

    public UnitOfWork(
        DbContext dbContext,
        ILifetimeScope lifetimeScope)
    {
        this.dbContext = dbContext;
        this.lifetimeScope = lifetimeScope;
        this.repositories = new Dictionary<string, object>();
    }        

    public void Dispose()
    {
        this.dbContext.Dispose();
        this.repositories.Clear();
    }

    public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        try
        {
            int changes = await this.dbContext.SaveChangesAsync(cancellationToken);
            return changes;
        }
        catch (Exception ex)
        {                
            throw;
        }
    }        

    public T GetRepository<T>()
        where T : class
    {
        var typeName = typeof(T).Name;

        if (!this.repositories.ContainsKey(typeName))
        {
            T instance = this.lifetimeScope.Resolve<T>(new TypedParameter(typeof(DbContext), this.dbContext));
            this.repositories.Add(typeName, instance);
        }

        return (T)this.repositories[typeName];
    }
}
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private readonly ILifetimeScope lifetimeScope;
    private readonly SwapDealContext context;

    public UnitOfWorkFactory(
        SwapDealContext context,
        ILifetimeScope lifetimeScope)
    {
        this.context = context;
        this.lifetimeScope = lifetimeScope;
    }

    public IUnitOfWork CreateUnitOfWork()
    {
        return new UnitOfWork(this.context, this.lifetimeScope);
    }
}

Услуги:

    public async Task<IList<UserDetails>> GetAllUsers()
    {
        using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
        {
            var userRepo = uow.GetRepository<IUserRepository>();

            var result = await userRepo.GetAllUsers();

            return Mapper.Map<List<UserDetails>>(result);
        }
    }

Контроллер

public class UserController : ControllerBase
{
    private readonly IUserService userService;
    private readonly ILogger logger;
    public UserController(IUserService userService, ILogger logger)
    {
        this.userService = userService;
        this.logger = logger;
    }
    [HttpGet]
    [Route("users")]
    [ProducesResponseType(typeof(IList<UserDetails>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    [AllowAnonymous]
    public async Task<IActionResult> GetUsersAnync()
    {
        try
        {
            var users = await userService.GetAllUsers();
            var users2 = await userService.GetAllUsers();

            if (!users.Any())
            {
                return NotFound($"No one user were found");
            }

            return Ok(users);
        }
        catch (Exception ex)
        {
            logger.ErrorFormat("Could not get users due to: {0}", ex, ex.Message);
            return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
        }
    }

} * * тысяча двадцать-один

StackTrace:

at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_QueryProvider()
   at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ParameterExtractingExpressionVisitor..ctor(IEvaluatableExpressionFilter evaluatableExpressionFilter, IParameterValues parameterValues, IDiagnosticsLogger`1 logger, DbContext context, Boolean parameterize, Boolean generateContextAccessors)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryModelGenerator.ExtractParameters(IDiagnosticsLogger`1 logger, Expression query, IParameterValues parameterValues, Boolean parameterize, Boolean generateContextAccessors)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.System.Collections.Generic.IAsyncEnumerable<TEntity>.GetEnumerator()
   at System.Linq.AsyncEnumerable.Aggregate_[TSource,TAccumulate,TResult](IAsyncEnumerable`1 source, TAccumulate seed, Func`3 accumulator, Func`2 resultSelector, CancellationToken cancellationToken)
   at SwapDeal.DataAccess.Repositories.UserRepository.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.DataAccess\Repositories\UserRepository.cs:line 23
   at SwapDeal.BizLogic.Services.UserService.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.BizLogic\Services\UserService.cs:line 40
   at SwapDeal.WebApi.Controllers.UserController.GetUsersAnync() in C:\Users\osmachenko\personal\VAO\SwapDeal.WebApi\Controllers\UserController.cs:line 37

Итак, когда я дважды вызываю метод GetAllUsers в Controller, я получаю System.ObjectDisposedException.

1 Ответ

0 голосов
/ 13 мая 2019

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

Если мы проследим через ...

  • Появится каждый тип, который вы регистрируете в Autofacбыть зарегистрированным InstancePerLifetimeScope.Это означает, что вы получите один на всю жизнь.В ASP.NET Core это равняется одному экземпляру на запрос (в основном).
  • Контроллер получит IUserService в своем контроллере.Мы не видим, как выглядит IUserService в целом, но мы видим GetAllUsers в примере кода.
  • GetAllUsers оборачивает операцию в оператор using - он создает, и затем удаляет единицу работы, когда она выполнена.
  • В конструкторе UnitOfWork вы передаете DbContext - один экземпляр , который вы получите, этот запрос,а затем ...
  • В UnitOfWork.Dispose вы избавляетесь от DbContext.Это происходит в конце GetAllUsers из-за этого оператора using.
    public async Task<IList<UserDetails>> GetAllUsers()
    {
        // The factory creates the unit of work here...
        using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
        {
            var userRepo = uow.GetRepository<IUserRepository>();

            var result = await userRepo.GetAllUsers();

            return Mapper.Map<List<UserDetails>>(result);
            // At the end of this using statement, the unit of work gets disposed
            // and in UnitOfWork.Dispose() you dispose of the DbContext.
        }
    }

Если вы хотите вызвать операцию дважды, вам необходимо:

  • Не распоряжаться контекстом в конце единицы работы; ИЛИ
  • Предоставить каждую единицу работы с собственным контекстом базы данных.
...