3

У меня сегодня проблема, и я не могу ее решить, я много искал и не могу попасть в решение, пожалуйста, помогите мне, если сможете.UOW + Repository + Autofac загружает два разных DbContext

Я реализую приложение MVC, которое использует EF + Repository Pattern + Unit of Work с Autofac как инжектор зависимостей.

Я был в состоянии работать с одним классом DbContext, но я столкнулся с ситуацией, когда мне нужно использовать другой экземпляр DbContext (который доступ к другой базе данных с другими учетными данными)

Позвольте мне объяснить лучше: у меня есть EntityA, который поступает из базы данных A (и имеет класс DatabaseA_Context). Поэтому мне нужен EntityB, который поступает из базы B (с собственным классом DatabaseB_Context).

Когда я регистрирую их с помощью AutoFac, в реализации GenericRepository вводится только последняя настроенная зависимость.

Я уже нашел статьи, в которых говорится, что Autofac переопределяет регистрацию с последним значением.

Я уже нашел другую статью, которая показывает, передаю ли IEnumerable в конструкторе UnitOfWork, я могу видеть все зарегистрированные типы для него, но я хочу конкретный.

Я достаточно ясно?

Мой код ниже:

Мой контроллер:

public class MyController : Controller 
{ 
    private readonly IBaseBLL<EntityA> _aBLL; 
    private readonly IBaseBLL<EntityB> _bBll; 

    public MyController(IBaseBLL<EntityA> aBLL, IBaseBLL<EntityB> bBLL) 
    { 
     _aBLL = aBLL; 
     _bBLL = bBLL; 
    } 
} 

Мой бизнес слой

public interface IBaseBLL<T> where T : class 
{ 
    T Select(Expression<Func<T, bool>> predicate); 
    T AddT entity); 
    void Update(T entity); 
    T Delete(T entity); 
} 

public class BaseBLL<T> : IBaseBLL<T> where T : class 
{ 
    private readonly IUnitOfWork _uow; 

    public BaseBLL(IUnitOfWork uow) 
    { 
     _uow = uow; 
    } 

    //implementation goes here... 
} 

Моя реализация UOW

public interface IUnitOfWork : IDisposable 
{ 
    int SaveChanges(); 
    IGenericRepository<T> Repository<T>() where T : class; 
} 

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly DbContext _dbContext; 
    private bool disposed = false; 
    private Dictionary<Type, object> repositories; 


    public UnitOfWork(DbContext dbContext) 
    { 
     _dbContext = dbContext; 
     repositories = new Dictionary<Type, object>(); 
    } 

    public IGenericReposity<T> Repository<T>() where T : class 
    { 
     if (repositories.Keys.Contains(typeof(T))) 
      return repositories[typeof(T)] as IGenericReposity<T>; 

     IGenericReposity<T> repository = new GenericRepository<T>(_dbContext); 
     repositories.Add(typeof(T), repository); 
     return repository ; 
    } 

    public int SaveChanges() 
    { 
     return _dbContext.SaveChanges(); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
      if (disposing) 
       _dbContext.Dispose(); 

     this.disposed = true; 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

Мой Repository реализация

public class GenericRepository<T> : IGenericRepositoryT> where T : class 
{ 
    protected readonly DbContext _dbContext; 
    protected IDbSet<T> _dbSet; 

    public GenericRepository(DbContext dbContext) 
    { 
     _dbContext = dbContext; 
     _dbSet = _dbContext.Set<T>(); 
    } 

    //implementation goes here... 
} 

Мой AutoFac регистрация (в Global.asax файл)

var builder = new ContainerBuilder(); 

builder.RegisterType(typeof(DatabaseA_Context)).As(typeof(DbContext)).InstancePerLifetimeScope(); 
builder.RegisterType(typeof(DatabaseB_Context)).As(typeof(DbContext)).InstancePerLifetimeScope(); 
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerRequest(); 

Пожалуйста, помогите

+0

У меня такая же проблема, как и при работе с несколькими базами данных и Autofac. Что вы сделали, чтобы решить эту проблему? Вы только что создали 2 экземпляра dbcontext в вашем автозапуске, или вы сделали это одинаково для своего подразделения? Каждый для одного dbcontext? – Mivaweb

+0

@Mivaweb использует атрибут [WithKey] перед параметром DbContext в вашем классе обслуживания –

ответ

1

Вы должны использовать Named and Keyed Service

builder.RegisterType<DatabaseA_Context>() 
     .Named<DbContext>("databaseA") 
     .InstancePerLifetimeScope(); 
builder.RegisterType<DatabaseB_Context>() 
     .Named<DbContext>("databaseB") 
     .InstancePerLifetimeScope(); 

Затем вы можете указать DbContext вы хотите для компонента при регистрации

builder.RegisterType<MyService>() 
     .As<IService>() 
     .WithParameter((pi, c) => pi.Name == "dbContext", 
         (pi, c) => c.ResolveNamed<DbContext>("databaseA")) 

или с помощью IIndex<,>

public class MyService : IService 
{ 
    public MyService(IIndex<String, DbContext> dbContexts) 
    { 
     var databaseA = dbContexts["databaseA"]; 
    } 
} 

Autofac также поддерживает указание имени регистрация с WithKeyAttribute

public class MyService : IService 
{ 
    public MyService([WithKey("DatabaseA")DbContext dbContext) 
    { 
    } 
} 

See the metadata documentation для получения дополнительной информации о том, как получить WithKeyAttribute настроить.

С помощью этого решения DbContext не будет зарегистрирован. Если вы хотите по умолчанию DbContext вы можете зарегистрировать один, как это:

builder.Register(c => c.ResolveNamed<DbContext>("databaseA")) 
     .As<DbContext>() 
     .InstancePerLifetimeScope(); 

Вы также можете использовать модуль, который будет выбрать правильную регистрацию на основе имени аргумента:

public class MyService : IService 
{ 
    public MyService(DbContext dbContextA, DbContext dbContextB) 
    { 
    } 
} 

Чтобы сделать это Вам нужно будет зарегистрировать этот Autofac Module

public class DbContextModule : Module 
{ 
    protected override void AttachToComponentRegistration(
     IComponentRegistry componentRegistry, IComponentRegistration registration) 
    { 
     registration.Preparing += Registration_Preparing; 
    } 

    private void Registration_Preparing(Object sender, PreparingEventArgs e) 
    { 
     Parameter parameter = new ResolvedParameter(
           (pi, c) => pi.ParameterType == typeof(DbContext), 
           (pi, c) => 
           { 
            if (pi.Name.Equals("dbContextA", StringComparison.OrdinalIgnoreCase)) 
            { 
             return c.ResolveNamed<DbContext>("databaseA"); 
            } 
            else if (pi.Name.Equals("dbContextB", StringComparison.OrdinalIgnoreCase)) 
            { 
             return c.ResolveNamed<DbContext>("databaseB"); 
            } 
            else 
            { 
             throw new NotSupportedException($"DbContext not found for '{pi.Name}' parameter name"); 
            } 
           }); 
     e.Parameters = e.Parameters.Concat(new Parameter[] { parameter }); 
    } 
} 

и

builder.RegisterModule<DbContextModule>() 
+0

Последний подход решил мою проблему, спасибо за помощь! –