Вторая операция началась в этом контексте до того, как предыдущая асинхронная операция была завершена с помощью UnitofWork и async.

Вторая операция началась в этом контексте до завершения предыдущей асинхронной операции. Используйте «ожидание», чтобы убедиться, что все асинхронные операции завершены, прежде чем вызывать другой метод в этом контексте. Любые члены экземпляра не гарантируют потокобезопасность.

Мой код единицы работы

 public class UnitOfWork : IUnitOfWork
    {
        private readonly CAMSDbEntities _context;
        private bool _disposed;
        public Dictionary<Type, object> repositories = new Dictionary<Type, object>();
        private Guid _objectId;

        public UnitOfWork(IContextFactory contextFactory)
        {
            _context = contextFactory.DbContext as CAMSDbEntities;
            _objectId = Guid.NewGuid();
        }

        public IGenericRepository<T> Repository<T>() where T : class
        {
            if (repositories.Keys.Contains(typeof(T)) == true)
            {
                return repositories[typeof(T)] as GenericRepository<T>;
            }
            GenericRepository<T> repo = new GenericRepository<T>(_context);
            repositories.Add(typeof(T), repo);
            return repo;
        }

Моя конфигурация Unity

      container.RegisterType<IHttpContext, HttpContextObject>();
                container.RegisterType<IDataBaseManager, DataBaseManager>();
                container.RegisterType<IContextFactory, ContextFactory>();

                container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));

                container.RegisterType<IUnitOfWork, UnitOfWork>();

                container.RegisterType<IAnalytics, DashbordService>();
    GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);

Контроллер веб-API

 public class DashbordController : ApiController
        {
            private static IAnalytics _analytics;
            public DashbordController(IAnalytics dashbordService)
            {
                _analytics = dashbordService;
            }

            [HttpGet]
            [Route("GetStudentAssessmentHistory")]
            public IHttpActionResult GetStudentAssessmentHistory(int studentID)
            {
                var result = _analytics.GetStudentAssessmentHistoryGraphData(studentID);
                return Ok(result);
            }

            [HttpGet]
            [Route("GetStudentFeePaymentHistory")]
            public async Task<IHttpActionResult> GetStudentFeePaymentData(int studentID)
            {
                var result = await _analytics.GetStudentFeePaymentData(studentID);
                return Ok(result);
            }

            [HttpGet]
            [Route("GetLedgerHitoryByDepartment")]
            public async Task<IHttpActionResult> GetLedgerHitoryByDepartment(int schoolID, int departmentId)
            {
                var result = await _analytics.GetLedgerHitory(schoolID, departmentId);
                return Ok(result);
            }

            [HttpGet]
            [Route("GetLedgerExpenseTrendByDepartment")]
            public async Task<IHttpActionResult> GetLedgerExpenseTrendByDepartment(int schoolID)
            {
                var result = await _analytics.GetLedgerExpenseTrend(schoolID);
                return Ok(result);
            }

код службы панели управления

  public async Task<List<LedgerExpense>> GetLedgerExpenseTrend(int schoolId)
        {
            try
            {
                var ledgerExpenses = new List<LedgerExpense>();
                var currentDate = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, INDIAN_ZONE);
                DateTime previoYearDate = currentDate.AddYears(-1);
                var ledgerPayments = await  _unitOfWork.Repository<LedgerDetail>().GetManyAsync(x => x.SchoolID == schoolId && x.PaymentDate <= currentDate
                                                       && x.PaymentDate >= previoYearDate);

                foreach (var ledgerPayment in ledgerPayments.OrderBy(x => x.PaymentDate).GroupBy(y => y.DepartmentID))
                {
                    var department = await  _unitOfWork.Repository<DeptartmentType>().GetAsync(x => x.ID == ledgerPayment.Key);

                    var ledgerData = new LedgerExpense
                    {
                        Department = department.DepartmentName,
                        TotalLedgerExpense = 0
                    };

                    foreach (var departmentPayment in ledgerPayment)
                    {
                        ledgerData.TotalLedgerExpense += departmentPayment.TotalPaidAmount;
                    }

                    ledgerExpenses.Add(ledgerData);
                }

                return ledgerExpenses;
            }
            catch (Exception ex)
            {
                logger.Log("An error occurred while fetching ledger expenses");
                return null;
            }
        }

У меня есть аналогичный тип асинхронных методов, реализованных в моем коде службы приборной панели. всякий раз, когда я запрашиваю пользовательский интерфейс панели управления, все запросы одновременно поступают на один и тот же контроллер и создают новый объект для unitofwork и dbcontext для каждого запроса один за другим. иногда это работает отлично, но иногда я думаю, что объект unitofwork и dbcontext перетекает с неправильным потоком и выдает эту ошибку. Я думаю, что каким-то образом он выбирает неправильный dbcontext, который уже занят каким-то другим запросом API от службы панели инструментов.


person Vivek chandra Joshi    schedule 07.04.2018    source источник
comment
Никаких операторов использования для последующего удаления единицы работы? Ваша обработка ошибок внутри службы данных совершенно бесполезна, вы не обрабатываете, вы игнорируете ошибки, нет необходимости в обработке ошибок, если вы не делаете с ней ничего полезного, вы могли бы также передать ее клиенту, она будет более значимым, чем регистрация строки и игнорирование потенциальных ошибок...   -  person Icepickle    schedule 07.04.2018
comment
@Icepickle, где именно вы хотите, чтобы я избавился от единицы работы? Я думаю, что все запросы внутри контроллера создадут новый экземпляр единицы работы, но по-прежнему единица работы и контекст будут совместно использоваться несколькими экземплярами контроллера для каждого запроса.   -  person Vivek chandra Joshi    schedule 07.04.2018
comment
У вас так много методов в вашем коде. Пожалуйста, сузьте ошибку до определенного метода. Если все они выдают одну и ту же ошибку, просто опубликуйте один метод, чтобы нам не пришлось читать так много кода. Также объясните, как протекает работа — контроллер › API › репозиторий?   -  person CodingYoshi    schedule 07.04.2018
comment
@MickyD Я не считаю эту проблему дубликатом, так как здесь объект единицы работы, созданный в конструкторе каждого запроса службы, и он предоставит новый экземпляр для всех запросов API в контроллере dasborad, но все еще выбирает неправильный поток для обработки единицы работы и dbcontext   -  person Vivek chandra Joshi    schedule 07.04.2018
comment
Это не имеет никакого значения   -  person MickyD    schedule 07.04.2018
comment
@CodingYoshi да, все они выдают ошибку, но каждый раз она может меняться. некоторые работают нормально, а некоторые терпят неудачу в зависимости от того, какой поток dbcontext блокируется. поток похож на пользовательский интерфейс. Я запрашиваю все методы контроллера одновременно, которые создают новый экземпляр для каждого запроса внутри контроллера и косвенно создают новые объекты для unitofwork/dbcontext и запрашивают код службы. Controller›webapi›unitofwork›Service-(может дать сбой в любой службе)   -  person Vivek chandra Joshi    schedule 07.04.2018
comment
@MickyD отлично работал после удаления ключевого слова static из контроллера (private static IAnalytics _analytics;) Спасибо CodingYoshi за понимание проблемы.   -  person Vivek chandra Joshi    schedule 07.04.2018
comment
Кажется, вы кешируете репозитории... Очень сложно отследить, кто что использует, где, почему и когда. И приглашая такого рода ошибки. Преждевременная оптимизация по-прежнему является причиной всех ошибок.   -  person Henk Holterman    schedule 07.04.2018
comment
@HenkHolterman Можете объяснить свою мысль на каком-нибудь примере? Я не могу понять, где именно мне нужно изменить в репозиториях   -  person Vivek chandra Joshi    schedule 09.04.2018
comment
Я отреагировал на код repositories.Keys.Contains(). Это то, что вы должны оставить в контейнере DI или просто использовать одноразовые.   -  person Henk Holterman    schedule 09.04.2018


Ответы (1)


Пожалуйста, удалите ключевое слово static в вашем контроллере из этого кода:

private static IAnalytics _analytics;`

После того, как это будет создано, оно никогда не будет создано снова, если пул приложений не будет перезапущен (ручной или перезапуск IIS и т. д.). Поскольку вы используете один и тот же экземпляр для всех запросов, вы получаете эту ошибку случайным образом. Если запрос завершится до поступления следующего, это НЕ приведет к ошибке. Иначе будет. Отсюда и причина не всегда получать ошибку (как вы упоминаете в своем вопросе).

Пожалуйста, прочитайте о том, как static влияет на дизайн в веб-сценарии (или на сервере).

Попробуйте подумать о веб-запросах как об одной транзакции, все классы создаются для каждого запроса, а затем отбрасываются после того, как запрос был обслужен. Это означает, что если у вас есть static или любой другой механизм для совместного использования, он будет использоваться совместно между запросами.

person CodingYoshi    schedule 07.04.2018