update

2881099
2022-08-19 19:58:05 +08:00
parent d49a90a8a6
commit 2a67c97828
7 changed files with 124 additions and 314 deletions

115
DI-UnitOfWorkManager.md Normal file

@@ -0,0 +1,115 @@
本篇文章内容引导,如何在 asp.net core 项目中使用特性(注解) 的方式管理事务。
> UnitOfWorkManager 只可以管理 Repository 仓储对象的事务,直接 fsql.Insert\<T>() 是不行的!!但是可以用 repository.Orm.Insert\<T\>repository.Orm 是特殊实现的 IFreeSql与 当前事务保持一致。
支持六种传播方式(propagation),意味着跨方法的事务非常方便,并且支持同步异步:
- Requierd如果当前没有事务就新建一个事务如果已存在一个事务中加入到这个事务中默认的选择。
- Supports支持当前事务如果没有当前事务就以非事务方法执行。
- Mandatory使用当前事务如果没有当前事务就抛出异常。
- NotSupported以非事务方式执行操作如果当前存在事务就把当前事务挂起。
- Never以非事务方式执行操作如果当前事务存在则抛出异常。
- Nested以嵌套事务方式执行。
### 第一步:引入动态代理库
> dotnet add package Rougamo.Fody
```csharp
[AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : Rougamo.MoAttribute
{
public Propagation Propagation { get; set; } = Propagation.Required;
public IsolationLevel IsolationLevel { get => m_IsolationLevel.Value; set => m_IsolationLevel = value; }
IsolationLevel? m_IsolationLevel;
static AsyncLocal<IServiceProvider> m_ServiceProvider = new AsyncLocal<IServiceProvider>();
public static void SetServiceProvider(IServiceProvider serviceProvider) => m_ServiceProvider.Value = serviceProvider;
IUnitOfWork _uow;
public override void OnEntry(MethodContext context)
{
var uowManager = m_ServiceProvider.Value.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager;
_uow = uowManager.Begin(this.Propagation, this.m_IsolationLevel);
}
public override void OnExit(MethodContext context)
{
try
{
if (context.Exception == null) _uow.Commit();
else _uow.Rollback();
}
finally
{
_uow.Dispose();
}
}
}
```
| UnitOfWorkManager 成员 | 说明 |
| -- | -- |
| IUnitOfWork Current | 返回当前的工作单元 |
| void Binding(repository) | 将仓储的事务交给它管理 |
| IUnitOfWork Begin(propagation, isolationLevel) | 创建工作单元 |
### 第二步:配置 Startup.cs 注入、中间件
```csharp
//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFreeSql>(fsql);
services.AddScoped<UnitOfWorkManager>();
services.AddFreeRepository(null, typeof(Startup).Assembly);
//批量注入 Service
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
TransactionalAttribute.SetServiceProvider(context.RequestServices);
await next();
});
}
```
### 第三步:在 Controller 或者 Service 或者 Repository 中使用事务特性
```csharp
public class SongService
{
readonly IBaseRepository<Song> _repoSong;
readonly IBaseRepository<Detail> _repoDetail;
readonly SongRepository _repoSong2;
public SongService(IBaseRepository<Song> repoSong, IBaseRepository<Detail> repoDetail, SongRepository repoSong2)
{
_repoSong = repoSong;
_repoDetail = repoDetail;
_repoSong2 = repoSong2;
}
[Transactional]
public virtual void Test1()
{
//这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元
this.Test2();
}
[Transactional(Propagation = Propagation.Nested)]
public virtual void Test2() //嵌套事务,新的(不使用 Test1 的事务)
{
//这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元
}
}
```
是不是进方法就开事务呢?
不一定是真实事务,有可能是虚的,就是一个假的 unitofwork不带事务
也有可能是延用上一次的事务
也有可能是新开事务,具体要看传播模式

@@ -1,141 +0,0 @@
**中文** | [English](Unit-of-Work-Manager)
## ASP.NET Core下FreeSql的仓储事务
#### 第一步:配置 Startup.cs 注入
引入包
```bash
dotnet add package FreeSql
dotnet add package FreeSql.DbContext
dotnet add package FreeSql.Provider.MySqlConnector
```
配置 Startup.cs 注入
```csharp
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection Mysql = Configuration.GetSection("Mysql");
Fsql = new FreeSqlBuilder()
.UseConnectionString(DataType.MySql, Mysql.Value)
.UseAutoSyncStructure(true)
.UseNameConvert(NameConvertType.PascalCaseToUnderscoreWithLower)
.UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
.Build();
services.AddSingleton<IFreeSql>(fsql);
services.AddScoped<UnitOfWorkManager>();
services.AddFreeRepository(null, typeof(Startup).Assembly);
//新增自己的服务,这里只有实现
services.AddScoped<TransBlogService>();
}
```
- appsettings.json
```json
{
"Mysql": "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=ovov_freesql_repository;Charset=utf8;SslMode=none;Max pool size=10",
}
```
| UnitOfWorkManager 成员 | 说明 |
| -- | -- |
| IUnitOfWork Current | 返回当前的工作单元 |
| void Binding(repository) | 将仓储的事务交给它管理 |
| IUnitOfWork Begin(propagation, isolationLevel) | 创建工作单元 |
- TransBlogService.cs
```csharp
private readonly IBaseRepository<Blog, int> _blogRepository;
private readonly IBaseRepository<Tag, int> _tagRepository;
private readonly UnitOfWorkManager _unitOfWorkManager;
public TransBlogService(IBaseRepository<Blog, int> blogRepository, IBaseRepository<Tag, int> tagRepository,UnitOfWorkManager unitOfWorkManager)
{
_blogRepository = blogRepository ;
_tagRepository = tagRepository ;
_unitOfWorkManager = unitOfWorkManager;
}
public async Task CreateBlogUnitOfWorkAsync(Blog blog,List<Tag>tagList)
{
using (IUnitOfWork unitOfWork = _unitOfWorkManager.Begin())
{
try
{
await _blogRepository.InsertAsync(blog);
tagList.ForEach(r =>
{
r.PostId = blog.Id;
});
await _tagRepository.InsertAsync(tagList);
unitOfWork.Commit();
}
catch (Exception e)
{
//实际 可以不Rollback。因为IUnitOfWork内部Dispose会把没有Commit的事务Rollback回来但能提前Rollback
unitOfWork.Rollback();
//记录日志、或继续throw;出来
}
}
}
public async Task UpdateBlogAsync(int id)
{
using (IUnitOfWork unitOfWork = _unitOfWorkManager.Begin())
{
try
{
Blog blog = _blogRepository.Select.Where(r => r.Id == id).First();
blog.IsDeleted = true;
await _blogRepository.UpdateAsync(blog);
unitOfWork.Commit();
}
catch (Exception e)
{
//记录日志、或继续throw;出来
unitOfWork.Rollback();
}
}
}
```
| IUnitOfWork 成员 | 说明 |
| -- | -- |
| IFreeSql Orm | 该对象 Select/Delete/Insert/Update/InsertOrUpdate 与工作单元事务保持一致,可省略传递 WithTransaction |
| DbTransaction GetOrBeginTransaction() | 开启事务,或者返回已开启的事务 |
| void Commit() | 提交事务 |
| void Rollback()| 回滚事务 |
| DbContext.EntityChangeReport EntityChangeReport |工作单元内的实体变化跟踪 |
#### 完整的代码
- [Blog.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.Core/Domain/Blog.cs)
- [Tag.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.Core/Domain/Tag.cs)
- [TransBlogService.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.FreeSql.AutoFac.DynamicProxy/Services/TransBlogService.cs)
以上使用的是泛型仓储,那我们如果是重写一个仓储 如何保持和``UnitOfWorkManager``同一个事务呢。
继承现有的``DefaultRepository<,>``仓储,实现自定义的仓储``BlogRepository.cs``,
```csharp
public class BlogRepository : DefaultRepository<Blog, int>, IBlogRepository
{
public BlogRepository(UnitOfWorkManager uowm) : base(uowm?.Orm, uowm)
{
}
public List<Blog> GetBlogs()
{
return Select.Page(1, 10).ToList();
}
}
```
其中接口。``IBlogRepository.cs``
```csharp
public interface IBlogRepository : IBaseRepository<Blog, int>
{
List<Blog> GetBlogs();
}
```
在 startup.cs注入此服务
```csharp
services.AddScoped<IBlogRepository, BlogRepository>();
```

@@ -1,156 +0,0 @@
[中文](DI-UnitOfWorkManager%E4%BA%8B%E5%8A%A1) | **English**
## Use FreeSql's repository transaction in ASP.NET Core
#### Step 1: Configure Startup.cs
Install NuGet packages:
```bash
dotnet add package FreeSql
dotnet add package FreeSql.DbContext
dotnet add package FreeSql.Provider.MySqlConnector
```
Configure `Startup.cs`:
```csharp
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection Mysql = Configuration.GetSection("Mysql");
Fsql = new FreeSqlBuilder()
.UseConnectionString(DataType.MySql, Mysql.Value)
.UseAutoSyncStructure(true)
.UseNameConvert(NameConvertType.PascalCaseToUnderscoreWithLower)
.UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
.Build();
services.AddSingleton<IFreeSql>(fsql);
services.AddScoped<UnitOfWorkManager>();
services.AddFreeRepository(null, typeof(Startup).Assembly);
//Add your own service, here is only an implementation
services.AddScoped<TransBlogService>();
}
```
Update your `appsettings.json`:
```json
{
"Mysql": "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=ovov_freesql_repository;Charset=utf8;SslMode=none;Max pool size=10",
}
```
| UnitOfWorkManager Members | Description |
| -- | -- |
| IUnitOfWork Current | Return the current unit of work |
| void Binding(repository) | Hand over repository transaction to ir for management |
| IUnitOfWork Begin(propagation, isolationLevel) | Create unit of work |
- TransBlogService.cs
```csharp
private readonly IBaseRepository<Blog, int> _blogRepository;
private readonly IBaseRepository<Tag, int> _tagRepository;
private readonly UnitOfWorkManager _unitOfWorkManager;
public TransBlogService(IBaseRepository<Blog, int> blogRepository, IBaseRepository<Tag, int> tagRepository,UnitOfWorkManager unitOfWorkManager)
{
_blogRepository = blogRepository ;
_tagRepository = tagRepository ;
_unitOfWorkManager = unitOfWorkManager;
}
public async Task CreateBlogUnitOfWorkAsync(Blog blog,List<Tag>tagList)
{
using (IUnitOfWork unitOfWork = _unitOfWorkManager.Begin())
{
try
{
await _blogRepository.InsertAsync(blog);
tagList.ForEach(r =>
{
r.PostId = blog.Id;
});
await _tagRepository.InsertAsync(tagList);
unitOfWork.Commit();
}
catch (Exception e)
{
//Actually, Rollback may not be used.
//Because the internal Dispose of IUnitOfWork will roll back the transaction without Commit.
//But here can be Rollback in advance.
unitOfWork.Rollback();
//Log,
//or use throw to continue throwing exceptions upwards
}
}
}
public async Task UpdateBlogAsync(int id)
{
using (IUnitOfWork unitOfWork = _unitOfWorkManager.Begin())
{
try
{
Blog blog = _blogRepository.Select.Where(r => r.Id == id).First();
blog.IsDeleted = true;
await _blogRepository.UpdateAsync(blog);
unitOfWork.Commit();
}
catch (Exception e)
{
//Log,
//or use throw to continue throwing exceptions upwards
unitOfWork.Rollback();
}
}
}
```
| IUnitOfWork Members | Description |
| -- | -- |
| IFreeSql Orm | The object Select/Delete/Insert/Update/InsertOrUpdate is consistent with the unit of work transaction and can be omitted to pass WithTransaction |
| DbTransaction GetOrBeginTransaction() | Open the transaction, or return to the opened transaction |
| void Commit() | Commit transaction |
| void Rollback()| Rollback transaction |
| DbContext.EntityChangeReport EntityChangeReport |Entity change tracking within the unit of work |
#### Complete code
- [Blog.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.Core/Domain/Blog.cs)
- [Tag.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.Core/Domain/Tag.cs)
- [TransBlogService.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/ORM/FreeSql/OvOv.FreeSql.AutoFac.DynamicProxy/Services/TransBlogService.cs)
The above uses generic repository.
If you want to rewrite a repository, how do you keep the same transaction as `UnitOfWorkManager`? You can inherit the existing `DefaultRepository<,>` and implement a custom repository `BlogRepository.cs`:
```csharp
public class BlogRepository : DefaultRepository<Blog, int>, IBlogRepository
{
public BlogRepository(UnitOfWorkManager uowm) : base(uowm?.Orm, uowm)
{
}
public List<Blog> GetBlogs()
{
return Select.Page(1, 10).ToList();
}
}
```
The interface is `IBlogRepository.cs`:
```csharp
public interface IBlogRepository : IBaseRepository<Blog, int>
{
List<Blog> GetBlogs();
}
```
Inject this service in `startup.cs`
```csharp
services.AddScoped<IBlogRepository, BlogRepository>();
```

@@ -33,7 +33,7 @@ using (var uow = fsql.CreateUnitOfWork())
}
```
Reference: [Use TransactionalAttribute + UnitOfWorkManager in ASP.NET Core to achieve multiple transaction propagation](https://github.com/dotnetcore/FreeSql/issues/289)
Reference: [Use TransactionalAttribute + UnitOfWorkManager in ASP.NET Core to achieve multiple transaction propagation](DI-UnitOfWorkManager)
## Interface Definition

@@ -26,7 +26,7 @@
* [工作单元](%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83) [Unit of Work](Unit-of-Work)
* [联级保存](%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98) [Cascade Saving](Cascade-Saving)
* [联级删除](%E8%81%94%E7%BA%A7%E5%88%A0%E9%99%A4) [Cascade Deletion](Cascade-Deletion)
* [工作单元管理器](DI-UnitOfWorkManager事务) [UoW Manager](Unit-of-Work-Manager)
* [工作单元管理器](DI-UnitOfWorkManager)
* [DbContext](DbContext)
* [CodeFirst](CodeFirst)
* [实体特性✨](%e5%ae%9e%e4%bd%93%e7%89%b9%e6%80%a7)

@@ -1,6 +1,6 @@
本文所有内容基于单机数据库事务,分布式数据库 TCC/SAGA 方案请移步https://github.com/2881099/FreeSql.Cloud
## 0、[ASP.NET Core配置DI使用UnitOfWorkManager此方法更简单](DI-UnitOfWorkManager事务)
## 0、[ASP.NET Core配置DI使用UnitOfWorkManager此方法更简单](DI-UnitOfWorkManager)
## 1、UnitOfWork 事务
@@ -24,8 +24,6 @@ using (var uow = fsql.CreateUnitOfWork())
}
```
参考:[在 asp.net core 中使用 TransactionalAttribute + UnitOfWorkManager 实现多种事务传播](https://github.com/dotnetcore/FreeSql/issues/289)
## 2、DbContext 事务
```csharp

@@ -36,13 +36,7 @@ fsql.Aop.ConfigEntityProperty += (s, e) => {
---
### 4、TransactionalAttribute + UnitOfWorkManager 事务传播
[https://github.com/dotnetcore/FreeSql/issues/289](https://github.com/dotnetcore/FreeSql/issues/289)
---
### 5、怎么执行 SQL 返回实体列表?
### 4、怎么执行 SQL 返回实体列表?
```csharp
//直接查询
@@ -57,25 +51,25 @@ fsql.Select<T>().WithMemory(list).ToList();
---
### 6、错误【主库】状态不可用等待后台检查程序恢复方可使用。xxx
### 5、错误【主库】状态不可用等待后台检查程序恢复方可使用。xxx
[https://github.com/dotnetcore/FreeSql/discussions/1080](https://github.com/dotnetcore/FreeSql/discussions/1080)
---
### 7、错误:【主库】对象池已释放,无法访问。
### 6、错误:【主库】对象池已释放,无法访问。
[https://github.com/dotnetcore/FreeSql/discussions/1079](https://github.com/dotnetcore/FreeSql/discussions/1079)
---
### 8、错误ObjectPool.Get 获取超时10秒
### 7、错误ObjectPool.Get 获取超时10秒
[https://github.com/dotnetcore/FreeSql/discussions/1081](https://github.com/dotnetcore/FreeSql/discussions/1081)
---
### 9、多平台代码参考,使用自定义SqliteProvider,例如Sqlite用Microsoft.Data.Sqlite或者反射Mono.Data.Sqlite.
### 8、多平台代码参考,使用自定义SqliteProvider,例如Sqlite用Microsoft.Data.Sqlite或者反射Mono.Data.Sqlite.
[arm/树莓派](https://github.com/densen2014/FreeSqlDemos/tree/master/ARM_ConsoleApp)
@@ -131,7 +125,7 @@ if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.And
---
### 10、 2.6.100升级到3.0.100 后无法连接 sqlserver 提示证书无效, 提示证书链是由不受信任的颁发机构颁发的.
### 9、 2.6.100升级到3.0.100 后无法连接 sqlserver 提示证书无效, 提示证书链是由不受信任的颁发机构颁发的.
请尝试: