[開源]Entity Framework 6 Repository 一種實現方式

  1. 在使用Entity Framework這種ORM框架得時候,通常結合Repository倉儲形式來處理業務邏輯;雖然這種模式帶來不少好處,可是也會引起一些爭議,在此拋開不談,小弟結合項目經驗來實現一下,歡迎大佬拍磚;
  2. 後續會帶來Dapper 基於Repository實現,代碼一些實現會兼容Dapper,因此作了一些比較醜陋得寫法;可是我得想法是經過一些Ioc能夠在Entity Framework和Dapper二者之間進行切換;
  3. 您能夠經過Nuget:Install-Package MasterChief.DotNet.Core.EF 安裝使用;
  4. 您能夠經過Github:MasterChief 查看具體源碼以及單元測試
  5. 歡迎Star,歡迎Issues;

插播一條求職

  1. 小弟擁有多年C#開發經驗,從事過路燈,消防平臺物聯網平臺開發,座標上海;
  2. 若是貴司在招聘,煩請大佬考慮下,聯繫郵箱:MeetYan@outlook.com

標準倉儲

/// <summary>
/// 標準倉儲接口
/// </summary>
public interface IRepository
{
    #region Methods
 
    /// <summary>
    /// 刪除記錄
    /// </summary>
    /// <returns>操做是否成功</returns>
    /// <param name="entity">須要操做的實體類.</param>
    bool Delete<T>(T entity) where T : ModelBase;
 
    /// <summary>
    /// 條件判斷是否存在
    /// </summary>
    /// <returns>是否存在</returns>
    /// <param name="predicate">判斷條件委託</param>
    bool Exist<T>(Expression<Func<T, bool>> predicate = null) where T : ModelBase;
 
    /// <summary>
    /// 根據id獲取記錄
    /// </summary>
    /// <returns>記錄</returns>
    /// <param name="id">id.</param>
    T GetByKeyId<T>(object id) where T : ModelBase;
 
    /// <summary>
    /// 條件獲取記錄集合
    /// </summary>
    /// <returns>集合</returns>
    /// <param name="predicate">篩選條件.</param>
    List<T> GetList<T>(Expression<Func<T, bool>> predicate = null) where T : ModelBase;
 
    /// <summary>
    /// 條件獲取記錄第一條或者默認
    /// </summary>
    /// <returns>記錄</returns>
    /// <param name="predicate">篩選條件.</param>
    T GetFirstOrDefault<T>(Expression<Func<T, bool>> predicate = null) where T : ModelBase;
 
    /// <summary>
    /// 建立一條記錄
    /// </summary>
    /// <returns>操做是否成功.</returns>
    /// <param name="entity">實體類記錄.</param>
    bool Create<T>(T entity) where T : ModelBase;
 
    /// <summary>
    /// 條件查詢
    /// </summary>
    /// <returns>IQueryable</returns>
    /// <param name="predicate">篩選條件.</param>
    IQueryable<T> Query<T>(Expression<Func<T, bool>> predicate = null) where T : ModelBase;
 
    /// <summary>
    /// 根據記錄
    /// </summary>
    /// <returns>操做是否成功.</returns>
    /// <param name="entity">實體類記錄.</param>
    bool Update<T>(T entity) where T : ModelBase;
 
    #endregion Methods
}

數據訪問上下文接口

public interface IDbContext : IDisposable, IRepository, IUnitOfWork
{
    /// <summary>
    ///     執行Sql 腳本查詢
    /// </summary>
    /// <param name="sql">Sql語句</param>
    /// <param name="parameters">參數</param>
    /// <returns>集合</returns>
    IEnumerable<T> SqlQuery<T>(string sql, IDbDataParameter[] parameters);
}

數據訪問上下文工廠

public interface IDatabaseContextFactory
{
    /// <summary>
    ///     Create this instance.
    /// </summary>
    /// <returns>The create.</returns>
    IDbContext Create();
}

基於EF的DbContext

public abstract class EfDbContextBase : DbContext, IDbContext
{
    #region Constructors
 
    /// <summary>
    ///     構造函數
    /// </summary>
    /// <param name="dbConnection">dbConnection</param>
    protected EfDbContextBase(DbConnection dbConnection)
        : base(dbConnection, true)
    {
        Configuration.LazyLoadingEnabled = false; //將不會查詢到從表的數據,只會執行一次查詢,能夠使用 Inculde 進行手動加載;
        Configuration.ProxyCreationEnabled = false;
        Configuration.AutoDetectChangesEnabled = false;
    }
 
    #endregion Constructors
 
    #region Fields
 
    /// <summary>
    ///     獲取 是否開啓事務提交
    /// </summary>
    public virtual bool TransactionEnabled => Database.CurrentTransaction != null;
 
    #endregion Fields
 
    #region Methods
 
    /// <summary>
    ///     顯式開啓數據上下文事務
    /// </summary>
    /// <param name="isolationLevel">指定鏈接的事務鎖定行爲</param>
    public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
    {
        if (!TransactionEnabled) Database.BeginTransaction(isolationLevel);
    }
 
    /// <summary>
    ///     提交當前上下文的事務更改
    /// </summary>
    /// <exception cref="DataAccessException">提交數據更新時發生異常:" + msg</exception>
    public void Commit()
    {
        if (TransactionEnabled)
            try
            {
                Database.CurrentTransaction.Commit();
            }
            catch (DbUpdateException ex)
            {
                if (ex.InnerException?.InnerException is SqlException sqlEx)
                {
                    var msg = DataBaseHelper.GetSqlExceptionMessage(sqlEx.Number);
                    throw new DataAccessException("提交數據更新時發生異常:" + msg, sqlEx);
                }
 
                throw;
            }
    }
 
    /// <summary>
    ///     建立記錄
    /// </summary>
    /// <returns>操做是否成功</returns>
    /// <param name="entity">須要操做的實體類.</param>
    public bool Create<T>(T entity)
        where T : ModelBase
    {
        ValidateOperator.Begin().NotNull(entity, "須要建立數據記錄");
        bool result;
        try
        {
            Entry(entity).State = EntityState.Added;
            result = SaveChanges() > 0;
        }
        catch (DbEntityValidationException dbEx)
        {
            throw new Exception(dbEx.GetFullErrorText(), dbEx);
        }
 
        return result;
    }
 
    /// <summary>
    ///     建立記錄集合
    /// </summary>
    /// <returns>操做是否成功.</returns>
    /// <param name="entities">實體類集合.</param>
    public bool Create<T>(IEnumerable<T> entities)
        where T : ModelBase
    {
        ValidateOperator.Begin().NotNull(entities, "須要建立數據集合");
        bool result;
        try
        {
            foreach (var entity in entities) Entry(entity).State = EntityState.Added;
 
            result = SaveChanges() > 0;
        }
        catch (DbEntityValidationException dbEx)
        {
            throw new Exception(dbEx.GetFullErrorText(), dbEx);
        }
 
        return result;
    }
 
    /// <summary>
    ///     刪除記錄
    /// </summary>
    /// <returns>操做是否成功</returns>
    /// <param name="entity">須要操做的實體類.</param>
    public bool Delete<T>(T entity)
        where T : ModelBase
    {
        ValidateOperator.Begin().NotNull(entity, "須要刪除的數據記錄");
        bool result;
        try
        {
            Entry(entity).State = EntityState.Deleted;
            result = SaveChanges() > 0;
        }
        catch (DbEntityValidationException dbEx)
        {
            throw new Exception(dbEx.GetFullErrorText(), dbEx);
        }
 
        return result;
    }
 
    /// <summary>
    ///     條件判斷是否存在
    /// </summary>
    /// <returns>是否存在</returns>
    /// <param name="predicate">判斷條件委託</param>
    public bool Exist<T>(Expression<Func<T, bool>> predicate = null)
        where T : ModelBase
    {
        return predicate == null ? Set<T>().Any() : Set<T>().Any(predicate);
    }
 
    /// <summary>
    ///     根據id獲取記錄
    /// </summary>
    /// <returns>記錄</returns>
    /// <param name="id">id.</param>
    public T GetByKeyId<T>(object id)
        where T : ModelBase
    {
        ValidateOperator.Begin().NotNull(id, "Id");
        return Set<T>().Find(id);
    }
 
    /// <summary>
    ///     條件獲取記錄集合
    /// </summary>
    /// <returns>集合</returns>
    /// <param name="predicate">篩選條件.</param>
    public List<T> GetList<T>(Expression<Func<T, bool>> predicate = null)
        where T : ModelBase
    {
        IQueryable<T> query = Set<T>();
 
        if (predicate != null) query = query.Where(predicate);
 
        return query.ToList();
    }
 
    /// <summary>
    ///     條件獲取記錄第一條或者默認
    /// </summary>
    /// <returns>記錄</returns>
    /// <param name="predicate">篩選條件.</param>
    public T GetFirstOrDefault<T>(Expression<Func<T, bool>> predicate = null)
        where T : ModelBase
    {
        IQueryable<T> query = Set<T>();
 
        if (predicate != null)
            return query.FirstOrDefault(predicate);
        return query.FirstOrDefault();
    }
 
    /// <summary>
    ///     條件查詢
    /// </summary>
    /// <returns>IQueryable</returns>
    /// <param name="predicate">篩選條件.</param>
    public IQueryable<T> Query<T>(Expression<Func<T, bool>> predicate = null)
        where T : ModelBase
    {
        IQueryable<T> query = Set<T>();
 
        if (predicate != null) query = query.Where(predicate);
 
        return query;
    }
 
    /// <summary>
    ///     顯式回滾事務,僅在顯式開啓事務後有用
    /// </summary>
    public void Rollback()
    {
        if (TransactionEnabled) Database.CurrentTransaction.Rollback();
    }
 
    /// <summary>
    ///     執行Sql 腳本查詢
    /// </summary>
    /// <param name="sql">Sql語句</param>
    /// <param name="parameters">參數</param>
    /// <returns>集合</returns>
    public IEnumerable<T> SqlQuery<T>(string sql, IDbDataParameter[] parameters)
    {
        ValidateOperator.Begin()
            .NotNullOrEmpty(sql, "Sql語句");
        // ReSharper disable once CoVariantArrayConversion
        return Database.SqlQuery<T>(sql, parameters);
    }
 
    /// <summary>
    ///     根據記錄
    /// </summary>
    /// <returns>操做是否成功.</returns>
    /// <param name="entity">實體類記錄.</param>
    public bool Update<T>(T entity)
        where T : ModelBase
    {
        ValidateOperator.Begin().NotNull(entity, "須要更新的數據記錄");
        bool result;
        try
        {
            var set = Set<T>();
            set.Attach(entity);
            Entry(entity).State = EntityState.Modified;
            result = SaveChanges() > 0;
        }
        catch (DbEntityValidationException dbEx)
        {
            throw new Exception(dbEx.GetFullErrorText(), dbEx);
        }
 
        return result;
    }
 
    #endregion Methods
}

單元測試

[TestClass()]
    public class SampleServiceTests
    {
        private IKernel _kernel;
        private ISampleService _sampleService;
        private readonly Guid _testId = "2F6D3C43-C2C7-4398-AD2B-ED5E82D79999".ToGuidOrDefault(Guid.Empty);
        private const string TestName = "EFSample";
 
        [TestInitialize]
        public void SetUp()
        {
            _kernel = new StandardKernel(new ServiceModule());
            Assert.IsNotNull(_kernel);
 
            _sampleService = _kernel.Get<ISampleService>();
            //if (!_sampleService.Exist(ent => ent.ID == _testID))
            //{
            //    _sampleService.Create(new EFSample() { UserName = _testName, ID = _testID });
            //}
        }
 
        /// <summary>
        /// 建立測試
        /// </summary>
        [TestMethod()]
        public void CreateTest()
        {
            bool actual = _sampleService.Create(new EfSample() { UserName = "ef" + DateTime.Now.ToString("MMddHHmmss") });
            Assert.IsTrue(actual);
 
            actual = _sampleService.Create(new EfSample() { UserName = "ef" + DateTime.Now.ToString("MMddHHmmss") });
            Assert.IsTrue(actual);
 
            actual = _sampleService.Create(new EfSample() { UserName = "ef" + DateTime.Now.ToString("MMddHHmmss") });
            Assert.IsTrue(actual);
 
            actual = _sampleService.Create(new EfSample() { UserName = "ef" + DateTime.Now.ToString("MMddHHmmss") });
            Assert.IsTrue(actual);
 
            actual = _sampleService.Create(new EfSample() { UserName = "ef" + DateTime.Now.ToString("MMddHHmmss") });
            Assert.IsTrue(actual);
        }
 
        [TestMethod()]
        public void GetFirstOrDefaultTest()
        {
            EfSample actual = _sampleService.GetFirstOrDefault(ent => ent.Id == _testId);
            Assert.IsNotNull(actual);
        }
 
        [TestMethod()]
        public void GetByKeyIdTest()
        {
            EfSample actual = _sampleService.GetByKeyId(_testId);
            Assert.IsNotNull(actual);
        }
 
        [TestMethod()]
        public void GetListTest()
        {
            // ReSharper disable once RedundantBoolCompare
            List<EfSample> actual = _sampleService.GetList(ent => ent.Available == true);
            Assert.IsNotNull(actual);
            CollectionAssert.AllItemsAreNotNull(actual);
        }
 
        [TestMethod()]
        public void UpdateTest()
        {
            EfSample sample = new EfSample
            {
                Id = _testId,
                ModifyTime = DateTime.Now,
                UserName = "modify"
            };
            bool actual = _sampleService.Update(sample);
            Assert.IsNotNull(actual);
        }
 
        [TestMethod()]
        public void TransactionSuccessTest()
        {
            EfSample sample = new EfSample
            {
                UserName = "TransactionSuccess1"
            };
 
            EfSample sample2 = new EfSample
            {
                UserName = "TransactionSuccess2"
            };
            bool actual = _sampleService.CreateWithTransaction(sample, sample2);
            Assert.IsTrue(actual);
        }
 
        [TestMethod()]
        public void TransactionFailTest()
        {
            EfSample sample3 = new EfSample
            {
                UserName = "TransactionSuccess3"
            };
 
            EfSample sample4 = new EfSample
            {
                UserName = null
            };
            bool actual = _sampleService.CreateWithTransaction(sample3, sample4);
            Assert.IsFalse(actual);
        }
 
        [TestMethod()]
        public void ExistTest()
        {
            bool actual = _sampleService.Exist(ent => ent.Id == _testId);
            Assert.IsTrue(actual);
 
            actual = _sampleService.Exist(ent => ent.UserName == TestName);
            Assert.IsTrue(actual);
 
            DateTime createTime = DateTime.Now.AddDays(-1);
            actual = _sampleService.Exist(ent => ent.CreateTime >= createTime);
            Assert.IsTrue(actual);
 
            actual = _sampleService.Exist(ent => ent.CreateTime <= DateTime.Now);
            Assert.IsTrue(actual);
 
            // ReSharper disable once RedundantBoolCompare
            actual = _sampleService.Exist(ent => ent.Available == true);
            Assert.IsTrue(actual);
 
            actual = _sampleService.Exist(ent => ent.Available != true);
            Assert.IsFalse(actual);
        }
 
        [TestMethod()]
        public void SqlQueryTest()
        {
            string sql = @"select * from [dbo].[EFSample]
where CreateTime>=@CreateTime
and Available=@Available
order by CreateTime desc";
            DbParameter[] parameter = {
                    new SqlParameter(){ ParameterName="@CreateTime", Value=DateTime.Now.AddDays(-1) },
                    new SqlParameter(){ ParameterName="@Available", Value=true }
                };
            List<EfSample> actual = _sampleService.SqlQuery(sql, parameter);
            Assert.IsNotNull(actual);
            CollectionAssert.AllItemsAreNotNull(actual);
        }
 
        /// <summary>
        /// 多線程測試
        /// </summary>
        [TestMethod()]
        public void CreateTestThreadTest()
        {
            Task[] tasks = {
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                                Task.Factory.StartNew(() => CreateTest()),
                            };
            Task.WaitAll(tasks);
        }
    }

結語

  1. 經過上述代碼,能夠在項目中很方面使用Entity Framework;
  2. 而且很輕鬆實現CURD以及事務處理,從而開發中關注業務便可;
  3. 小弟不才,大佬輕拍;
相關文章
相關標籤/搜索