我已經在一些地方看到EF已經實現了它自己的UnitOfWork和事務。我是否需要爲EF Core實現UnitOfWork,如果有的話,是否存在某種標準模式?
我真的不喜歡這一點,因爲我不希望有手動在每一個類型的回購我有作爲添加違背的原因GenericRepository已經投入了很多工作來製作泛型。
也期待在這裏所描述的的UnitOfWork屬性解決方案,但後退了,因爲作者自己討論的原因: Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter
但讓我嘗試奠定了我在下面的討論問題。
我有一個通用回購和一個通用服務。他們註冊在我的啓動是這樣的:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
我的通用回購看起來像這樣(留出的簡潔界面):
public enum FilteredSource
{
All,
GetAllIncluding,
}
public class GenericRepository<T> : IGenericRepository<T>
where T: BaseEntity
{
protected readonly ApplicationDbContext _context;
protected DbSet<T> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
// no eager loading
private IQueryable<T> All => _dbSet.Cast<T>();
// eager loading
private IQueryable<T> GetAllIncluding(
params Expression<Func<T, object>>[] includeProperties) =>
includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));
// eager loading
public async Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> entities = GetAllIncluding(includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return await entities.SingleOrDefaultAsync(e => e.Id == id);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
}
/// <summary>
/// Takes in a lambda selector and let's you filter results from GetAllIncluding or All.
/// </summary>
/// <param name="selector">labmda expression to filter results by.</param>
/// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
/// <param name="includeProperties">array of eager load lamda expressions.</param>
/// <returns></returns>
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All.Where(selector);
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties).Where(selector);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All;
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<T> InsertAsync(T entity)
{
if (entity == null)
{
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Insert");
}
await _dbSet.AddAsync(entity);
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
T entityToUpdate = await
_dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
if (entityToUpdate == null)
{
//return null;
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Update");
}
_dbSet.Update(entity);
return entity;
}
public async Task<T> DeleteAsync(T entity)
{
_dbSet.Remove(entity);
return await Task.FromResult(entity);
}
public Task SaveAsync() => _context.SaveChangesAsync();
}
服務層是這樣的:
public class GenericService<T> : IGenericService<T>
where T : BaseEntity
{
private IGenericRepository<T> _genericRepo;
public GenericService(IGenericRepository<T> genericRepo)
{
_genericRepo = genericRepo;
}
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetFiltered(selector, filteredSource,
includeProperties);
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetUnFiltered(filteredSource,
includeProperties);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _genericRepo.GetSingleIncludingAsync(id);
}
// eager loading
public async Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties)
{
T entity = await _genericRepo.GetSingleIncludingAsync(id, includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return entity;
}
public async Task<T> InsertAsync(T entity)
{
var result = await _genericRepo.InsertAsync(entity);
await _genericRepo.SaveAsync();
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
var result = await _genericRepo.UpdateAsync(entity);
if (result != null)
{
await _genericRepo.SaveAsync();
}
return result;
}
public async Task<T> DeleteAsync(T entity)
{
throw new NotImplementedException();
}
}
使用該服務的MVC Core Web API控制器的示例如下所示:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericService<Employee> _genericService;
public EmployeesController(IGenericService<Employee> genericService)
{
_genericService = genericService;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
return await _genericService.GetFiltered(
e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
FilteredSource.GetAllIncluding,
new Expression<Func<Employee, object>>[] { a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit }
);
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
var employee = await _genericService.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _genericService.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}
}
因此,這裏是我的問題: 我的理解是,如果你在兩個儲存帶入一個服務或控制器的UnitOfWork使用。如果您在回購庫1中操作數據並通過,然後在回購庫2中處理數據,並且它失敗(或反之亦然),則要將所有回滾。
到目前爲止,我沒有使用兩個回購。但是,如果出現這種情況,那將是因爲我將兩個GenericServices引入控制器,而這又會帶來兩個GenericRepos。
現在我們來談談範圍。 我必須把我的GenericRepo作爲Scoped。不是單身人士。因爲如果我在一個請求中查詢一個對象,然後嘗試在下一個請求中更新該對象,那麼我將得到一個錯誤,指出該對象無法更新,因爲它已經由前一個請求中的Singleton Repo跟蹤。 所以我把它的作用域如圖啓動段:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
我也帶來了服務的範圍的。相當多的猜測,我應該把它納入範圍之內。
現在,如果我引入類型Employee的GenericService - >將類型Employee的GenericRepository獲取到控制器中,並且還引入Type Case的GenericService - >其獲取Type Case的GenericRepository,這兩個不同GenericRepos?或者它們是相同的回購? 它們會被視爲同一事務,並且一起通過或失敗嗎?
還是我需要手動實現UnitOfWork?
另一個因素我認爲,進入這個判斷下列烤成核心DI線單身或作用域:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConn")));
正如我明白你的問題,我的觀點是:不,你不需要落實UOW。因爲最好根據特性設計控制器和業務對象或存儲庫:HumanResources而不是Employee,Sales而不是Order。此外,Uow是允許提交更改的對象,因此如果您擁有自己的存儲庫,則可以在操作控制器中落實更改 –
一般來說,單身人士是邪惡的,特別是在Web應用程序中。 https://duckduckgo.com/singletonitis –