У меня есть веб-служба Web API, которая использует EF для операций с базой данных и Unity для внедрения зависимостей. У меня есть несколько баз данных с разными именами, но с одной и той же схемой. На каждый розничный магазин приходится одна база данных. Когда пользователь входит в систему, в зависимости от его привилегий, он может выбрать, с каким магазином он хочет работать. Это проблема с использованием внедрения зависимостей, потому что мне нужно изменить базу данных после внедрения репозитория. У меня есть что-то, что работает, но я не уверен, что это лучший подход.
Мои конкретные вопросы:
Это хороший подход к этой проблеме? Я видел другие вопросы, в которых упоминается изменение строки подключения во время выполнения, но я думаю, что мне нужно либо иметь строку подключения для каждого хранилища в моем
Web.Config
, либо каким-то образом динамически создавать строку подключения.Нужна ли мне логика
Dispose
на моей фабрике? Если бы я вводил репозиторий напрямую, я знаю, что мне это не понадобилось бы. Поскольку я создаю репо из введенной фабрики, могу ли я доверять Unity, чтобы избавиться от репо и закрыть соединения с базой данных в какой-то момент? Должен ли я вместо этого использовать операторыusing
вокруг сгенерированных репозиториев?
Некоторые вопросы, которые я рассмотрел, пытаясь решить эту проблему, - это этот , этот и < href="https://stackoverflow.com/questions/29113206/change-injected-object-at-runtime">этот. Однако ни один из них напрямую не выполняет то, что я пытаюсь сделать. Ниже мое текущее решение.
Это мой репозиторий и его интерфейс. Я пропустил некоторые методы для краткости:
IGenericRepository
public interface IGenericRepository<T> where T: class
{
IQueryable<T> Get();
void ChangeDatabase(string database);
void Update(T entityToUpdate);
void Save();
}
Универсальный репозиторий
public class GenericRepository<TDbSet, TDbContext> :
IGenericRepository<TDbSet> where TDbSet : class
where TDbContext : DbContext, new()
{
internal DbContext Context;
internal DbSet<TDbSet> DbSet;
public GenericRepository() : this(new TDbContext())
{
}
public GenericRepository(TDbContext context)
{
Context = context;
DbSet = Context.Set<TDbSet>();
}
public virtual IQueryable<TDbSet> Get()
{
return DbSet;
}
public void ChangeDatabase(string database)
{
var dbConnection = Context.Database.Connection;
if (database == null || dbConnection.Database == database)
return;
if (dbConnection.State == ConnectionState.Closed)
{
dbConnection.Open();
}
Context.Database.Connection.ChangeDatabase(database);
}
public virtual void Update(TDbSet entityToUpdate)
{
DbSet.Attach(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void Save()
{
Context.SaveChanges();
}
}
Чтобы использовать внедрение зависимостей, я внедряю фабрику репозитория, которой я могу передать имя базы данных. Фабрика создает репозиторий с базой данных по умолчанию строки подключения, изменяет базу данных на указанную и возвращает репозиторий.
IRepositoryFactory
public interface IRepositoryFactory
{
IGenericRepository<TDbSet> GetRepository<TDbSet>(string dbName) where TDbSet : class;
}
Фабрика StoreEntities
public class StoreEntitiesFactory : IRepositoryFactory
{
private bool _disposed;
readonly StoreEntities _context;
public StoreEntitiesFactory()
{
_context = new StoreEntities();
}
public IGenericRepository<TDbSet> GetRepository<TDbSet>(string dbName) where TDbSet : class
{
var repo = new GenericRepository<TDbSet, StoreEntities>(_context);
repo.ChangeDatabase(dbName);
return repo;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_context.Dispose();
}
_disposed = true;
}
~StoreEntitiesFactory()
{
Dispose(false);
}
}
Вот как я добавляю фабрику репозитория в свой файл WebApiConfig:
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IRepositoryFactory, StoreEntitiesFactory>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
}
}
Наконец, вот как я бы использовал фабрику в своем контроллере:
Контроллер хранилища
public class StoreController : ApiController
{
private readonly IRepositoryFactory _storeEntitiesRepoFactory;
public StoreController(IRepositoryFactory storeEntitiesRepoFactory)
{
_storeEntitiesRepoFactory = storeEntitiesRepoFactory;
}
[HttpGet]
public IHttpActionResult Get()
{
var dbName = getStoreDbName(storeNumberWeGotFromSomewhere);
try
{
var employeeRepo = _storeEntitiesRepoFactory.GetRepository<Employee>(dbName);
var inventoryRepo = _storeEntitiesRepoFactory.GetRepository<Inventory>(dbName);
var employees = employeeRepo.Get().ToList();
var inventory = inventoryRepo.Get().ToList();
}
catch (Exception ex)
{
return InternalServerError();
}
}
}