- 在使用Entity Framework這種ORM框架得時候,通常結合Repository倉儲形式來處理業務邏輯;雖然這種模式帶來不少好處,可是也會引起一些爭議,在此拋開不談,小弟結合項目經驗來實現一下,歡迎大佬拍磚;
- 後續會帶來Dapper 基於Repository實現,代碼一些實現會兼容Dapper,因此作了一些比較醜陋得寫法;可是我得想法是經過一些Ioc能夠在Entity Framework和Dapper二者之間進行切換;
- 您能夠經過Nuget:Install-Package MasterChief.DotNet.Core.EF 安裝使用;
- 您能夠經過Github:MasterChief 查看具體源碼以及單元測試
- 歡迎Star,歡迎Issues;
插播一條求職
- 小弟擁有多年C#開發經驗,從事過路燈,消防平臺物聯網平臺開發,座標上海;
- 若是貴司在招聘,煩請大佬考慮下,聯繫郵箱: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);
}
}
結語
- 經過上述代碼,能夠在項目中很方面使用Entity Framework;
- 而且很輕鬆實現CURD以及事務處理,從而開發中關注業務便可;
- 小弟不才,大佬輕拍;