我正在考虑实施处理多个数据源的单一工作单元的选项 - 实体框架。我想出了一个尝试性的方法 - 现在处理一个单一的背景 - 但它显然不是一个好主意。
如果我们要分析下面的代码,您会认为这是一个糟糕的实现吗?交易范围的生命周期是一个潜在的问题吗?
当然,如果我们用不同的上下文包装事务范围,如果第二个context.SaveChanges()失败,我们将被覆盖...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
using(UnitOfWork unitOfWork = new UnitOfWork())
{
var repository = new EmployeeRepository(unitOfWork);
var employee = repository.CreateOrGetEmployee("Whatever Name");
Console.Write(employee.Id);
unitOfWork.SaveChanges();
}
}
}
class UnitOfWork : IDisposable
{
TestEntities _context;
TransactionScope _scope;
public UnitOfWork()
{
_scope = new TransactionScope();
_context = new TestEntities();
}
public void SaveChanges()
{
_context.SaveChanges();
_scope.Complete();
}
public TestEntities Context
{
get
{
return _context;
}
}
public void Dispose()
{
_scope.Dispose();
_context.Dispose();
}
}
class EmployeeRepository
{
UnitOfWork _unitOfWork;
public EmployeeRepository(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public Employee GetEmployeeById(int employeeId)
{
return _unitOfWork.Context.Employees.SingleOrDefault(e => e.Id == employeeId);
}
public Employee CreateEmployee(string fullName)
{
Employee employee = new Employee();
employee.FullName = fullName;
_unitOfWork.Context.SaveChanges();
return employee;
}
public Employee CreateOrGetEmployee(string fullName)
{
var employee = _unitOfWork.Context.Employees.FirstOrDefault(e => e.FullName == fullName);
if (employee == null)
{
employee = new Employee();
employee.FullName = fullName;
this.AddEmployee(employee);
}
return employee;
}
public Employee AddEmployee(Employee employee)
{
_unitOfWork.Context.Employees.AddObject(employee);
_unitOfWork.Context.SaveChanges();
return employee;
}
}
}
答案 0 :(得分:6)
为什么要在构造函数中启动TransactionScope
?您只需要保存更改。
public void SaveChanges()
{
// SaveChanges also uses transaction which uses by default ReadCommitted isolation
// level but TransactionScope uses by default more restrictive Serializable isolation
// level
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
_context.SaveChanges();
scope.Complete();
}
}
如果您希望工作单元具有更多上下文,则只需将所有这些上下文包装在同一工作单元中。你的SaveChanges
会变得有点复杂:
public void SaveChanges()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
_contextA.SaveChanges(SaveOptions.DetectChangesBeforeSave);
_contextB.SaveChanges(SaveOptions.DetectChangesBeforeSave);
scope.Complete();
_contextA.AcceptAllChanges();
_contextB.AcceptAllChanges();
}
}
此版本将重置上下文的内部状态与保存操作分开。原因是如果第一个上下文成功保存更改但第二个上下文触发异常,则事务将被回滚。因此我们不希望第一个上下文已经清除所有已接受的更改(我们将丢失有关已执行更改的信息,我们将无法再次保存它们)。