源代码
3.3.1. 工作单元
IUnitOfWorkManager 和 UnitOfWorkManager
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace EntityFramework.UnitOfWorks
{
public interface IUnitOfWorkManager
{
IUnitOfWorkHandle<TDbContext> Begin<TDbContext>() where TDbContext : DbContext;
IUnitOfWorkHandle<TDbContext> BeginNew<TDbContext>() where TDbContext : DbContext;
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace EntityFramework.UnitOfWorks.Impl
{
public class UnitOfWorkManager : IUnitOfWorkManager
{
private readonly IServiceProvider _serviceProvider;
public UnitOfWorkManager(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IUnitOfWorkHandle<TDbContext> Begin<TDbContext>() where TDbContext : DbContext
{
var uowHander = AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current;
if (uowHander == null)
{
return BeginNew<TDbContext>();
}
else
{
return new InnerUnitOfWorkHandle<TDbContext>();
}
}
public IUnitOfWorkHandle<TDbContext> BeginNew<TDbContext>() where TDbContext : DbContext
{
IServiceScope serviceScope = _serviceProvider.CreateScope();
var uowHander = new UnitOfWorkHandle<TDbContext>(serviceScope);
AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current = uowHander;
return uowHander;
}
}
}
IUnitOfWorkHandle<TDbContext>, UnitOfWorkHandle<TDbContext> 和 InnerUnitOfWorkHandle<TDbContext>
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace EntityFramework.UnitOfWorks
{
public interface IUnitOfWorkHandle<TDbContext> : IDisposable where TDbContext:DbContext
{
TDbContext GetActiveUnitOfWork();
int SaveChange();
Task<int> SaveChangeAsync();
bool IsDisposed { get; }
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace EntityFramework.UnitOfWorks.Impl
{
public class UnitOfWorkHandle<TDbContext> : IUnitOfWorkHandle<TDbContext> where TDbContext : DbContext
{
private readonly IServiceScope _serviceScope;
public bool IsDisposed { get; private set; }
public UnitOfWorkHandle(IServiceScope serviceScope)
{
_serviceScope = serviceScope;
}
public TDbContext GetActiveUnitOfWork()
{
return _serviceScope.ServiceProvider.GetRequiredService<TDbContext>();
}
public void Dispose()
{
_serviceScope.Dispose();
IsDisposed = true;
// 清空当前 Handle 或回到 OuterHandle
AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current = null;
}
public int SaveChange()
{
var dbContext = GetActiveUnitOfWork();
if (dbContext == null)
{
return 0;
}
return dbContext.SaveChanges();
}
public async Task<int> SaveChangeAsync()
{
var dbContext = GetActiveUnitOfWork();
if (dbContext == null)
{
return await Task.FromResult(0);
}
return await dbContext.SaveChangesAsync(CancellationToken.None);
}
}
}
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Nuctech.TrDevice.EntityFramework.UnitOfWorks.Impl
{
public class InnerUnitOfWorkHandle<TDbContext> : IUnitOfWorkHandle<TDbContext> where TDbContext : DbContext
{
public bool IsDisposed { get; private set; }
public void Dispose()
{
IsDisposed = true;
}
public TDbContext GetActiveUnitOfWork()
=> AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current?.GetActiveUnitOfWork();
public int SaveChange()
{
return 0;
}
public Task<int> SaveChangeAsync()
{
return Task.FromResult(0);
}
}
}
AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext> 和 LocalUowWrapper<TDbContext>
由于这两个类是强关联的,所以这里将LocalUowWrapper<TDbContext> 定义为其内部类 LocalUowWrapper
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace EntityFramework.UnitOfWorks.Impl
{
public class AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext> where TDbContext : DbContext
{
private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>();
public static UnitOfWorkHandle<TDbContext> Current
{
get { return GetCurrentUow(); }
set { SetCurrentUow(value); }
}
private static UnitOfWorkHandle<TDbContext> GetCurrentUow()
{
var uow = AsyncLocalUow.Value?.UnitOfWorkHandle;
if (uow == null)
{
return null;
}
if (uow.IsDisposed)
{
AsyncLocalUow.Value = null;
return null;
}
return uow;
}
private static void SetCurrentUow(UnitOfWorkHandle<TDbContext> value)
{
lock (AsyncLocalUow)
{
if (value == null)
{
if (AsyncLocalUow.Value == null)
{
return;
}
if (AsyncLocalUow.Value.Outer == null)
{
AsyncLocalUow.Value.UnitOfWorkHandle = null;
AsyncLocalUow.Value = null;
return;
}
var oldValue = AsyncLocalUow.Value;
AsyncLocalUow.Value = AsyncLocalUow.Value.Outer;
oldValue.Outer = null;
}
else
{
if (AsyncLocalUow.Value?.UnitOfWorkHandle == null)
{
if (AsyncLocalUow.Value != null)
{
AsyncLocalUow.Value.UnitOfWorkHandle = value;
}
AsyncLocalUow.Value = new LocalUowWrapper(value);
return;
}
var newValue = new LocalUowWrapper(value) { Outer = AsyncLocalUow.Value };
AsyncLocalUow.Value = newValue;
}
}
}
private class LocalUowWrapper
{
public UnitOfWorkHandle<TDbContext> UnitOfWorkHandle { get; set; }
public LocalUowWrapper Outer { get; set; }
public LocalUowWrapper(UnitOfWorkHandle<TDbContext> unitOfWorkHandle)
{
UnitOfWorkHandle = unitOfWorkHandle;
}
}
}
}
3.3.2. 单元测试
以下单元测试基本涵盖了常见的工作单元使用情况;
事务时直接使用 DbContext 启动的事务,更复杂的情况请查看官方文档;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using EntityFramework.UnitOfWorks;
using EntityFramework.UnitOfWorks.Impl;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace EntityFramework.UnitOfWorkTest
{
public class UnitOfWorkTests
{
public const string ConnectString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TestDatabase";
private readonly IServiceProvider _serviceProvider;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public UnitOfWorkTests()
{
IServiceCollection services = new ServiceCollection();
services.AddDbContext<PersionDbContext>(options => options.UseSqlServer(ConnectString));
services.AddTransient<IUnitOfWorkManager, UnitOfWorkManager>();
_serviceProvider = services.BuildServiceProvider();
_unitOfWorkManager = _serviceProvider.GetRequiredService<IUnitOfWorkManager>();
}
/// <summary>
/// 正常操作
/// </summary>
/// <returns></returns>
[Fact]
public async Task ShouldNormalOperation()
{
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
var lists = uowHandle.GetActiveUnitOfWork().Persions.ToList();
//清理
lists.ForEach(u => uowHandle.GetActiveUnitOfWork().Persions.Remove(u));
uowHandle.SaveChange();
}
await AddUser("张三");
await AddUser("李四");
await AddUser("王五");
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
var lists = uowHandle.GetActiveUnitOfWork().Persions.ToList();
lists.Count.ShouldBe(3);
//清理
lists.ForEach(u => uowHandle.GetActiveUnitOfWork().Persions.Remove(u));
uowHandle.SaveChange();
}
async Task AddUser(string name)
{
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
uowHandle.GetActiveUnitOfWork().Persions.Add(new Persion() { Name = name });
await uowHandle.SaveChangeAsync();
}
}
}
/// <summary>
/// 超出使用范围使用工作单元
/// </summary>
[Fact]
public void ShouldNotUseUow()
{
var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>();
uowHandle.Dispose();
Assert.Throws<ObjectDisposedException>(() => uowHandle.GetActiveUnitOfWork().Persions.ToList());
}
/// <summary>
/// 工作单元嵌套时,当前工作单IUnitOfWorkHandle和DbContext的实际情况
/// </summary>
[Fact]
public void ShouldAcrossMutiFunction()
{
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
var outerDbContext = uowHandle.GetActiveUnitOfWork();
uowHandle.ShouldBeOfType<UnitOfWorkHandle<PersionDbContext>>();
using (var innerUowHandle = _unitOfWorkManager.BeginNew<PersionDbContext>())
{
var innerDbContext = innerUowHandle.GetActiveUnitOfWork();
innerUowHandle.GetActiveUnitOfWork().ShouldNotBe(outerDbContext);
innerUowHandle.ShouldBeOfType<UnitOfWorkHandle<PersionDbContext>>();
innerUowHandle.ShouldNotBe(uowHandle);
using (var innerInnerUowHandle = _unitOfWorkManager.BeginNew<PersionDbContext>())
{
innerInnerUowHandle.ShouldBeOfType<UnitOfWorkHandle<PersionDbContext>>();
innerInnerUowHandle.GetActiveUnitOfWork().ShouldNotBe(outerDbContext);
innerInnerUowHandle.ShouldNotBe(uowHandle);
}
innerUowHandle.GetActiveUnitOfWork().ShouldBe(innerDbContext);
}
using (var innerUowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
innerUowHandle.ShouldBeOfType<InnerUnitOfWorkHandle<PersionDbContext>>();
innerUowHandle.GetActiveUnitOfWork().ShouldBe(outerDbContext);
}
uowHandle.GetActiveUnitOfWork().ShouldBe(outerDbContext);
}
}
/// <summary>
/// 使用数据库事务
/// </summary>
/// <param name="isCommit">是否提交数据</param>
/// <returns></returns>
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ShouldCommitTransaction(bool isCommit)
{
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
var lists = uowHandle.GetActiveUnitOfWork().Persions.ToList();
lists.ForEach(u => uowHandle.GetActiveUnitOfWork().Persions.Remove(u));
uowHandle.SaveChange();
}
List<string> names = new List<string> { "张三", "李四", "王老五" };
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
using (var transaction = uowHandle.GetActiveUnitOfWork().Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
{
for (int i = 0; i < names.Count; i++)
{
uowHandle.GetActiveUnitOfWork().Persions.Add(new Persion() { Name = names[i] });
//事务期间的SaveChange不会提交到数据库
await uowHandle.SaveChangeAsync();
}
if (isCommit)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
using (var uowHandle = _unitOfWorkManager.Begin<PersionDbContext>())
{
uowHandle.GetActiveUnitOfWork().Persions.Count().ShouldBe(isCommit ? 3 : 0);
}
}
}
}
4. 封装-仓储
4.1. 分析
关于仓储类的封装主要是为了更方便的使用工作单元;
面向我们经常使用的操作: Select, Insert, Update, Delete 四个方向进行封装。
4.2. 设计
4.2.1. 类图
IRepository<TDbContext, TEntity>
+IUnitOfWorkManager UnitOfWorkManager+TDbContext CurrentDbContext
+BeginUnitOfWork() : IUnitOfWorkHandle<TDbContext>+BeginNewUnitOfWork() : IUnitOfWorkHandle<TDbContext>+Insert(TEntity entity) : TEntity+GetAll() : IQueryable<TEntity>+GetAllList() : List<TEntity>+Update(TEntity entity) : TEntity+Delete(TEntity entity)
EFRepository<TDbContext, TEntity>
AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>
IUnitOfWorkManager
IUnitOfWorkHandle
4.2.2. 时序图
EFRepository
AsyncLocalCurrentUnitOfWorkHandleProvider
IUnitOfWorkManager
IUnitOfWorkHandle
比如返回IQueryable的方法
获取当前生效的 DbContext
使用此上下文执行操作
抛出 ArgumentNullException 异常
alt
[存在]
[不存在]
Begin()一个工作单元
创建的工作单元处理器类型受外部影响,前面已介绍
Begin()一个工作单元
获取生效的DbContext并进行操作
释放资源: Dispose
par
[执行后需要DbContext继续生效的方法]
[执行后DbContext不生效也没影响的方法]
EFRepository
AsyncLocalCurrentUnitOfWorkHandleProvider
IUnitOfWorkManager
IUnitOfWorkHandle
4.2.3. 源码
IRepository<TDbContext, TEntity>
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using EntityFramework.UnitOfWorks;
namespace EntityFramework.Repositories
public interface IRepository<TDbContext, TEntity> where TEntity : class where TDbContext : DbContext
{
IUnitOfWorkManager UnitOfWorkManager { get; }
TDbContext CurrentDbContext { get; }
IUnitOfWorkHandle<TDbContext> BeginUnitOfWork();
IUnitOfWorkHandle<TDbContext> BeginNewUnitOfWork();
#region Select/Get/Query
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);
List<TEntity> GetAllList();
Task<List<TEntity>> GetAllListAsync();
List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);
T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);
TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
#endregion
TEntity Insert(TEntity entity);
Task<TEntity> InsertAsync(TEntity entity);
TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity);
void Delete(TEntity entity);
Task DeleteAsync(TEntity entity);
}
}
EFRepository<TDbContext, TEntity>
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using EntityFramework.UnitOfWorks;
using EntityFramework.UnitOfWorks.Impl;
namespace EntityFramework.Repositories.Impl
{
public class EFRepository<TDbContext, TEntity> : IRepository<TDbContext, TEntity> where TEntity : class where TDbContext : DbContext
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public EFRepository(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
}
public virtual TDbContext CurrentDbContext => AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current?.GetActiveUnitOfWork();
public IUnitOfWorkManager UnitOfWorkManager => _unitOfWorkManager;
public IUnitOfWorkHandle<TDbContext> BeginUnitOfWork()
{
return _unitOfWorkManager.Begin<TDbContext>();
}
public IUnitOfWorkHandle<TDbContext> BeginNewUnitOfWork()
{
return _unitOfWorkManager.BeginNew<TDbContext>();
}
public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return uowHander.GetActiveUnitOfWork().Set<TEntity>().FirstOrDefault(predicate);
}
public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return await uowHander.GetActiveUnitOfWork().Set<TEntity>().FirstOrDefaultAsync(predicate);
}
public IQueryable<TEntity> GetAll()
{
var context = CurrentDbContext;
if (context != null)
{
return context.Set<TEntity>().AsQueryable();
}
throw new ArgumentNullException(nameof(CurrentDbContext));
}
public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
{
var context = CurrentDbContext;
if (context != null)
{
var query = context.Set<TEntity>().AsQueryable();
if (propertySelectors != null)
{
foreach (var propertySelector in propertySelectors)
{
query = query.Include(propertySelector);
}
}
return query;
}
throw new ArgumentNullException(nameof(CurrentDbContext));
}
public List<TEntity> GetAllList()
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return uowHander.GetActiveUnitOfWork().Set<TEntity>().ToList();
}
public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return uowHander.GetActiveUnitOfWork().Set<TEntity>().Where(predicate).ToList();
}
public async Task<List<TEntity>> GetAllListAsync()
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return await uowHander.GetActiveUnitOfWork().Set<TEntity>().ToListAsync();
}
public async Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return await uowHander.GetActiveUnitOfWork().Set<TEntity>().Where(predicate).ToListAsync();
}
public T Query<T>(Func<IQueryable<TEntity>, T> queryMethod)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return queryMethod(GetAll());
}
public TEntity Single(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return uowHander.GetActiveUnitOfWork().Set<TEntity>().SingleOrDefault(predicate);
}
public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
return await uowHander.GetActiveUnitOfWork().Set<TEntity>().SingleOrDefaultAsync(predicate);
}
public TEntity Insert(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
entity = uowHander.GetActiveUnitOfWork().Set<TEntity>().Add(entity).Entity;
uowHander.SaveChange();
return entity;
}
public async Task<TEntity> InsertAsync(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
entity = uowHander.GetActiveUnitOfWork().Set<TEntity>().Add(entity).Entity;
await uowHander.SaveChangeAsync();
return entity;
}
public TEntity Update(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
var context = uowHander.GetActiveUnitOfWork();
AttachIfNot(context, entity);
context.Entry(entity).State = EntityState.Modified;
uowHander.SaveChange();
return entity;
}
public async Task<TEntity> UpdateAsync(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
var context = uowHander.GetActiveUnitOfWork();
AttachIfNot(context, entity);
context.Entry(entity).State = EntityState.Modified;
await uowHander.SaveChangeAsync();
return entity;
}
public void Delete(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
var context = uowHander.GetActiveUnitOfWork();
AttachIfNot(context, entity);
context.Set<TEntity>().Remove(entity);
uowHander.SaveChange();
}
public async Task DeleteAsync(TEntity entity)
{
using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
var context = uowHander.GetActiveUnitOfWork();
AttachIfNot(context, entity);
context.Set<TEntity>().Remove(entity);
await uowHander.SaveChangeAsync();
}
protected virtual void AttachIfNot(DbContext dbContext, TEntity entity)
{
var entry = dbContext.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity == entity);
if (entry != null)
{
return;
}
dbContext.Set<TEntity>().Attach(entity);
}
}
}
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com