update demo

This commit is contained in:
2881099 2022-08-19 20:42:33 +08:00
parent c51cffc2c2
commit 3339d96117
10 changed files with 131 additions and 88 deletions

View File

@ -3,6 +3,7 @@ using FreeSql.DataAnnotations;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -24,20 +25,21 @@ namespace aspnetcore_transaction.Controllers
[HttpGet("1")] [HttpGet("1")]
//[Transactional] //[Transactional]
virtual public object Get([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2, async public Task<object> Get([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2,
[FromServices] SongService serviceSong) [FromServices] SongService serviceSong)
{ {
//repoSong.Insert(new Song()); //repoSong.Insert(new Song());
//repoDetail.Insert(new Detail()); //repoDetail.Insert(new Detail());
//repoSong2.Insert(new Song()); //repoSong2.Insert(new Song());
serviceSong.Test1(); //serviceSong.Test1();
await serviceSong.Test11();
return "111"; return "111";
} }
[HttpGet("2")] [HttpGet("2")]
//[Transactional] //[Transactional]
async virtual public Task<object> GetAsync([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2, async public Task<object> GetAsync([FromServices] BaseRepository<Song> repoSong, [FromServices] BaseRepository<Detail> repoDetail, [FromServices] SongRepository repoSong2,
[FromServices] SongService serviceSong) [FromServices] SongService serviceSong)
{ {
await serviceSong.Test2(); await serviceSong.Test2();
@ -61,15 +63,21 @@ namespace aspnetcore_transaction.Controllers
} }
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的 [Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
public virtual void Test1() public void Test1()
{ {
_repoSong.Insert(new Song()); _repoSong.Insert(new Song());
_repoDetail.Insert(new Detail()); _repoDetail.Insert(new Detail());
_repoSong2.Insert(new Song()); _repoSong2.Insert(new Song());
} }
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
async public Task Test11()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(t =>
_repoSong.InsertAsync(new Song()));
}
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的 [Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
async public virtual Task Test2() async public Task Test2()
{ {
await _repoSong.InsertAsync(new Song()); await _repoSong.InsertAsync(new Song());
await _repoDetail.InsertAsync(new Detail()); await _repoDetail.InsertAsync(new Detail());
@ -77,7 +85,7 @@ namespace aspnetcore_transaction.Controllers
} }
[Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的 [Transactional(Propagation = Propagation.Nested)] //sqlite 不能嵌套事务,会锁库的
async public virtual Task<object> Test3() async public Task<object> Test3()
{ {
await _repoSong.InsertAsync(new Song()); await _repoSong.InsertAsync(new Song());
await _repoDetail.InsertAsync(new Detail()); await _repoDetail.InsertAsync(new Detail());
@ -110,53 +118,4 @@ namespace aspnetcore_transaction.Controllers
public int SongId { get; set; } public int SongId { get; set; }
public string Title { get; set; } public string Title { get; set; }
} }
public static class IdleBusExtesions
{
static AsyncLocal<string> AsyncLocalTenantId = new AsyncLocal<string>();
public static IdleBus<IFreeSql> ChangeTenant(this IdleBus<IFreeSql> ib, string tenantId)
{
AsyncLocalTenantId.Value = tenantId;
return ib;
}
public static IFreeSql Get(this IdleBus<IFreeSql> ib) => ib.Get(AsyncLocalTenantId.Value ?? "default");
public static IBaseRepository<T> GetRepository<T>(this IdleBus<IFreeSql> ib) where T : class => ib.Get().GetRepository<T>();
static void test()
{
IdleBus<IFreeSql> ib = null; //单例注入
var fsql = ib.Get(); //获取当前租户对应的 IFreeSql
var fsql00102 = ib.ChangeTenant("00102").Get(); //切换租户,后面的操作都是针对 00102
var songRepository = ib.GetRepository<Song>();
var detailRepository = ib.GetRepository<Detail>();
}
public static IServiceCollection AddRepository(this IServiceCollection services, params Assembly[] assemblies)
{
services.AddScoped(typeof(IBaseRepository<>), typeof(YourDefaultRepository<>));
services.AddScoped(typeof(BaseRepository<>), typeof(YourDefaultRepository<>));
services.AddScoped(typeof(IBaseRepository<,>), typeof(YourDefaultRepository<,>));
services.AddScoped(typeof(BaseRepository<,>), typeof(YourDefaultRepository<,>));
if (assemblies?.Any() == true)
foreach (var asse in assemblies)
foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IBaseRepository).IsAssignableFrom(a)))
services.AddScoped(repo);
return services;
}
}
class YourDefaultRepository<T> : BaseRepository<T> where T : class
{
public YourDefaultRepository(IdleBus<IFreeSql> ib) : base(ib.Get(), null, null) { }
}
class YourDefaultRepository<T, TKey> : BaseRepository<T, TKey> where T : class
{
public YourDefaultRepository(IdleBus<IFreeSql> ib) : base(ib.Get(), null, null) { }
}
} }

View File

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Rougamo />
</Weavers>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Rougamo" minOccurs="0" maxOccurs="1" type="xs:anyType" />
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,6 +22,7 @@ namespace aspnetcore_transaction
{ {
webBuilder.UseStartup<Startup>(); webBuilder.UseStartup<Startup>();
}) })
.UseServiceProviderFactory(new FreeSql.DynamicProxyServiceProviderFactory()); //.UseServiceProviderFactory(new FreeSql.DynamicProxyServiceProviderFactory())
;
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -52,6 +52,12 @@ namespace aspnetcore_transaction
Console.OutputEncoding = Encoding.GetEncoding("GB2312"); Console.OutputEncoding = Encoding.GetEncoding("GB2312");
Console.InputEncoding = Encoding.GetEncoding("GB2312"); Console.InputEncoding = Encoding.GetEncoding("GB2312");
app.Use(async (context, next) =>
{
TransactionalAttribute.SetServiceProvider(context.RequestServices);
await next();
});
app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" }); app.UseHttpMethodOverride(new HttpMethodOverrideOptions { FormFieldName = "X-Http-Method-Override" });
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseRouting(); app.UseRouting();

View File

@ -1,54 +1,43 @@
using FreeSql; using FreeSql;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Rougamo.Context;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FreeSql namespace FreeSql
{ {
/// <summary>
/// 使用事务执行,请查看 Program.cs 代码开启动态代理
/// </summary>
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)]
public class TransactionalAttribute : DynamicProxyAttribute, IActionFilter public class TransactionalAttribute : Rougamo.MoAttribute
{ {
public Propagation Propagation { get; set; } = Propagation.Required; public Propagation Propagation { get; set; } = Propagation.Required;
public IsolationLevel IsolationLevel { get => _IsolationLevelPriv.Value; set => _IsolationLevelPriv = value; } public IsolationLevel IsolationLevel { get => m_IsolationLevel.Value; set => m_IsolationLevel = value; }
IsolationLevel? _IsolationLevelPriv; IsolationLevel? m_IsolationLevel;
static AsyncLocal<IServiceProvider> m_ServiceProvider = new AsyncLocal<IServiceProvider>();
public static void SetServiceProvider(IServiceProvider serviceProvider) =>
m_ServiceProvider.Value = serviceProvider;
[DynamicProxyFromServices]
#pragma warning disable IDE0044 // 添加只读修饰符
UnitOfWorkManager _uowManager;
#pragma warning restore IDE0044 // 添加只读修饰符
IUnitOfWork _uow; IUnitOfWork _uow;
public override void OnEntry(MethodContext context)
public override Task Before(DynamicProxyBeforeArguments args) => OnBefore(_uowManager);
public override Task After(DynamicProxyAfterArguments args) => OnAfter(args.Exception);
//这里是为了 controller
public void OnActionExecuting(ActionExecutingContext context) => OnBefore(context.HttpContext.RequestServices.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager);
public void OnActionExecuted(ActionExecutedContext context) => OnAfter(context.Exception);
Task OnBefore(UnitOfWorkManager uowm)
{ {
_uow = uowm.Begin(this.Propagation, this._IsolationLevelPriv); var uowManager = m_ServiceProvider.Value.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager;
return Task.FromResult(false); _uow = uowManager.Begin(this.Propagation, this.m_IsolationLevel);
} }
Task OnAfter(Exception ex) public override void OnExit(MethodContext context)
{ {
try try
{ {
if (ex == null) _uow.Commit(); if (context.Exception == null) _uow.Commit();
else _uow.Rollback(); else _uow.Rollback();
} }
finally finally
{ {
_uow.Dispose(); _uow.Dispose();
} }
return Task.FromResult(false);
} }
} }
} }

View File

@ -0,0 +1,54 @@
//using FreeSql;
//using Microsoft.AspNetCore.Mvc.Filters;
//using System;
//using System.Collections.Generic;
//using System.Data;
//using System.Text;
//using System.Threading.Tasks;
//namespace FreeSql
//{
// /// <summary>
// /// 使用事务执行,请查看 Program.cs 代码开启动态代理
// /// </summary>
// [AttributeUsage(AttributeTargets.Method)]
// public class TransactionalAttribute : DynamicProxyAttribute, IActionFilter
// {
// public Propagation Propagation { get; set; } = Propagation.Required;
// public IsolationLevel IsolationLevel { get => _IsolationLevelPriv.Value; set => _IsolationLevelPriv = value; }
// IsolationLevel? _IsolationLevelPriv;
// [DynamicProxyFromServices]
//#pragma warning disable IDE0044 // 添加只读修饰符
// UnitOfWorkManager _uowManager;
//#pragma warning restore IDE0044 // 添加只读修饰符
// IUnitOfWork _uow;
// public override Task Before(DynamicProxyBeforeArguments args) => OnBefore(_uowManager);
// public override Task After(DynamicProxyAfterArguments args) => OnAfter(args.Exception);
// //这里是为了 controller
// public void OnActionExecuting(ActionExecutingContext context) => OnBefore(context.HttpContext.RequestServices.GetService(typeof(UnitOfWorkManager)) as UnitOfWorkManager);
// public void OnActionExecuted(ActionExecutedContext context) => OnAfter(context.Exception);
// Task OnBefore(UnitOfWorkManager uowm)
// {
// _uow = uowm.Begin(this.Propagation, this._IsolationLevelPriv);
// return Task.FromResult(false);
// }
// Task OnAfter(Exception ex)
// {
// try
// {
// if (ex == null) _uow.Commit();
// else _uow.Rollback();
// }
// finally
// {
// _uow.Dispose();
// }
// return Task.FromResult(false);
// }
// }
//}

View File

@ -11,6 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Rougamo.Fody" Version="1.1.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
<PackageReference Include="FreeSql.DynamicProxy" Version="1.5.0" /> <PackageReference Include="FreeSql.DynamicProxy" Version="1.5.0" />
<PackageReference Include="IdleBus" Version="1.5.2" /> <PackageReference Include="IdleBus" Version="1.5.2" />

View File

@ -9,10 +9,5 @@
自增 自增
</summary> </summary>
</member> </member>
<member name="T:FreeSql.TransactionalAttribute">
<summary>
使用事务执行,请查看 Program.cs 代码开启动态代理
</summary>
</member>
</members> </members>
</doc> </doc>

View File

@ -800,5 +800,14 @@
<param name="that"></param> <param name="that"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
<summary>
批量注入 Repository可以参考代码自行调整
</summary>
<param name="services"></param>
<param name="globalDataFilter"></param>
<param name="assemblies"></param>
<returns></returns>
</member>
</members> </members>
</doc> </doc>