У меня есть веб-сервис веб-API, который использует EF для операций с базой данных и Unity для внедрения зависимостей. У меня есть несколько баз данных с разными именами, но одна и та же схема. В каждом розничном магазине есть одна база данных. Когда пользователь входит в систему, в зависимости от своих привилегий, он может выбрать, с каким магазином он хочет работать. Это сложная задача с использованием внедрения зависимостей, потому что мне нужно изменить базу данных после внедрения репозитория. У меня есть кое-что, что работает, но я не уверен, что это лучший подход.
Мои конкретные вопросы:
Это хороший подход к этой проблеме? Я видел другие вопросы, в которых упоминается об изменении строки подключения во время выполнения, но я думаю, что мне нужно либо иметь строку подключения для магазина в моем Web.Config
, либо как-то динамически построить строку подключения.
Нужна ли логика Dispose
на моем заводе? Если бы я вводил репозиторий напрямую, я бы знал, что он мне не нужен. Поскольку я генерирую репо из внедренной фабрики, могу ли я верить, что Unity утилизирует репо и в какой-то момент закрывает соединения db? Стоит ли использовать вместо операторов using
сгенерированные репозитории?
Некоторые вопросы, на которые я смотрел, пытаясь решить эту проблему: этот , этот и этот . Однако никто из них не выполняет то, что я пытаюсь сделать напрямую. Ниже мое текущее решение.
Это мой репозиторий и его интерфейс. Я упустил некоторые методы для краткости:
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;
}
StoreEntitiesFactory
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);
}
}
Наконец, вот как я бы использовал фабрику в моем контроллере:
StoreController
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();
}
}
}