EF6 DBContext (несколько дБ), UnitofWork, репозиторий, Autofac IOC зависимости - PullRequest
0 голосов
/ 17 октября 2018

Я использую Ef6 в приложении .net web Api.Это устаревший монолит, который мы пытаемся преобразовать в архитектуру микросервиса CQRS.В настоящее время мы сталкиваемся с проблемами параллелизма с использованием EF6, и я понятия не имею, как решить эту проблему.Для начала у меня есть две базы данных (запись и чтение).Мы используем SQL Server 2012 в качестве RDBMS.Я использую IOC / DI с Autofac и, поскольку у нас есть 2 дБ, я хотел создать 2 различных объекта контекста, и вот как выглядит код.

// Конструктор Unit of Work

   public UnitOfWork(IContext readContext, IContext writeContext, IMapper 
    mapper)
    {
       _readContext = readContext;
      _writeContext = writeContext;
      _mapper = mapper; //this is for the automapper
    }

Это создает 2 объекта контекста из интерфейса IContext.

// Интерфейс IContext имеет 2 пользовательских объекта контекста следующим образом

public partial class MyWriteContext : IContext,IDisposable
{
}

public partial class MyReadContext : IContext,IDisposable
{
}

Мы специально создали эти 2 класса как частичные, поскольку это те же имена, которые мы используем для создания фактического Ef6dbcontexts

// Объекты контекста Actual Entity Framework, сгенерированные с помощью обратного poco

public partial class EfWriteContext : DbContext{}

partial class EFReadContext : DbContext{}

Это моя конфигурация Autofac для одного и того же dbcontext, единицы работы и общего хранилища, все они определены в модулях autofac и зарегистрированы иразрешается в веб-API Startup.cs

// Модуль регистрации контекстного автофака

protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<MyReadContext>()
                .Named<IContext>("MyReadContext")
                .InstancePerDependency();

            builder.RegisterType<MyWriteContext>()
                   .Named<IContext>("MyWriteContext")
                   .InstancePerDependency();
        }

        protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
        {
            registration.Preparing -= Registration_Preparing;
            registration.Preparing += Registration_Preparing;
        }

        private void Registration_Preparing(object sender, PreparingEventArgs e)
        {
            Parameter parameter = new ResolvedParameter(
                (pi, c) => pi.ParameterType == typeof(IContext),
                                        (pi, c) =>
                                        {
                                            if (pi.Name.Equals("readContext", StringComparison.OrdinalIgnoreCase))
                                            {
                                                return c.ResolveNamed<IContext>("MyReadContext");
                                            }
                                            else if (pi.Name.Equals("writeContext", StringComparison.OrdinalIgnoreCase))
                                            {
                                                return c.ResolveNamed<IContext>("MyWriteContext");
                                            }
                                            else
                                            {
                                                return c.ResolveNamed<IContext>("MyReadContext");
                                            }
                                        });
            e.Parameters = e.Parameters.Concat(new Parameter[] { parameter });
        }

// Единица работы

 builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerDependency();

// Хранилище

builder.RegisterAssemblyTypes(Assembly.Load("Infrastructure"))
                          .Where(t => t.Name.EndsWith("Repository"))
                          .AsImplementedInterfaces()
                          .InstancePerDependency();

// Конструктор репозитория

public Repository(IContext dbContext)
 {
    context = dbContext;
    _dbset = context.Set<TEntity>();
 }

Этот код отлично работает и вставляет и обновляет данные в случае использования одного пользователя при вызове writecontext.SaveChanges ().Это даже работает, если несколько пользователей имеют временной интервал между ними при сохранении данных в БД, но проблема возникает, когда 2 пользователя фактически пытаются сохранить данные в БД одновременно.Затем он начинает выдавать ошибки

  1. Аргумент 'entitySetName' не может быть нулевым, пустым или содержать только пробел.

  2. Элемент уже существует вмодель.

  3. Нарушение ограничения ПЕРВИЧНЫЙ КЛЮЧ.Невозможно вставить дубликат ключа в объект.Дубликат значения ключа (0, 4862).Оператор был прерван.

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

...