Как использовать универсальный репозиторий, шаблон единицы работы с несколькими EDMX для нескольких баз данных
Я занимаюсь разработкой проекта, в котором есть 3 базы данных MS SQL Server.
Я использую,
- EntityFramework 6.4.0,
- Общий репозиторий,
- Шаблон единицы работы репозитория.
- ASP.Net MVC
- Модели данных с несколькими сущностями
Мне нужно сохранить изменения в нескольких таблицах в нескольких БД как одну транзакцию (используя одну SaveChanges()
метод)
Я выполнял проекты с использованием вышеуказанных шаблонов с использованием одной базы данных, одного класса UoW, одного общего репозитория. Теперь я застрял с несколькими БД.
Мой общий класс репозитория следующим образом
public class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
internal DbContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(DbContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public IEnumerable<TEntity> GetAll()
{
return context.Set<TEntity>().ToList();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void InsertRange(IList<TEntity> entityList)
{
dbSet.AddRange(entityList);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void DeleteRange(IEnumerable<TEntity> lstEntity)
{
dbSet.RemoveRange(lstEntity);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
Мой класс Unit Of Work следующим образом;
public class UnitOfWork<TContext> : IDisposable where TContext : DbContext, new()
{
private DbContext context;
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext> tbl_Data_EmployeeData_sin;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext> Tbl_Data_EmployeeData_sin
{
get
{
if (this.tbl_Data_EmployeeData_sin == null)
{
this.tbl_Data_EmployeeData_sin = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext>(context);
}
return tbl_Data_EmployeeData_sin;
}
}
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext> tbl_Data_EmployeeData_phl;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext> Tbl_Data_EmployeeData_phl
{
get
{
if (this.tbl_Data_EmployeeData_phl == null)
{
this.tbl_Data_EmployeeData_phl = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext>(context);
}
return tbl_Data_EmployeeData_phl;
}
}
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext> tbl_Data_EmployeeData_eng;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext> Tbl_Data_EmployeeData_eng
{
get
{
if (this.tbl_Data_EmployeeData_eng == null)
{
this.tbl_Data_EmployeeData_eng = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext>(context);
}
return tbl_Data_EmployeeData_eng;
}
}
public UnitOfWork()
{
context = new TContext();
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Мои классы уровня обслуживания следующие
public class svcEmployeeData
{
private UnitOfWork<DAL.RCPlusEntities> unitOfWork;
public svcEmployeeData()
{
unitOfWork = new UnitOfWork<DAL.RCPlusEntities>();
}
public void updateEmployees(TBL_RC_DATA_PHL_EmployeeData_SIN sin, TBL_RC_DATA_PHL_EmployeeData_ENG eng)
{
//updating Business Logic goes here
unitOfWork.Tbl_Data_EmployeeData_sin.Update(sin);
unitOfWork.Tbl_Data_EmployeeData_eng.Update(eng);
unitOfWork.Save();
}
}
Как преобразовать эту архитектуру в 3 EDMX по 3 базам данных...?
Заранее спасибо.
1 ответ
Ниже предполагается, что структура всех трех баз данных одинакова. Если структура отличается, я рекомендую избегать столь частых обобщений. Вместо этого поддерживайте разные уровни доступа к данным для каждой базы данных.
Причина, по которой вы не можете повторно использовать один и тот же класс для другой базы данных, заключается в том, что вы создаете экземпляр DbContext
в UnitOfWork
конструктор, как показано ниже:
private DbContext context; public UnitOfWork() { context = new TContext(); }
Вместо этого, если вы примете экземпляр DbContext
в конструкторе, я думаю, то же самое должно работать с любой базой данных.
private DbContext context;
public UnitOfWork(DbContext context)
{
this.context = context;
}
Конечно, в дальнейшем вам нужно изменить уровень обслуживания, чтобы внедрить правильный экземпляр DbContext
.
Кстати, подумайте еще раз, прежде чем использовать слишком много оберток поверх оберток. Пожалуйста, обратитесь к этому сообщению для получения более подробной информации.
Теперь, как вы сказали:
Мне нужно сохранить изменения в нескольких таблицах в нескольких БД как одну транзакцию (с помощью одного метода SaveChanges())
и прокомментировал:
Мне нужно обрабатывать транзакции. Если при обновлении 2-й таблицы БД возникла какая-либо ошибка, необходимо откатить изменения в 1-й таблице БД. Легко сделать это с помощью одного UnitOwWork.Save()
Обратите внимание на вопрос, поднятый @Holger в комментарии.
Примечание. Транзакции между базами данных на SQL-сервере поддерживаются только на одном сервере. - Хольгер
В SaveChanges
метод здесь не поможет. Вместо этого вы можете использовать TransactionScope
на уровне сервиса для фиксации / отката всего пакета по базам данных.
Итак, на уровне обслуживания это выглядит примерно так:
using (TransactionScope scope = new TransactionScope())
{
//Create unitOfWorkDb1 here
unitOfWorkDb1.DoSomething();
unitOfWorkDb1.Save();
//Create unitOfWorkDb2 here
unitOfWorkDb2.DoSomething();
unitOfWorkDb2.Save();
scope.Complete();
}
Пожалуйста, обратитесь к этому и этому сообщению о масштабах транзакции. Также эта статья очень информативна.