Jun*_*ior 6 c# entity-framework transactions repository repository-pattern
我试图在我的应用程序中使用存储库设计模式有两个原因
如果我决定不在某个时候使用Entity Framework,我喜欢将我的应用程序与Entity分离
我希望能够重用与模型交互的逻辑
我成功设置并使用了存储库模式.但是,我有一个复杂性来处理哪个是交易.
我希望能够使用事务,以便我可以对存储库进行多次调用,然后提交或回滚.
这是我的存储库界面
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Support.Repositories.Contracts
{
public interface IRepository<TModel> where TModel : class
{
// Get records by it's primary key
TModel Get(int id);
// Get all records
IEnumerable<TModel> GetAll();
// Get all records matching a lambda expression
IEnumerable<TModel> Find(Expression<Func<TModel, bool>> predicate);
// Get the a single matching record or null
TModel SingleOrDefault(Expression<Func<TModel, bool>> predicate);
// Add single record
void Add(TModel entity);
// Add multiple records
void AddRange(IEnumerable<TModel> entities);
// Remove records
void Remove(TModel entity);
// remove multiple records
void RemoveRange(IEnumerable<TModel> entities);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我像这样为Entity Framework创建一个实现
using Support.Repositories.Contracts;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace Support.Repositories
{
public class EntityRepository<TEntity> : IRepository<TEntity>
where TEntity : class
{
protected readonly DbContext Context;
protected readonly DbSet<TEntity> DbSet;
public EntityRepository(DbContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
public TEntity Get(int id)
{
return DbSet.Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return DbSet.ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.SingleOrDefault(predicate);
}
public void Add(TEntity entity)
{
DbSet.Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
DbSet.AddRange(entities);
}
public void Remove(TEntity entity)
{
DbSet.Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
DbSet.RemoveRange(entities);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我创建了一个IUnitOfWork
与存储库交互的方式
using System;
namespace App.Repositories.Contracts
{
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
}
Run Code Online (Sandbox Code Playgroud)
然后我为Entity Framework实现了这样的接口,如下所示:
using App.Contexts;
using App.Repositories.Contracts;
using App.Repositories.Entity;
namespace App.Repositories
{
public class UnitOfWork : IUnitOfWork
{
private readonly AppContext _context;
public IUserRepository Users { get; private set; }
public IAddressRepository Addresses { get; private set; }
public UnitOfWork(AppContext context)
{
_context = context;
Users = new UserRepository(_context);
Addresses = new AddressRepository(_context);
}
public UnitOfWork() : this(new AppContext())
{
}
public int Save()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以像这样使用存储库
using(var repository = new UnitOfWork())
{
repository.Users.Add(new User(... User One ...))
repository.Save();
repository.Addresses(new Address(... Address For User One ...))
repository.Save();
repository.Users.Add(new User(... User Two...))
repository.Save();
repository.Addresses(new Address(... Address For User Two...))
repository.Save();
}
Run Code Online (Sandbox Code Playgroud)
现在,我希望能够使用数据库事务,所以只有在一切都很好的情况下才能提交回滚.
我的第一个想法是添加一个叫BeginTransaction()
我UnitOfWork
班级的新方法.但是我的代码只会与Entity Framework结合.
现在,我正在考虑创建一个提供的新接口BeginTransaction()
,Commit()
以及Rollback()
允许我为任何ORM编写实现的方法.
即
namespace Support.Contracts
{
public IRepositoryDatabase
{
SomethingToReturn BeginTransaction();
void Commit();
void Rollback();
}
}
Run Code Online (Sandbox Code Playgroud)
问题是如何IRepositoryDatabase
绑定到我的UnitOfWork以便我能正确实现?什么BeginTransaction()
需要返回?
Jun*_*ior 12
我想我找到了做到这一点的方法.(我希望我以正确的方式做到了)
这就是我所做的,我希望这可以帮助那些想要做同样事情的人.
我创建了一个新的界面
using System;
namespace Support.Repositories.Contracts
{
public interface IDatabaseTransaction : IDisposable
{
void Commit();
void Rollback();
}
}
Run Code Online (Sandbox Code Playgroud)
然后我IDatabaseTransaction
像这样为Entity框架实现了
using Support.Repositories.Contracts;
using System.Data.Entity;
namespace Support.Entity.Repositories
{
public class EntityDatabaseTransaction : IDatabaseTransaction
{
private DbContextTransaction _transaction;
public EntityDatabaseTransaction(DbContext context)
{
_transaction = context.Database.BeginTransaction();
}
public void Commit()
{
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
public void Dispose()
{
_transaction.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,我添加了一个新的方法调用BeginTransaction()
我的IUnitOfWork
合同,就像这样
using System;
namespace App.Repositories.Contracts
{
public interface IUnitOfWork : IDisposable
{
IDatabaseTransaction BeginTrainsaction();
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
}
Run Code Online (Sandbox Code Playgroud)
最后,以下是我UnitOfwork
对Entity的实现
using App.Contexts;
using App.Repositories.Contracts;
using App.Repositories.Entity;
using Support.Repositories;
namespace App.Repositories
{
public class UnitOfWork : IUnitOfWork
{
private readonly AppContext _context;
public IUserRepository Users { get; private set; }
public IAddressRepository Addresses { get; private set; }
public UnitOfWork(AppContext context)
{
_context = context;
Users = new UserRepository(_context);
Addresses = new AddressRepository(_context);
}
public UnitOfWork() : this(new AppContext())
{
}
public int Save()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
public IDatabaseTransaction BeginTransaction()
{
return new EntityDatabaseTransaction(_context);
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是我如何从控制器中使用UnitOfWork实现
using(var unitOfWork = new UnitOfWork())
using(var transaction = new unitOfWork.BeginTransaction())
{
try
{
repository.Users.Add(new User(... User One ...))
repository.Save();
repository.Addresses(new Address(... Address For User One ...))
repository.Save();
repository.Users.Add(new User(... User Two...))
repository.Save();
repository.Addresses(new Address(... Address For User Two...))
repository.Save();
transaction.Commit();
}
catch(Exception)
{
transaction.Rollback();
}
}
Run Code Online (Sandbox Code Playgroud)
Efe*_*Efe 12
在EF Core中,虽然 UnitOfWork 模式是在内部实现的,但您可以简单地使用IDbContextTransaction接口,如下所示(假设您使用依赖注入):
public interface IUnitOfWork
{
int SaveChanges();
Task<int> SaveChangesAsync();
IDbContextTransaction BeginTransaction();
Task<IDbContextTransaction> BeginTransactionAsync();
IUserRepository Users { get; }
IAddressRepository Addresses { get; }
}
Run Code Online (Sandbox Code Playgroud)
和实施:
public class UnitOfWork : IUnitOfWork, IDisposable
{
private bool _disposed;
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context,
IUserRepository userRepositpry, IAddressRepository addressRepository)
{
_context = context;
Users = userRepositpry;
Addresses = addressRepository;
}
public IUserRepository Users { get; }
public IAddressRepository Addresses { get; }
public int SaveChanges()
{
return _context.SaveChanges();
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public IDbContextTransaction BeginTransaction()
{
return _context.Database.BeginTransaction();
}
public async Task<IDbContextTransaction> BeginTransactionAsync()
{
return await _context.Database.BeginTransactionAsync();
}
protected void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
public class FooService
{
private readonly IUnitOfWork _unitOfWork;
public FooService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public void Bar()
{
using (var transaction = _unitOfWork.BeginTransaction())
{
try
{
_unitOfWork.Users.Add(new UserModel("dummy username"));
_unitOfWork.SaveChanges();
_unitOfWork.Addresses.Add(new AddressModel("dummy address"));
_unitOfWork.SaveChanges();
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
虽然 Rufo 爵士的评论是正确的,但您确实说过想要一个独立于 EF 的解决方案,虽然通常从 ORM 中抽象出来是一种矫枉过正,但如果您仍然决定自己处理事务,您可以使用TransactionScope
(这显然是实现在BeginTransaction
拥有之前控制交易context.Database
)。
有关详细信息,请参阅以下文章:https://msdn.microsoft.com/en-us/data/dn456843.aspx
相关的一点是,您可以将所有调用包含在 a 中TransactionScope
(这实际上也可以在其他 ORM 中开箱即用):
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingTransactionScope()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
var sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.CommandText =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
sqlCommand.ExecuteNonQuery();
using (var context =
new BloggingContext(conn, contextOwnsConnection: false))
{
var query = context.Posts.Where(p => p.Blog.Rating > 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
}
}
scope.Complete();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但需要注意以下注意事项:
该方法仍然存在一些局限性TransactionScope
:
Database.UseTransaction()
它不能与前面部分的方法结合使用 归档时间: |
|
查看次数: |
10369 次 |
最近记录: |