NopCommerce,一直沒有事務機制。作為一個商城,我覺得事務也還是很有必要的。以下事務代碼以3.9版本作為參考:
首先,IDbContext接口繼承IDisposable接口,以便手動釋放相關資源,并添加一個新方法CurrentEntries,目的是得到跟蹤實體的當前跟蹤狀態(tài)(主要作用是使用事務回滾后改變當前實體對應的狀態(tài)):
/// <summary>
/// 得到跟蹤實體的當前跟蹤狀態(tài)
/// </summary>
/// <returns></returns>
IEnumerable<DbEntityEntry> CurrentEntries();
自然相應的IDbContext接口實現(xiàn)類NopObjectContext也要實現(xiàn)該方 CurrentEntries() { return ChangeTracker.Entries(); }
注意:主項目代碼添加這個方法之后,所有需要操作數(shù)據(jù)庫的插件都要實現(xiàn)該方法,這個大家自行斟酌,如果插件也需要事務的話。
添加一個接口命名IUnitOfWork,如下:
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// 開啟事務
/// </summary>
/// <param name="isolationLevel"></param>
void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified);
/// <summary>
/// 提交
/// </summary>
void Commit();
/// <summary>
/// 回滾
/// </summary>
void Rollback();
/// <summary>
/// 釋放資源
/// </summary>
/// <param name="disposing">是否釋放</param>
void Dispose(bool disposing);
}
并實現(xiàn)該接口,添加實現(xiàn)類命名UnitOfWork,如下:
public class UnitOfWork : IUnitOfWork
{
private IDbContext _context;
private ObjectContext _objectContext;
private IDbTransaction _transaction;
private bool _disposed;
public UnitOfWork(IDbContext context)
{
_context = context;
}
public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
{
_objectContext = ((IObjectContextAdapter)_context).ObjectContext;
if (_objectContext.Connection.State != ConnectionState.Open)
_objectContext.Connection.Open();
_transaction = _objectContext.Connection.BeginTransaction(isolationLevel);
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
foreach (var entry in _context.CurrentEntries())
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
entry.State = EntityState.Unchanged;
break;
}
}
}
public void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
try
{
if (_objectContext != null && _objectContext.Connection.State == ConnectionState.Open)
_objectContext.Connection.Close();
}
catch (ObjectDisposedException)
{
}
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
代碼很好理解,我就不多做注釋了,不清楚的自行網(wǎng)上了解。
下面附上我寫的單元測試:
[TestClass]
public class UnitTest1
{
protected static NopObjectContext Context = new NopObjectContext(ConfigurationManager.ConnectionStrings["ConnectionStr"].ToString());
private readonly IUnitOfWork _unitOfWork = new UnitOfWork(Context);
protected readonly IRepository<ForumGroup> ForumGroupRepository = new EfRepository<ForumGroup>(Context);
protected readonly IRepository<Setting> SettingRepository = new EfRepository<Setting>(Context);
[TestMethod]
public void Can_Commit_Test()
{
try
{
_unitOfWork.BeginTransaction(); // 開啟事務
var forumGroup = new ForumGroup
{
Name = "ForumGroup1", // 自行建立Name的唯一約束測試,測試兩次第二次會自行回滾
DisplayOrder = 1,
CreatedOnUtc = DateTime.Now,
UpdatedOnUtc = DateTime.Now.AddDays(1)
};
ForumGroupRepository.Insert(forumGroup); // 第一次插入數(shù)據(jù)
var setting = new Setting
{
Name = "test_transaction_name",
Value = "test_transaction_value",
StoreId = 1
};
SettingRepository.Insert(setting);
_unitOfWork.Commit(); // 提交
}
catch (Exception)
{
_unitOfWork.Rollback(); // 回滾
}
Assert.AreEqual(ForumGroupRepository.TableNoTracking.Count(), 1);
Assert.AreEqual(SettingRepository.TableNoTracking.Count(x => x.Name == "test_transaction_name"), 1);
}
}
公告

浙公網(wǎng)安備 33010602011771號