c# - 如何使用 Entity Framework 为我的存储库实现事务?

标签 c# entity-framework transactions repository repository-pattern

我尝试在我的应用程序中使用存储库设计模式有两个原因

  1. 我喜欢将我的应用程序与 Entity 分离,以防我决定在某个时候不使用 Entity Framework

  2. 我希望能够重用与模型交互的逻辑

我成功设置并使用了存储库模式。但是,我有一个复杂的问题要处理,那就是事务。

我希望能够使用事务,以便我可以对存储库进行多次调用,然后提交或回滚。

这是我的仓库界面

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);
    }
}

然后我像这样为 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);
        }

    }
}

现在,我创建一个 IUnitOfWork 来像这样与存储库交互

using System;

namespace App.Repositories.Contracts
{
    public interface IUnitOfWork : IDisposable
    {
        IUserRepository Users { get; }
        IAddressRepository Addresses { get;  }
    }
}

然后我像这样为 Entity Framework 实现了这个接口(interface):

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();
        }
    }
}

我可以像这样使用存储库

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();
}

现在,我希望能够使用数据库事务,因此只有在一切正常时才提交,否则回滚。

我的第一个想法是向我的 UnitOfWork 类添加一个名为 BeginTransaction() 的新方法。但只会将我的代码耦合到 Entity Framework 。

现在,我正在考虑创建一个新接口(interface),提供 BeginTransaction()Commit()Rollback() 方法,这将允许我为任何 ORM 编写一个实现。

namespace Support.Contracts
{
    public IRepositoryDatabase
    {
        SomethingToReturn BeginTransaction();

        void Commit();
        void Rollback();
    }
}

问题是我如何将 IRepositoryDatabase 绑定(bind)回我的 UnitOfWork 以便我可以正确实现? BeginTransaction() 需要返回什么?

最佳答案

我想我找到了实现它的方法。 (我希望我做对了)

这是我所做的,我希望这能帮助那些想做同样事情的人。

我像这样创建了一个新的界面

using System;

    namespace Support.Repositories.Contracts
    {
        public interface IDatabaseTransaction : IDisposable
        {
            void Commit();
    
            void Rollback();
        }
    }

然后我像这样为 Entity Framework 实现了IDatabaseTransaction

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();
        }
    }
}

然后,我向我的 IUnitOfWork 合约添加了一个名为 BeginTransaction() 的新方法

using System;

namespace App.Repositories.Contracts
{
    public interface IUnitOfWork : IDisposable
    {
        IDatabaseTransaction BeginTransaction();
        IUserRepository Users { get; }
        IAddressRepository Addresses { get;  }
    }
}

最后,下面是我对 Entity 的 UnitOfwork 实现

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);
        }
    }
}

下面是我如何从我的 Controller 中使用 UnitOfWork 实现

using(var unitOfWork = new UnitOfWork())
using(var transaction = new unitOfWork.BeginTransaction())
{
     try
     {
         unitOfWork.Users.Add(new User(... User One ...))
         unitOfWork.Save();

         unitOfWork.Addresses(new Address(... Address For User One ...))
         unitOfWork.Save();

         unitOfWork.Users.Add(new User(... User Two...))
         unitOfWork.Save();

         unitOfWork.Addresses(new Address(... Address For User Two...))
         unitOfWork.Save();
         transaction.Commit();
     } 
     catch(Exception)
     {
          transaction.Rollback();
     }

}

关于c# - 如何使用 Entity Framework 为我的存储库实现事务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39906474/

相关文章:

c# - 通过代码添加 Entity Framework 迁移

linux - 为什么 sqlite 独占事务不尊重 linux 上的独占?

c# - 在哪里可以找到MSIL字符串常量中的转义字符列表?

c# - 从 javascript 函数调用 webservice 并存储结果

c# - 简单的无锁秒表

c# - 关于在 HttpWebRequest GetResponseStream 中选择缓冲区大小

c# - 如何使用 Entity Framework Code First Fluent API 仅在连接表上级联删除

entity-framework - 使用 EF Core 3.1 将 SQL 查询挂载到 EXTRACT postgresql 函数

Spring事务传播REQUIRED,REQUIRES_NEW

django - 长时间运行的 django 事务完整性错误