Merge branch 'ShardingNavigations'

# Conflicts:
#	nuget-publish.bat
#	src/ShardingCore/Extensions/DbContextExtensions/DbContextExtensionShardingCore.cs
This commit is contained in:
xuejiaming 2023-02-10 15:13:05 +08:00
commit 422e78c0d1
57 changed files with 889 additions and 3881 deletions

View File

@ -55,7 +55,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.ShardingConsole", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.OracleIssue", "samples\Sample.OracleIssue\Sample.OracleIssue.csproj", "{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.Test6x", "test\ShardingCore.Test6x\ShardingCore.Test6x.csproj", "{8ED3A191-5A29-4599-B383-9FD225CC0BA2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test6x", "test\ShardingCore.Test6x\ShardingCore.Test6x.csproj", "{8ED3A191-5A29-4599-B383-9FD225CC0BA2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src6", "src6", "{585A790B-8B19-430F-BEB7-9F7D1A3AAFAA}"
EndProject

View File

@ -1,10 +1,10 @@
:start
::定义版本
set EFCORE7=7.7.0.12
set EFCORE6=7.6.0.12
set EFCORE5=7.5.0.12
set EFCORE3=7.3.0.12
set EFCORE2=7.2.0.12
set EFCORE7=7.7.1.1-preview1
set EFCORE6=7.6.1.1-preview1
set EFCORE5=7.5.1.1-preview1
set EFCORE3=7.3.1.1-preview1
set EFCORE2=7.2.1.1-preview1
::删除所有bin与obj下的文件
@echo off

View File

@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Extensions;
using ShardingCore.Extensions.ShardingQueryableExtensions;
namespace Sample.AutoCreateIfPresent.Controllers;

View File

@ -26,7 +26,6 @@ namespace Sample.AutoCreateIfPresent
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new OrderByHourMap());
modelBuilder.ApplyConfiguration(new AreaDeviceMap());
Console.WriteLine(this.IsExecutor);
}
public IRouteTail RouteTail { get; set; }

View File

@ -44,7 +44,7 @@ public static class MyShardingExtension
new Dictionary<DbContext, IEnumerable<TEntity>>()
{
{
shardingDbContext.CreateGenericDbContext(entitiesArray[0]),
shardingDbContext.GetShardingExecutor().CreateGenericDbContext(entitiesArray[0]),
entitiesArray
}
}

View File

@ -25,6 +25,7 @@ namespace Sample.BulkConsole
});
static void Main(string[] args)
{
var s = Guid.NewGuid().ToString("n");
var services = new ServiceCollection();
services.AddLogging();

View File

@ -23,7 +23,7 @@ public class DbSetDiscoverExpressionVisitor<TEntity>:ExpressionVisitor where TEn
var dbContextDependencies =
typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as IDbContextDependencies;
var targetIQ =
((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryRootExpression.EntityType.ClrType);
((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryRootExpression.ElementType);
DbSet = (DbSet<TEntity>)targetIQ;
}
return base.VisitExtension(node);

View File

@ -9,6 +9,7 @@ using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources.PhysicDataSources;
using ShardingCore.Core.VirtualRoutes.TableRoutes;
using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions;
using ShardingCore.Extensions.ShardingPageExtensions;
using ShardingCore.Extensions.ShardingQueryableExtensions;
using ShardingCore.Helpers;
@ -61,30 +62,31 @@ namespace Sample.MySql.Controllers
public IQueryable<SysTest> GetAll()
{
var shardingRouteManager = _shardingRuntimeContext.GetShardingRouteManager();
// var shardingTableCreator = _shardingRuntimeContext.GetShardingTableCreator();
// var tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
// //系统的时间分片都会实现 ITailAppendable 如果不是系统的自定义的转成你自己的对象即可
// var virtualTableRoute = (ITailAppendable)tableRouteManager.GetRoute(typeof(SysUserMod));
// //一定要先在路由里面添加尾巴
// virtualTableRoute.Append("20220921");
// shardingTableCreator.CreateTable<SysUserMod>("ds0","20220921");
var shardingTableCreator = _shardingRuntimeContext.GetShardingTableCreator();
var tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
//系统的时间分片都会实现 ITailAppendable 如果不是系统的自定义的转成你自己的对象即可
var virtualTableRoute = (ITailAppendable)tableRouteManager.GetRoute(typeof(SysUserMod));
//一定要先在路由里面添加尾巴
virtualTableRoute.Append("20220921");
shardingTableCreator.CreateTable<SysUserMod>("ds0","20220921");
return _defaultTableDbContext.Set<SysTest>();
}
[HttpGet]
public async Task<IActionResult> Get()
{
var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource();
virtualDataSource.AddPhysicDataSource(new DefaultPhysicDataSource("2023", "xxxxxxxx", false));
var dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
var virtualDataSourceRoute = dataSourceRouteManager.GetRoute(typeof(SysUserMod));
virtualDataSourceRoute.AddDataSourceName("2023");
var dataSourceInitializer = _shardingRuntimeContext.GetDataSourceInitializer();
dataSourceInitializer.InitConfigure("2023",true,true);
using (var dbContextTransaction = _defaultTableDbContext.Database.BeginTransaction())
{
}
var s = Guid.NewGuid().ToString();
var page =await _defaultTableDbContext.Set<SysUserLogByMonth>().Where(o=>o.Id==s).OrderByDescending(o=>o.Time).ToShardingPageAsync(1,2);
// var virtualDataSource = _shardingRuntimeContext.GetVirtualDataSource();
// virtualDataSource.AddPhysicDataSource(new DefaultPhysicDataSource("2023", "xxxxxxxx", false));
// var dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
// var virtualDataSourceRoute = dataSourceRouteManager.GetRoute(typeof(SysUserMod));
// virtualDataSourceRoute.AddDataSourceName("2023");
// var dataSourceInitializer = _shardingRuntimeContext.GetDataSourceInitializer();
// dataSourceInitializer.InitConfigure("2023",true,true);
// using (var dbContextTransaction = _defaultTableDbContext.Database.BeginTransaction())
// {
//
// }
var x2 = await (from ut in _defaultTableDbContext.Set<SysTest>()
join uu in _defaultTableDbContext.Set<SysUserLogByMonth>()
@ -244,5 +246,27 @@ namespace Sample.MySql.Controllers
// var sysUserMods2 = await _defaultTableDbContext.Set<SysTest>().FromSqlRaw("select * from SysTest where id='2'").ToListAsync();
return Ok();
}
[HttpGet]
public async Task<IActionResult> Get6()
{
var sysUserMod = await _defaultTableDbContext.Set<SysUserMod>().FirstOrDefaultAsync();
sysUserMod.Age = new Random().Next(1,999);
_defaultTableDbContext.Update(sysUserMod);
_defaultTableDbContext.SaveChanges();
// var sysUserMods1 = await _defaultTableDbContext.Set<SysUserMod>().FromSqlRaw("select * from SysUserMod where id='2'").ToListAsync();
// var sysUserMods2 = await _defaultTableDbContext.Set<SysTest>().FromSqlRaw("select * from SysTest where id='2'").ToListAsync();
return Ok();
}
[HttpGet]
public async Task<IActionResult> Get7()
{
var sysUserMod = await _defaultTableDbContext.Set<SysUserMod>().FindAsync("101");
sysUserMod.Age = new Random().Next(1,999);
_defaultTableDbContext.Update(sysUserMod);
_defaultTableDbContext.SaveChanges();
// var sysUserMods1 = await _defaultTableDbContext.Set<SysUserMod>().FromSqlRaw("select * from SysUserMod where id='2'").ToListAsync();
// var sysUserMods2 = await _defaultTableDbContext.Set<SysTest>().FromSqlRaw("select * from SysTest where id='2'").ToListAsync();
return Ok();
}
}
}

View File

@ -38,12 +38,13 @@ namespace Sample.MySql
});
}
var userModMonths = new List<SysUserLogByMonth>();
var begin = new DateTime(2021, 1, 01);
foreach (var id in ids)
{
userModMonths.Add(new SysUserLogByMonth()
{
Id = id.ToString(),
Time = DateTime.Now
Time = begin.AddHours(id*12)
});
}

View File

@ -40,10 +40,10 @@ namespace Sample.MySql.DbContexts
modelBuilder.ApplyConfiguration(new SysTestMap());
modelBuilder.ApplyConfiguration(new SysUserLogByMonthMap());
modelBuilder.Entity<SysUserLogByMonth>().HasData(new SysUserLogByMonth() { Id = "1", Time = DateTime.Now });
modelBuilder.Entity<SysTest>().HasData(new SysTest() { Id = "1", UserId = "123" });
modelBuilder.Entity<TestMod>().ToTable(nameof(TestMod));
modelBuilder.Entity<TestModItem>().ToTable(nameof(TestModItem));
// modelBuilder.Entity<SysUserLogByMonth>().HasData(new SysUserLogByMonth() { Id = "1", Time = DateTime.Now });
// modelBuilder.Entity<SysTest>().HasData(new SysTest() { Id = "1", UserId = "123" });
// modelBuilder.Entity<TestMod>().ToTable(nameof(TestMod));
// modelBuilder.Entity<SysTest>().ToTable("xxx");
}

View File

@ -12,11 +12,11 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0-alpha.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src6\ShardingCore6\ShardingCore6.csproj" />
<ProjectReference Include="..\..\src\ShardingCore\ShardingCore.csproj" />
</ItemGroup>

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Update;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Migrations;
using ShardingCore.Core.RuntimeContexts;
@ -12,7 +13,8 @@ namespace Sample.MySql
{
private readonly IShardingRuntimeContext _shardingRuntimeContext;
public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options)
public ShardingMySqlMigrationsSqlGenerator(IShardingRuntimeContext shardingRuntimeContext,MigrationsSqlGeneratorDependencies dependencies, ICommandBatchPreparer commandBatchPreparer, IMySqlOptions options) : base(dependencies, commandBatchPreparer, options)
{
_shardingRuntimeContext = shardingRuntimeContext;
}

View File

@ -9,14 +9,14 @@ public class SysUserModVirtualDataSourceRoute:AbstractShardingOperatorVirtualDat
{
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return $"{((string)shardingKey=="ds1"?"ds1":"ds2")}";
return $"{shardingKey}";
}
public override List<string> GetAllDataSourceNames()
{
return new List<string>()
{
"ds1", "ds2"
"ds0", "ds1", "ds2"
};
}

View File

@ -74,8 +74,8 @@ namespace Sample.MySql
.UseRouteConfig(o =>
{
o.AddShardingTableRoute<DynamicTableRoute>();
o.AddShardingTableRoute<SysUserLogByMonthRoute>();
// o.AddShardingTableRoute<SysUserModVirtualTableRoute>();
o.AddShardingTableRoute<SysUserLogByMonthRoute>();
o.AddShardingTableRoute<SysUserModVirtualTableRoute>();
o.AddShardingDataSourceRoute<SysUserModVirtualDataSourceRoute>();
o.AddShardingTableRoute<TestModRoute>();
o.AddShardingTableRoute<TestModItemRoute>();
@ -104,25 +104,25 @@ namespace Sample.MySql
o.UseShardingQuery((conStr, builder) =>
{
builder.UseMySql(conStr, new MySqlServerVersion(new Version()));
// .UseLoggerFactory(efLogger)
// .EnableSensitiveDataLogging()
builder.UseMySql(conStr, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger)
.EnableSensitiveDataLogging();
//.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
o.UseShardingTransaction((connection, builder) =>
{
builder
.UseMySql(connection, new MySqlServerVersion(new Version()));
// .UseLoggerFactory(efLogger)
// .EnableSensitiveDataLogging()
.UseMySql(connection, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger)
.EnableSensitiveDataLogging();
//.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
o.AddDefaultDataSource("ds0",
"server=127.0.0.1;port=3306;database=dbdbd02;userid=root;password=root;");
"server=127.0.0.1;port=3306;database=dbdbd0;userid=root;password=root;");
o.AddExtraDataSource(sp => new Dictionary<string, string>()
{
{ "ds1", "server=127.0.0.1;port=3306;database=dbdbd12;userid=root;password=root;" },
{ "ds2", "server=127.0.0.1;port=3306;database=dbdbd22;userid=root;password=root;" }
{ "ds1", "server=127.0.0.1;port=3306;database=dbdbd1;userid=root;password=root;" },
{ "ds2", "server=127.0.0.1;port=3306;database=dbdbd2;userid=root;password=root;" }
});
o.UseShardingMigrationConfigure(b =>
{

View File

@ -1,24 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ShardingDbContextExecutors;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ShardingCore.EFCores;
using ShardingCore.Helpers;
using ShardingCore.Utils;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Reflection;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Exceptions;
using ShardingCore.Sharding;
namespace Samples.AbpSharding
{
@ -26,59 +14,36 @@ namespace Samples.AbpSharding
public abstract class AbstractShardingAbpDbContext<TDbContext> : AbpDbContext<TDbContext>, IShardingDbContext
where TDbContext : DbContext
{
private readonly IShardingDbContextExecutor _shardingDbContextExecutor;
protected AbstractShardingAbpDbContext(DbContextOptions<TDbContext> options) : base(options)
{
var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
if (wrapOptionsExtension != null)
{
_shardingDbContextExecutor = new ShardingDbContextExecutor(this);
}
}
/// <summary>
/// 是否是真正的执行者
/// </summary>
private bool isExecutor => _shardingDbContextExecutor == null;
//public void ShardingUpgrade()
//{
// //IsExecutor = true;
//}
public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail)
{
var dbContext = _shardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail);
if (dbContext is AbpDbContext<TDbContext> abpDbContext)
{
abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
}
return dbContext;
}
/// <summary>
/// 根据对象创建通用的dbcontext
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public DbContext CreateGenericDbContext<TEntity>(TEntity entity) where TEntity : class
{
CheckAndSetShardingKeyThatSupportAutoCreate(entity);
var dbContext = _shardingDbContextExecutor.CreateGenericDbContext(entity);
if (dbContext is AbpDbContext<TDbContext> abpDbContext && abpDbContext.LazyServiceProvider == null)
{
abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
}
return dbContext;
}
private IShardingDbContextExecutor _shardingDbContextExecutor;
public IShardingDbContextExecutor GetShardingExecutor()
{
return _shardingDbContextExecutor;
return _shardingDbContextExecutor??=DoCreateShardingDbContextExecutor();
}
private IShardingDbContextExecutor DoCreateShardingDbContextExecutor()
{
var shardingDbContextExecutor = this.CreateShardingDbContextExecutor()!;
shardingDbContextExecutor.EntityCreateDbContextBefore += (sender, args) =>
{
CheckAndSetShardingKeyThatSupportAutoCreate(args.Entity);
};
shardingDbContextExecutor.CreateDbContextAfter += (sender, args) =>
{
var argsDbContext = args.DbContext;
if (argsDbContext is AbpDbContext<TDbContext> abpDbContext&&
abpDbContext.LazyServiceProvider == null)
{
abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
}
};
return shardingDbContextExecutor;
}
@ -118,416 +83,19 @@ namespace Samples.AbpSharding
}
}
public override EntityEntry Add(object entity)
{
if (isExecutor)
base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
public override EntityEntry<TEntity> Add<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
public override ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override ValueTask<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override void AddRange(params object[] entities)
{
if (isExecutor)
{
base.AddRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AddRange(group.Select(o => o.Entity));
}
}
public override void AddRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.AddRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AddRange(group.Select(o => o.Entity));
}
}
public override async Task AddRangeAsync(params object[] entities)
{
if (isExecutor)
{
await base.AddRangeAsync(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
await group.Key.AddRangeAsync(group.Select(o => o.Entity));
}
}
public override async Task AddRangeAsync(IEnumerable<object> entities, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
{
await base.AddRangeAsync(entities, cancellationToken);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
await group.Key.AddRangeAsync(group.Select(o => o.Entity));
}
}
public override EntityEntry<TEntity> Attach<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override EntityEntry Attach(object entity)
{
if (isExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override void AttachRange(params object[] entities)
{
if (isExecutor)
{
base.AttachRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AttachRange(group.Select(o => o.Entity));
}
}
public override void AttachRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.AttachRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AttachRange(group.Select(o => o.Entity));
}
}
//public override DatabaseFacade Database => _dbContextCaches.Any()
// ? _dbContextCaches.First().Value.Database
// : GetDbContext(true, string.Empty).Database;
public override EntityEntry<TEntity> Entry<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry Entry(object entity)
{
if (isExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override EntityEntry Update(object entity)
{
if (isExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override void UpdateRange(params object[] entities)
{
if (isExecutor)
{
base.UpdateRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o => o.Entity));
}
}
public override void UpdateRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.UpdateRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o => o.Entity));
}
}
public override EntityEntry<TEntity> Remove<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override EntityEntry Remove(object entity)
{
if (isExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override void RemoveRange(params object[] entities)
{
if (isExecutor)
{
base.RemoveRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o => o.Entity));
}
}
public override void RemoveRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.RemoveRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o => o.Entity));
}
}
public override int SaveChanges()
{
if (isExecutor)
return base.SaveChanges();
return this.SaveChanges(true);
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
if (isExecutor)
return base.SaveChanges(acceptAllChangesOnSuccess);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.CurrentTransaction == null && _shardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = Database.BeginTransaction())
{
i = _shardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _shardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.SaveChangesAsync(cancellationToken);
return this.SaveChangesAsync(true, cancellationToken);
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.CurrentTransaction == null && _shardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = await Database.BeginTransactionAsync(cancellationToken))
{
i = await _shardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _shardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
public override void Dispose()
{
if (isExecutor)
{
base.Dispose();
}
else
{
_shardingDbContextExecutor.Dispose();
base.Dispose();
}
_shardingDbContextExecutor?.Dispose();
base.Dispose();
}
public override async ValueTask DisposeAsync()
{
if (isExecutor)
{
await base.DisposeAsync();
}
else
if (_shardingDbContextExecutor != null)
{
await _shardingDbContextExecutor.DisposeAsync();
await base.DisposeAsync();
}
await base.DisposeAsync();
}
}
}

View File

@ -34,478 +34,35 @@ namespace Samples.AbpSharding
where TUser : AbpUser<TUser>
where TSelf : AbpZeroDbContext<TTenant, TRole, TUser, TSelf>
{
private readonly IShardingDbContextExecutor _shardingDbContextExecutor;
protected AbstractShardingAbpZeroDbContext(DbContextOptions<TSelf> options)
: base(options)
{
var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
if (wrapOptionsExtension != null)
{
_shardingDbContextExecutor = new ShardingDbContextExecutor(this);
}
}
/// <summary>
/// 是否是真正的执行者
/// </summary>
protected bool isExecutor => _shardingDbContextExecutor == null;
/// <summary>
/// 路由表
/// </summary>
public IRouteTail RouteTail { get; set; }
public override EntityEntry Add(object entity)
{
if (isExecutor)
base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
public override EntityEntry<TEntity> Add<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
public override ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override ValueTask<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override void AddRange(params object[] entities)
{
if (isExecutor)
{
base.AddRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AddRange(group.Select(o => o.Entity));
}
}
public override void AddRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.AddRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AddRange(group.Select(o => o.Entity));
}
}
public override async Task AddRangeAsync(params object[] entities)
{
if (isExecutor)
{
await base.AddRangeAsync(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
await group.Key.AddRangeAsync(group.Select(o => o.Entity));
}
}
public override async Task AddRangeAsync(IEnumerable<object> entities, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
{
await base.AddRangeAsync(entities, cancellationToken);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
await group.Key.AddRangeAsync(group.Select(o => o.Entity));
}
}
public override EntityEntry<TEntity> Attach<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override EntityEntry Attach(object entity)
{
if (isExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override void AttachRange(params object[] entities)
{
if (isExecutor)
{
base.AttachRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AttachRange(group.Select(o => o.Entity));
}
}
public override void AttachRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.AttachRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AttachRange(group.Select(o => o.Entity));
}
}
public override EntityEntry<TEntity> Entry<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry Entry(object entity)
{
if (isExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override EntityEntry Update(object entity)
{
if (isExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override void UpdateRange(params object[] entities)
{
if (isExecutor)
{
base.UpdateRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o => o.Entity));
}
}
public override void UpdateRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.UpdateRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o => o.Entity));
}
}
public override EntityEntry<TEntity> Remove<TEntity>(TEntity entity)
{
if (isExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override EntityEntry Remove(object entity)
{
if (isExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override void RemoveRange(params object[] entities)
{
if (isExecutor)
{
base.RemoveRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o => o.Entity));
}
}
public override void RemoveRange(IEnumerable<object> entities)
{
if (isExecutor)
{
base.RemoveRange(entities);
return;
}
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o => o.Entity));
}
}
public override int SaveChanges()
{
if (isExecutor)
return base.SaveChanges();
return this.SaveChanges(true);
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
if (isExecutor)
return base.SaveChanges(acceptAllChangesOnSuccess);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.CurrentTransaction == null && _shardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = Database.BeginTransaction())
{
i = _shardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _shardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return base.SaveChangesAsync(cancellationToken);
return this.SaveChangesAsync(true, cancellationToken);
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
if (isExecutor)
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.CurrentTransaction == null && _shardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = await Database.BeginTransactionAsync(cancellationToken))
{
i = await _shardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _shardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
public override void Dispose()
{
if (isExecutor)
{
base.Dispose();
}
else
{
_shardingDbContextExecutor.Dispose();
base.Dispose();
}
}
public override async ValueTask DisposeAsync()
{
if (isExecutor)
{
await base.DisposeAsync();
}
else
{
await _shardingDbContextExecutor.DisposeAsync();
await base.DisposeAsync();
}
}
#region Sharding Core
/// <summary>
/// 根据查询获取DbContext
/// </summary>
/// <param name="dataSourceName"></param>
/// <param name="strategy"></param>
/// <param name="routeTail"></param>
/// <returns></returns>
public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail)
{
var dbContext = _shardingDbContextExecutor.CreateDbContext(
strategy,
dataSourceName,
routeTail
);
this.FillDbContextInject(dbContext);
return dbContext;
}
/// <summary>
/// 根据对象创建通用的dbcontext
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public DbContext CreateGenericDbContext<TEntity>(TEntity entity) where TEntity : class
{
CheckAndSetShardingKeyThatSupportAutoCreate(entity);
var dbContext = _shardingDbContextExecutor.CreateGenericDbContext(entity);
this.FillDbContextInject(dbContext);
return dbContext;
}
private IShardingDbContextExecutor _shardingDbContextExecutor;
public IShardingDbContextExecutor GetShardingExecutor()
{
return _shardingDbContextExecutor;
return _shardingDbContextExecutor??=DoCreateShardingDbContextExecutor();
}
private IShardingDbContextExecutor DoCreateShardingDbContextExecutor()
{
var shardingDbContextExecutor = this.CreateShardingDbContextExecutor()!;
shardingDbContextExecutor.EntityCreateDbContextBefore += (sender, args) =>
{
CheckAndSetShardingKeyThatSupportAutoCreate(args.Entity);
};
shardingDbContextExecutor.CreateDbContextAfter += (sender, args) =>
{
var argsDbContext = args.DbContext;
FillDbContextInject(argsDbContext);
};
return shardingDbContextExecutor;
}
#endregion
@ -562,7 +119,7 @@ namespace Samples.AbpSharding
/// <param name="dbContext"></param>
protected virtual void FillDbContextInject(DbContext dbContext)
{
if (dbContext is AbpZeroCommonDbContext<TRole, TUser, TSelf> abpDbContext)
if ( dbContext is AbpZeroCommonDbContext<TRole, TUser, TSelf> abpDbContext)
{
// AbpZeroCommonDbContext
if (abpDbContext.EntityHistoryHelper == null)
@ -604,5 +161,23 @@ namespace Samples.AbpSharding
}
#endregion
public override void Dispose()
{
_shardingDbContextExecutor?.Dispose();
base.Dispose();
}
public override async ValueTask DisposeAsync()
{
if(_shardingDbContextExecutor!=null)
{
await _shardingDbContextExecutor.DisposeAsync();
}
await base.DisposeAsync();
}
}
}

View File

@ -13,7 +13,7 @@ namespace ShardingCore.Core.QueryTrackers
{
public object Track(object entity, IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(entity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var attachedEntity = genericDbContext.GetAttachedEntity(entity);
if (attachedEntity == null)
genericDbContext.Attach(entity);

View File

@ -71,7 +71,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
var dataSources = allDataSourceNames.Where(o => o== shardingKeyToDataSource).ToList();
if (dataSources.IsEmpty())
{
throw new ShardingCoreException($"sharding key route not match {EntityMetadata.EntityType} -> [{EntityMetadata.ShardingTableProperty.Name}] ->【{shardingKey}】 all data sources ->[{string.Join(",", allDataSourceNames.Select(o=>o))}]");
throw new ShardingCoreException($"sharding key route not match {EntityMetadata.EntityType} -> [{EntityMetadata.ShardingDataSourceProperty.Name}] ->【{shardingKey}】 all data sources ->[{string.Join(",", allDataSourceNames.Select(o=>o))}]");
}
if (dataSources.Count > 1)

View File

@ -91,7 +91,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}
@ -101,7 +101,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}

View File

@ -1,62 +0,0 @@
#if EFCORE2
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 August 2021 10:17:43
* @Email: 326308290@qq.com
*/
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class ShardingDbSetSource : IDbSetSource, IDbQuerySource
{
private static readonly MethodInfo _genericCreateSet
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory));
private static readonly MethodInfo _genericCreateQuery
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateQueryFactory));
private readonly ConcurrentDictionary<Type, Func<DbContext, object>> _cache
= new ConcurrentDictionary<Type, Func<DbContext, object>>();
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual object Create(DbContext context, Type type)
=> CreateCore(context, type, _genericCreateSet);
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual object CreateQuery(DbContext context, Type type)
=> CreateCore(context, type, _genericCreateQuery);
private object CreateCore(DbContext context, Type type, MethodInfo createMethod)
=> _cache.GetOrAdd(
type,
t => (Func<DbContext, object>)createMethod
.MakeGenericMethod(t)
.Invoke(null, null))(context);
private static Func<DbContext, object> CreateSetFactory<TEntity>()
where TEntity : class
=> c => new ShardingInternalDbSet<TEntity>(c);
private static Func<DbContext, DbQuery<TQuery>> CreateQueryFactory<TQuery>()
where TQuery : class
=> c => new ShardingInternalDbQuery<TQuery>(c);
}
}
#endif

View File

@ -1,27 +0,0 @@
#if EFCORE2
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/20 17:05:36
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbQuery<TQuery> : InternalDbQuery<TQuery> where TQuery : class
{
public ShardingInternalDbQuery(DbContext context) : base(context)
{
}
}
}
#endif

View File

@ -1,427 +0,0 @@
#if EFCORE2
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using ShardingCore.Core;
using ShardingCore.Sharding.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.TrackerManagers;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/15 8:39:15
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbSet<TEntity> : InternalDbSet<TEntity>
where TEntity : class
{
private readonly IShardingDbContext _context;
private readonly IShardingRuntimeContext _shardingRuntimeContext;
private LocalView<TEntity>? _localView;
public ShardingInternalDbSet(DbContext context) : base(context)
{
_context = (IShardingDbContext)context;
_shardingRuntimeContext = context.GetShardingRuntimeContext();
}
private IDataSourceRouteManager _dataSourceRouteManager;
protected IDataSourceRouteManager DataSourceRouteManager
{
get
{
if (null == _dataSourceRouteManager)
{
_dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
}
return _dataSourceRouteManager;
}
}
public override LocalView<TEntity> Local
{
get
{
if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled)
{
((DbContext)_context).ChangeTracker.DetectChanges();
}
return _localView ??= new ShardingLocalView<TEntity>(this);
}
}
private ITableRouteManager _tableRouteManager;
protected ITableRouteManager TableRouteManager
{
get
{
if (null == _tableRouteManager)
{
_tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
}
return _tableRouteManager;
}
}
private IEntityMetadataManager _entityMetadataManager;
protected IEntityMetadataManager EntityMetadataManager
{
get
{
if (null == _entityMetadataManager)
{
_entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager();
}
return _entityMetadataManager;
}
}
private ITrackerManager _trackerManager;
protected ITrackerManager TrackerManager
{
get
{
if (null == _trackerManager)
{
_trackerManager = _shardingRuntimeContext.GetTrackerManager();
}
return _trackerManager;
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Add(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Add(entity);
}
public override async Task<EntityEntry<TEntity>> AddAsync(TEntity entity, CancellationToken cancellationToken =
new CancellationToken())
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return await genericDbContext.Set<TEntity>().AddAsync(entity, cancellationToken);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Attach(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Attach(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Remove(TEntity entity)
{
Check.NotNull(entity, nameof(entity));
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Remove(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Update(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Update(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(params TEntity[] entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value, cancellationToken);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(IEnumerable<TEntity> entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic(IEnumerable<TEntity> entities)
{
return entities.Select(o =>
{
var dbContext = _context.CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override TEntity Find(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().Find(keyValues);
}
return base.Find(keyValues);
}
public override Task<TEntity> FindAsync(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues);
}
return base.FindAsync(keyValues);
}
public override Task<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues, cancellationToken);
}
return base.FindAsync(keyValues, cancellationToken);
}
private DbContext GetDbContextByKeyValue(params object[] keyValues)
{
var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity));
if (entityMetadata == null)
{
throw new ShardingCoreInvalidOperationException(
$"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}");
}
//既不是分表也不是分库的话就是默认对象
if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource())
{
var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName;
var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory();
var routeTail = routeTailFactory.Create(string.Empty);
return _context.GetShareDbContext(defaultDataSourceName, routeTail);
}
if (keyValues.Length == 1)
{
//单key字段
if (entityMetadata.IsSingleKey)
{
var isShardingDataSource = entityMetadata.IsShardingDataSource();
var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey();
if (isShardingDataSource && !shardingDataSourceFieldIsKey)
return null;
var isShardingTable = entityMetadata.IsShardingTable();
var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey();
if (isShardingTable && !shardingTableFieldIsKey)
return null;
var primaryKeyValue = keyValues[0];
if (primaryKeyValue != null)
{
var dataSourceName = GetDataSourceName(primaryKeyValue);
var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity));
var tableTail = TableRouteManager.GetTableTail<TEntity>(dataSourceName, primaryKeyValue,realEntityType);
var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail);
return _context.GetShareDbContext(dataSourceName, routeTail);
}
}
}
return null;
}
private string GetDataSourceName(object shardingKeyValue)
{
return DataSourceRouteManager.GetDataSourceName<TEntity>(shardingKeyValue);
}
}
}
#endif

View File

@ -0,0 +1,105 @@
#if EFCORE2
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.EFCores
{
public class ShardingStateManager:StateManager
{
private readonly DbContext _currentDbContext;
private readonly IShardingDbContext _currentShardingDbContext;
public ShardingStateManager(StateManagerDependencies dependencies) : base(dependencies)
{
_currentDbContext=dependencies.CurrentContext.Context;
_currentShardingDbContext = (IShardingDbContext)_currentDbContext;
}
public override InternalEntityEntry GetOrCreateEntry(object entity)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity);
}
public override InternalEntityEntry GetOrCreateEntry(object entity, IEntityType entityType)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var findEntityType = genericDbContext.Model.FindEntityType(entity.GetType());
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity, findEntityType);
}
public override InternalEntityEntry StartTrackingFromQuery(IEntityType baseEntityType, object entity, in ValueBuffer valueBuffer,
ISet<IForeignKey> handledForeignKeys)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, bool throwOnNonUniqueness = true)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, IEntityType entityType)
{
throw new ShardingCoreNotImplementedException();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled&&_currentDbContext.Database.CurrentTransaction==null&&_currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = _currentDbContext.Database.BeginTransaction())
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled && _currentDbContext.Database.CurrentTransaction==null && _currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = await _currentDbContext.Database.BeginTransactionAsync(cancellationToken))
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
tran.Commit();
}
}
else
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
}
}
#endif

View File

@ -92,7 +92,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}
@ -102,7 +102,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}

View File

@ -1,48 +0,0 @@
#if EFCORE3
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 August 2021 10:17:43
* @Email: 326308290@qq.com
*/
public class ShardingDbSetSource:IDbSetSource
{
private static readonly MethodInfo _genericCreateSet
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory));
private readonly ConcurrentDictionary<Type, Func<DbContext, object>> _cache
= new ConcurrentDictionary<Type, Func<DbContext, object>>();
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, Type type)
=> CreateCore(context, type, _genericCreateSet);
private object CreateCore(DbContext context, Type type, MethodInfo createMethod)
=> _cache.GetOrAdd(
type,
t => (Func<DbContext, object>)createMethod
.MakeGenericMethod(t)
.Invoke(null, null))(context);
private static Func<DbContext, object> CreateSetFactory<TEntity>()
where TEntity : class
=> c => new ShardingInternalDbSet<TEntity>(c);
}
}
#endif

View File

@ -1,436 +0,0 @@
#if EFCORE3
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using ShardingCore.Core;
using ShardingCore.Sharding.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.TrackerManagers;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/15 8:39:15
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbSet<TEntity> : InternalDbSet<TEntity>
where TEntity : class
{
private readonly IShardingDbContext _context;
private readonly IShardingRuntimeContext _shardingRuntimeContext;
private LocalView<TEntity>? _localView;
public ShardingInternalDbSet(DbContext context) : base(context)
{
_context = (IShardingDbContext)context;
_shardingRuntimeContext = context.GetShardingRuntimeContext();
}
private IDataSourceRouteManager _dataSourceRouteManager;
protected IDataSourceRouteManager DataSourceRouteManager
{
get
{
if (null == _dataSourceRouteManager)
{
_dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
}
return _dataSourceRouteManager;
}
}
public override LocalView<TEntity> Local
{
get
{
if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled)
{
((DbContext)_context).ChangeTracker.DetectChanges();
}
return _localView ??= new ShardingLocalView<TEntity>(this);
}
}
private ITableRouteManager _tableRouteManager;
protected ITableRouteManager TableRouteManager
{
get
{
if (null == _tableRouteManager)
{
_tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
}
return _tableRouteManager;
}
}
private IEntityMetadataManager _entityMetadataManager;
protected IEntityMetadataManager EntityMetadataManager
{
get
{
if (null == _entityMetadataManager)
{
_entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager();
}
return _entityMetadataManager;
}
}
private ITrackerManager _trackerManager;
protected ITrackerManager TrackerManager
{
get
{
if (null == _trackerManager)
{
_trackerManager = _shardingRuntimeContext.GetTrackerManager();
}
return _trackerManager;
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Add(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Add(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async ValueTask<EntityEntry<TEntity>> AddAsync(
TEntity entity,
CancellationToken cancellationToken = default)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return await genericDbContext.Set<TEntity>().AddAsync(entity, cancellationToken);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Attach(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Attach(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Remove(TEntity entity)
{
Check.NotNull(entity, nameof(entity));
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Remove(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Update(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Update(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(params TEntity[] entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value, cancellationToken);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(IEnumerable<TEntity> entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic(IEnumerable<TEntity> entities)
{
return entities.Select(o =>
{
var dbContext = _context.CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override TEntity Find(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().Find(keyValues);
}
return base.Find(keyValues);
}
public override ValueTask<TEntity> FindAsync(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues);
}
return base.FindAsync(keyValues);
}
public override ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues, cancellationToken);
}
return base.FindAsync(keyValues, cancellationToken);
}
private DbContext GetDbContextByKeyValue(params object[] keyValues)
{
var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity));
if (entityMetadata == null)
{
throw new ShardingCoreInvalidOperationException(
$"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}");
}
//既不是分表也不是分库的话就是默认对象
if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource())
{
var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName;
var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory();
var routeTail = routeTailFactory.Create(string.Empty);
return _context.GetShareDbContext(defaultDataSourceName, routeTail);
}
if (keyValues.Length == 1)
{
//单key字段
if (entityMetadata.IsSingleKey)
{
var isShardingDataSource = entityMetadata.IsShardingDataSource();
var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey();
if (isShardingDataSource && !shardingDataSourceFieldIsKey)
return null;
var isShardingTable = entityMetadata.IsShardingTable();
var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey();
if (isShardingTable && !shardingTableFieldIsKey)
return null;
var primaryKeyValue = keyValues[0];
if (primaryKeyValue != null)
{
var dataSourceName = GetDataSourceName(primaryKeyValue);
var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity));
var tableTail = TableRouteManager.GetTableTail<TEntity>(dataSourceName, primaryKeyValue,realEntityType);
var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail);
return _context.GetShareDbContext(dataSourceName, routeTail);
}
}
}
return null;
}
private string GetDataSourceName(object shardingKeyValue)
{
return DataSourceRouteManager.GetDataSourceName<TEntity>(shardingKeyValue);
}
}
}
#endif

View File

@ -0,0 +1,104 @@
#if EFCORE3
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.EFCores
{
public class ShardingStateManager:StateManager
{
private readonly DbContext _currentDbContext;
private readonly IShardingDbContext _currentShardingDbContext;
public ShardingStateManager(StateManagerDependencies dependencies) : base(dependencies)
{
_currentDbContext=dependencies.CurrentContext.Context;
_currentShardingDbContext = (IShardingDbContext)_currentDbContext;
}
public override InternalEntityEntry GetOrCreateEntry(object entity)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity);
}
public override InternalEntityEntry GetOrCreateEntry(object entity, IEntityType entityType)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var findEntityType = genericDbContext.Model.FindEntityType(entity.GetType());
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity, findEntityType);
}
public override InternalEntityEntry StartTrackingFromQuery(IEntityType baseEntityType, object entity, in ValueBuffer valueBuffer)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, bool throwOnNonUniqueness = true)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, IEntityType entityType, bool throwOnTypeMismatch = true)
{
throw new ShardingCoreNotImplementedException();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled&&_currentDbContext.Database.CurrentTransaction==null&&_currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = _currentDbContext.Database.BeginTransaction())
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled && _currentDbContext.Database.CurrentTransaction==null && _currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = await _currentDbContext.Database.BeginTransactionAsync(cancellationToken))
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
}
}
#endif

View File

@ -91,7 +91,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}
@ -101,7 +101,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}

View File

@ -1,57 +0,0 @@
#if EFCORE5
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 August 2021 10:17:43
* @Email: 326308290@qq.com
*/
public class ShardingDbSetSource : IDbSetSource
{
private static readonly MethodInfo _genericCreateSet
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory));
private readonly ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>> _cache
= new ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>>();
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, Type type)
=> CreateCore(context, type, null, _genericCreateSet);
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, string name, Type type)
=> CreateCore(context, type, name, _genericCreateSet);
private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod)
=> _cache.GetOrAdd(
(type, name),
t => (Func<DbContext, string, object>)createMethod
.MakeGenericMethod(t.Type)
.Invoke(null, null))(context, name);
private static Func<DbContext, string, object> CreateSetFactory<TEntity>()
where TEntity : class
=> (c, name) => new ShardingInternalDbSet<TEntity>(c, name);
}
}
#endif

View File

@ -1,439 +0,0 @@
#if EFCORE5
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using ShardingCore.Core;
using ShardingCore.Sharding.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.TrackerManagers;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/15 8:39:15
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbSet<TEntity> : InternalDbSet<TEntity>
where TEntity : class
{
private readonly IShardingDbContext _context;
private readonly IShardingRuntimeContext _shardingRuntimeContext;
private LocalView<TEntity>? _localView;
public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName)
{
_context = (IShardingDbContext)context;
_shardingRuntimeContext = context.GetShardingRuntimeContext();
}
private IDataSourceRouteManager _dataSourceRouteManager;
protected IDataSourceRouteManager DataSourceRouteManager
{
get
{
if (null == _dataSourceRouteManager)
{
_dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
}
return _dataSourceRouteManager;
}
}
public override LocalView<TEntity> Local
{
get
{
if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled)
{
((DbContext)_context).ChangeTracker.DetectChanges();
}
return _localView ??= new ShardingLocalView<TEntity>(this);
}
}
private ITableRouteManager _tableRouteManager;
protected ITableRouteManager TableRouteManager
{
get
{
if (null == _tableRouteManager)
{
_tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
}
return _tableRouteManager;
}
}
private IEntityMetadataManager _entityMetadataManager;
protected IEntityMetadataManager EntityMetadataManager
{
get
{
if (null == _entityMetadataManager)
{
_entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager();
}
return _entityMetadataManager;
}
}
private ITrackerManager _trackerManager;
protected ITrackerManager TrackerManager
{
get
{
if (null == _trackerManager)
{
_trackerManager = _shardingRuntimeContext.GetTrackerManager();
}
return _trackerManager;
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Add(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Add(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async ValueTask<EntityEntry<TEntity>> AddAsync(
TEntity entity,
CancellationToken cancellationToken = default)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return await genericDbContext.Set<TEntity>().AddAsync(entity, cancellationToken);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Attach(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Attach(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Remove(TEntity entity)
{
Check.NotNull(entity, nameof(entity));
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Remove(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Update(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Update(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(params TEntity[] entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value, cancellationToken);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(IEnumerable<TEntity> entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic(IEnumerable<TEntity> entities)
{
return entities.Select(o =>
{
var dbContext = _context.CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override TEntity Find(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().Find(keyValues);
}
return base.Find(keyValues);
}
public override ValueTask<TEntity> FindAsync(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues);
}
return base.FindAsync(keyValues);
}
public override ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues, cancellationToken);
}
return base.FindAsync(keyValues, cancellationToken);
}
private DbContext GetDbContextByKeyValue(params object[] keyValues)
{
var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity));
if (entityMetadata == null)
{
throw new ShardingCoreInvalidOperationException(
$"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}");
}
//既不是分表也不是分库的话就是默认对象
if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource())
{
var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName;
var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory();
var routeTail = routeTailFactory.Create(string.Empty);
return _context.GetShareDbContext(defaultDataSourceName, routeTail);
}
if (keyValues.Length == 1)
{
//单key字段
if (entityMetadata.IsSingleKey)
{
var isShardingDataSource = entityMetadata.IsShardingDataSource();
var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey();
if (isShardingDataSource && !shardingDataSourceFieldIsKey)
return null;
var isShardingTable = entityMetadata.IsShardingTable();
var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey();
if (isShardingTable && !shardingTableFieldIsKey)
return null;
var primaryKeyValue = keyValues[0];
if (primaryKeyValue != null)
{
var dataSourceName = GetDataSourceName(primaryKeyValue);
var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity));
var tableTail = TableRouteManager.GetTableTail<TEntity>(dataSourceName, primaryKeyValue,realEntityType);
var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail);
return _context.GetShareDbContext(dataSourceName, routeTail);
}
}
}
return null;
}
private string GetDataSourceName(object shardingKeyValue)
{
return DataSourceRouteManager.GetDataSourceName<TEntity>(shardingKeyValue);
}
}
}
#endif

View File

@ -0,0 +1,104 @@
#if EFCORE5
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.EFCores
{
public class ShardingStateManager:StateManager
{
private readonly DbContext _currentDbContext;
private readonly IShardingDbContext _currentShardingDbContext;
public ShardingStateManager(StateManagerDependencies dependencies) : base(dependencies)
{
_currentDbContext=dependencies.CurrentContext.Context;
_currentShardingDbContext = (IShardingDbContext)_currentDbContext;
}
public override InternalEntityEntry GetOrCreateEntry(object entity)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity);
}
public override InternalEntityEntry GetOrCreateEntry(object entity, IEntityType entityType)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var findEntityType = genericDbContext.Model.FindEntityType(entity.GetType());
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity, findEntityType);
}
public override InternalEntityEntry StartTrackingFromQuery(IEntityType baseEntityType, object entity, in ValueBuffer valueBuffer)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, bool throwOnNonUniqueness = true)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, IEntityType entityType, bool throwOnTypeMismatch = true)
{
throw new ShardingCoreNotImplementedException();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled&&_currentDbContext.Database.CurrentTransaction==null&&_currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = _currentDbContext.Database.BeginTransaction())
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled && _currentDbContext.Database.CurrentTransaction==null && _currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = await _currentDbContext.Database.BeginTransactionAsync(cancellationToken))
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
}
}
#endif

View File

@ -91,7 +91,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}
@ -101,7 +101,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}

View File

@ -1,57 +0,0 @@
#if EFCORE6
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 August 2021 10:17:43
* @Email: 326308290@qq.com
*/
public class ShardingDbSetSource : IDbSetSource
{
private static readonly MethodInfo _genericCreateSet
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory));
private readonly ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>> _cache
= new ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>>();
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, Type type)
=> CreateCore(context, type, null, _genericCreateSet);
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, string name, Type type)
=> CreateCore(context, type, name, _genericCreateSet);
private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod)
=> _cache.GetOrAdd(
(type, name),
t => (Func<DbContext, string, object>)createMethod
.MakeGenericMethod(t.Type)
.Invoke(null, null))(context, name);
private static Func<DbContext, string, object> CreateSetFactory<TEntity>()
where TEntity : class
=> (c, name) => new ShardingInternalDbSet<TEntity>(c, name);
}
}
#endif

View File

@ -1,436 +0,0 @@
#if EFCORE6
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using ShardingCore.Core;
using ShardingCore.Sharding.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.TrackerManagers;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/15 8:39:15
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbSet<TEntity> : InternalDbSet<TEntity>
where TEntity : class
{
private readonly IShardingDbContext _context;
private readonly IShardingRuntimeContext _shardingRuntimeContext;
private LocalView<TEntity>? _localView;
public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName)
{
_context = (IShardingDbContext)context;
_shardingRuntimeContext = context.GetShardingRuntimeContext();
}
private IDataSourceRouteManager _dataSourceRouteManager;
protected IDataSourceRouteManager DataSourceRouteManager
{
get
{
if (null == _dataSourceRouteManager)
{
_dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
}
return _dataSourceRouteManager;
}
}
public override LocalView<TEntity> Local
{
get
{
if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled)
{
((DbContext)_context).ChangeTracker.DetectChanges();
}
return _localView ??= new ShardingLocalView<TEntity>(this);
}
}
private ITableRouteManager _tableRouteManager;
protected ITableRouteManager TableRouteManager
{
get
{
if (null == _tableRouteManager)
{
_tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
}
return _tableRouteManager;
}
}
private IEntityMetadataManager _entityMetadataManager;
protected IEntityMetadataManager EntityMetadataManager
{
get
{
if (null == _entityMetadataManager)
{
_entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager();
}
return _entityMetadataManager;
}
}
private ITrackerManager _trackerManager;
protected ITrackerManager TrackerManager
{
get
{
if (null == _trackerManager)
{
_trackerManager = _shardingRuntimeContext.GetTrackerManager();
}
return _trackerManager;
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Add(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Add(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async ValueTask<EntityEntry<TEntity>> AddAsync(
TEntity entity,
CancellationToken cancellationToken = default)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return await genericDbContext.Set<TEntity>().AddAsync(entity, cancellationToken);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Attach(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Attach(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Remove(TEntity entity)
{
Check.NotNull(entity, nameof(entity));
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Remove(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Update(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Update(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(params TEntity[] entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value, cancellationToken);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(IEnumerable<TEntity> entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic(IEnumerable<TEntity> entities)
{
return entities.Select(o =>
{
var dbContext = _context.CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override TEntity Find(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().Find(keyValues);
}
return base.Find(keyValues);
}
public override ValueTask<TEntity> FindAsync(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues);
}
return base.FindAsync(keyValues);
}
public override ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues, cancellationToken);
}
return base.FindAsync(keyValues, cancellationToken);
}
private DbContext GetDbContextByKeyValue(params object[] keyValues)
{
var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity));
if (entityMetadata == null)
{
throw new ShardingCoreInvalidOperationException(
$"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}");
}
//既不是分表也不是分库的话就是默认对象
if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource())
{
var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName;
var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory();
var routeTail = routeTailFactory.Create(string.Empty);
return _context.GetShareDbContext(defaultDataSourceName, routeTail);
}
if (keyValues.Length == 1)
{
//单key字段
if (entityMetadata.IsSingleKey)
{
var isShardingDataSource = entityMetadata.IsShardingDataSource();
var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey();
if (isShardingDataSource && !shardingDataSourceFieldIsKey)
return null;
var isShardingTable = entityMetadata.IsShardingTable();
var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey();
if (isShardingTable && !shardingTableFieldIsKey)
return null;
var primaryKeyValue = keyValues[0];
if (primaryKeyValue != null)
{
var dataSourceName = GetDataSourceName(primaryKeyValue);
var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity));
var tableTail = TableRouteManager.GetTableTail<TEntity>(dataSourceName, primaryKeyValue,realEntityType);
var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail);
return _context.GetShareDbContext(dataSourceName, routeTail);
}
}
}
return null;
}
private string GetDataSourceName(object shardingKeyValue)
{
return DataSourceRouteManager.GetDataSourceName<TEntity>(shardingKeyValue);
}
}
}
#endif

View File

@ -0,0 +1,104 @@
#if EFCORE6
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.EFCores
{
public class ShardingStateManager:StateManager
{
private readonly DbContext _currentDbContext;
private readonly IShardingDbContext _currentShardingDbContext;
public ShardingStateManager(StateManagerDependencies dependencies) : base(dependencies)
{
_currentDbContext=dependencies.CurrentContext.Context;
_currentShardingDbContext = (IShardingDbContext)_currentDbContext;
}
public override InternalEntityEntry GetOrCreateEntry(object entity)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity);
}
public override InternalEntityEntry GetOrCreateEntry(object entity, IEntityType entityType)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var findEntityType = genericDbContext.Model.FindEntityType(entity.GetType());
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity, findEntityType);
}
public override InternalEntityEntry StartTrackingFromQuery(IEntityType baseEntityType, object entity, in ValueBuffer valueBuffer)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, bool throwOnNonUniqueness = true)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, IEntityType entityType, bool throwOnTypeMismatch = true)
{
throw new ShardingCoreNotImplementedException();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled&&_currentDbContext.Database.CurrentTransaction==null&&_currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = _currentDbContext.Database.BeginTransaction())
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (_currentDbContext.Database.AutoTransactionsEnabled && _currentDbContext.Database.CurrentTransaction==null && _currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = await _currentDbContext.Database.BeginTransactionAsync(cancellationToken))
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
}
}
#endif

View File

@ -91,7 +91,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}
@ -101,7 +101,7 @@ namespace ShardingCore.EFCores
{
if (_dbContext is IShardingDbContext shardingDbContext)
{
var genericDbContext = shardingDbContext.CreateGenericDbContext(rootEntity);
var genericDbContext = shardingDbContext.GetShardingExecutor().CreateGenericDbContext(rootEntity);
genericDbContext.ChangeTracker.TrackGraph(rootEntity,state,callback);
// Do(c => c.TrackGraph(rootEntity,callback));
}

View File

@ -1,57 +0,0 @@
#if EFCORE7
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 August 2021 10:17:43
* @Email: 326308290@qq.com
*/
public class ShardingDbSetSource : IDbSetSource
{
private static readonly MethodInfo _genericCreateSet
= typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory));
private readonly ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>> _cache
= new ConcurrentDictionary<(Type Type, string Name), Func<DbContext, string, object>>();
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, Type type)
=> CreateCore(context, type, null, _genericCreateSet);
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object Create(DbContext context, string name, Type type)
=> CreateCore(context, type, name, _genericCreateSet);
private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod)
=> _cache.GetOrAdd(
(type, name),
t => (Func<DbContext, string, object>)createMethod
.MakeGenericMethod(t.Type)
.Invoke(null, null))(context, name);
private static Func<DbContext, string, object> CreateSetFactory<TEntity>()
where TEntity : class
=> (c, name) => new ShardingInternalDbSet<TEntity>(c, name);
}
}
#endif

View File

@ -1,435 +0,0 @@
#if EFCORE7
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using ShardingCore.Core;
using ShardingCore.Sharding.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.TrackerManagers;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualRoutes.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/8/15 8:39:15
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingInternalDbSet<TEntity> : InternalDbSet<TEntity>
where TEntity : class
{
private readonly IShardingDbContext _context;
private readonly IShardingRuntimeContext _shardingRuntimeContext;
private LocalView<TEntity>? _localView;
public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName)
{
_context = (IShardingDbContext)context;
_shardingRuntimeContext = context.GetShardingRuntimeContext();
}
private IDataSourceRouteManager _dataSourceRouteManager;
protected IDataSourceRouteManager DataSourceRouteManager
{
get
{
if (null == _dataSourceRouteManager)
{
_dataSourceRouteManager = _shardingRuntimeContext.GetDataSourceRouteManager();
}
return _dataSourceRouteManager;
}
}
public override LocalView<TEntity> Local
{
get
{
if (((DbContext)_context).ChangeTracker.AutoDetectChangesEnabled)
{
((DbContext)_context).ChangeTracker.DetectChanges();
}
return _localView ??= new ShardingLocalView<TEntity>(this);
}
}
private ITableRouteManager _tableRouteManager;
protected ITableRouteManager TableRouteManager
{
get
{
if (null == _tableRouteManager)
{
_tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
}
return _tableRouteManager;
}
}
private IEntityMetadataManager _entityMetadataManager;
protected IEntityMetadataManager EntityMetadataManager
{
get
{
if (null == _entityMetadataManager)
{
_entityMetadataManager = _shardingRuntimeContext.GetEntityMetadataManager();
}
return _entityMetadataManager;
}
}
private ITrackerManager _trackerManager;
protected ITrackerManager TrackerManager
{
get
{
if (null == _trackerManager)
{
_trackerManager = _shardingRuntimeContext.GetTrackerManager();
}
return _trackerManager;
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Add(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Add(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async ValueTask<EntityEntry<TEntity>> AddAsync(
TEntity entity,
CancellationToken cancellationToken = default)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return await genericDbContext.Set<TEntity>().AddAsync(entity, cancellationToken);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Attach(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Attach(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Remove(TEntity entity)
{
Check.NotNull(entity, nameof(entity));
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Remove(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override EntityEntry<TEntity> Update(TEntity entity)
{
var genericDbContext = _context.CreateGenericDbContext(entity);
return genericDbContext.Set<TEntity>().Update(entity);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(params TEntity[] entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(params TEntity[] entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AddRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AddRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override async Task AddRangeAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.Set<TEntity>().AddRangeAsync(aggregateKv.Value, cancellationToken);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void AttachRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().AttachRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void RemoveRange(IEnumerable<TEntity> entities)
{
Check.NotNull(entities, nameof(entities));
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().RemoveRange(aggregateKv.Value);
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override void UpdateRange(IEnumerable<TEntity> entities)
{
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.Set<TEntity>().UpdateRange(aggregateKv.Value);
}
}
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic(IEnumerable<TEntity> entities)
{
return entities.Select(o =>
{
var dbContext = _context.CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override TEntity Find(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().Find(keyValues);
}
return base.Find(keyValues);
}
public override ValueTask<TEntity> FindAsync(params object[] keyValues)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues);
}
return base.FindAsync(keyValues);
}
public override ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
var primaryKeyFindDbContext = GetDbContextByKeyValue(keyValues);
if (primaryKeyFindDbContext != null)
{
return primaryKeyFindDbContext.Set<TEntity>().FindAsync(keyValues, cancellationToken);
}
return base.FindAsync(keyValues, cancellationToken);
}
private DbContext GetDbContextByKeyValue(params object[] keyValues)
{
var entityMetadata = EntityMetadataManager.TryGet(typeof(TEntity));
if (entityMetadata == null)
{
throw new ShardingCoreInvalidOperationException(
$"cant found type:[{typeof(TEntity)}] in {nameof(IEntityMetadataManager)}");
}
//既不是分表也不是分库的话就是默认对象
if (!entityMetadata.IsShardingTable() && !entityMetadata.IsShardingDataSource())
{
var defaultDataSourceName = _shardingRuntimeContext.GetVirtualDataSource().DefaultDataSourceName;
var routeTailFactory = _shardingRuntimeContext.GetRouteTailFactory();
var routeTail = routeTailFactory.Create(string.Empty);
return _context.GetShareDbContext(defaultDataSourceName, routeTail);
}
if (keyValues.Length == 1)
{
//单key字段
if (entityMetadata.IsSingleKey)
{
var isShardingDataSource = entityMetadata.IsShardingDataSource();
var shardingDataSourceFieldIsKey = entityMetadata.ShardingDataSourceFieldIsKey();
if (isShardingDataSource && !shardingDataSourceFieldIsKey)
return null;
var isShardingTable = entityMetadata.IsShardingTable();
var shardingTableFieldIsKey = entityMetadata.ShardingTableFieldIsKey();
if (isShardingTable && !shardingTableFieldIsKey)
return null;
var primaryKeyValue = keyValues[0];
if (primaryKeyValue != null)
{
var dataSourceName = GetDataSourceName(primaryKeyValue);
var realEntityType = TrackerManager.TranslateEntityType(typeof(TEntity));
var tableTail = TableRouteManager.GetTableTail<TEntity>(dataSourceName, primaryKeyValue,realEntityType);
var routeTail = _shardingRuntimeContext.GetRouteTailFactory().Create(tableTail);
return _context.GetShareDbContext(dataSourceName, routeTail);
}
}
}
return null;
}
private string GetDataSourceName(object shardingKeyValue)
{
return DataSourceRouteManager.GetDataSourceName<TEntity>(shardingKeyValue);
}
}
}
#endif

View File

@ -2,9 +2,12 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ShardingDbContextExecutors;
namespace ShardingCore.EFCores
{

View File

@ -0,0 +1,103 @@
#if EFCORE7
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.EFCores
{
public class ShardingStateManager:StateManager
{
private readonly IShardingDbContext _currentShardingDbContext;
public ShardingStateManager(StateManagerDependencies dependencies) : base(dependencies)
{
_currentShardingDbContext = (IShardingDbContext)Context;
}
public override InternalEntityEntry GetOrCreateEntry(object entity)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity);
}
public override InternalEntityEntry GetOrCreateEntry(object entity, IEntityType entityType)
{
var genericDbContext = _currentShardingDbContext.GetShardingExecutor().CreateGenericDbContext(entity);
var findEntityType = genericDbContext.Model.FindEntityType(entity.GetType());
var dbContextDependencies = genericDbContext.GetService<IDbContextDependencies>();
var stateManager = dbContextDependencies.StateManager;
return stateManager.GetOrCreateEntry(entity, findEntityType);
}
public override InternalEntityEntry StartTrackingFromQuery(IEntityType baseEntityType, object entity, in ValueBuffer valueBuffer)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, bool throwOnNonUniqueness = true)
{
throw new ShardingCoreNotImplementedException();
}
public override InternalEntityEntry TryGetEntry(object entity, IEntityType entityType, bool throwOnTypeMismatch = true)
{
throw new ShardingCoreNotImplementedException();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Context.Database.AutoTransactionsEnabled&&Context.Database.CurrentTransaction==null&&_currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = Context.Database.BeginTransaction())
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = _currentShardingDbContext.GetShardingExecutor().SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Context.Database.AutoTransactionsEnabled && Context.Database.CurrentTransaction==null && _currentShardingDbContext.GetShardingExecutor().IsMultiDbContext)
{
using (var tran = await Context.Database.BeginTransactionAsync(cancellationToken))
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await tran.CommitAsync(cancellationToken);
}
}
else
{
i = await _currentShardingDbContext.GetShardingExecutor().SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
}
}
#endif

View File

@ -16,7 +16,10 @@ namespace ShardingCore.Exceptions
[ExcludeFromCodeCoverage]
public class ShardingCoreException : Exception
{
public ShardingCoreException()
{
}
public ShardingCoreException(string message) : base(message)
{
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Exceptions
{
[ExcludeFromCodeCoverage]
public class ShardingCoreNotImplementedException:ShardingCoreException
{
public ShardingCoreNotImplementedException()
{
}
public ShardingCoreNotImplementedException(string message) : base(message)
{
}
public ShardingCoreNotImplementedException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -146,7 +146,7 @@ namespace ShardingCore.Extensions
var shardingRuntimeContext = dbContext.GetShardingRuntimeContext();
var entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager();
#if EFCORE6 || EFCORE7
#if EFCORE6
var entityTypes = contextModel.GetEntityTypes();
foreach (var entityType in entityTypes)
{
@ -156,17 +156,8 @@ namespace ShardingCore.Extensions
}
}
var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel;
foreach (var keyValuePair in contextModelRelationalModel.Tables)
{
foreach (var valueEntityTypeMapping in keyValuePair.Value.EntityTypeMappings)
{
var x = !entityMetadataManager.IsOnlyShardingDataSource(valueEntityTypeMapping.EntityType.ClrType);
Console.WriteLine(valueEntityTypeMapping.EntityType.ClrType);
Console.WriteLine(x);
}
}
var valueTuples =
contextModelRelationalModel.Tables.Where(o => o.Value.EntityTypeMappings.Any(m => !entityMetadataManager.IsOnlyShardingDataSource(m.EntityType.ClrType))).Select(o => o.Key).ToList();
contextModelRelationalModel.Tables.Where(o => o.Value.EntityTypeMappings.Any(m => !entityMetadataManager.IsShardingDataSource(m.EntityType.ClrType) ||entityMetadataManager.TryGet(m.EntityType.ClrType)==null)).Select(o => o.Key).ToList();
for (int i = 0; i < valueTuples.Count; i++)
{
contextModelRelationalModel.Tables.Remove(valueTuples[i]);
@ -183,7 +174,7 @@ namespace ShardingCore.Extensions
}
var contextModelRelationalModel = contextModel.RelationalModel as RelationalModel;
var valueTuples =
contextModelRelationalModel.Tables.Where(o => o.Value.EntityTypeMappings.Any(m => !entityMetadataManager.IsOnlyShardingDataSource(m.EntityType.ClrType))).Select(o => o.Key).ToList();
contextModelRelationalModel.Tables.Where(o => o.Value.EntityTypeMappings.Any(m => !entityMetadataManager.IsShardingDataSource(m.EntityType.ClrType)||entityMetadataManager.TryGet(m.EntityType.ClrType)==null)).Select(o => o.Key).ToList();
for (int i = 0; i < valueTuples.Count; i++)
{
contextModelRelationalModel.Tables.Remove(valueTuples[i]);
@ -200,7 +191,7 @@ namespace ShardingCore.Extensions
_data.Clear();
}
}
var list = entityTypes.Where(o => !entityMetadataManager.IsOnlyShardingDataSource(o.Value.ClrType)).Select(o => o.Key).ToList();
var list = entityTypes.Where(o => !entityMetadataManager.IsShardingDataSource(o.Value.ClrType) || entityMetadataManager.TryGet(o.Value.ClrType) == null).Select(o => o.Key).ToList();
for (int i = 0; i < list.Count; i++)
{
entityTypes.Remove(list[i]);
@ -225,7 +216,7 @@ namespace ShardingCore.Extensions
var contextModel = dbContext.Model as Model;
#endif
#if EFCORE6|| EFCORE7
#if EFCORE6
var contextModelRelationalModel = contextModel.GetRelationalModel() as RelationalModel;
contextModelRelationalModel.Tables.Clear();
#endif

View File

@ -14,11 +14,24 @@ using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.EFCores;
using ShardingCore.Sharding;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ShardingDbContextExecutors;
namespace ShardingCore.Extensions
{
public static class ShardingDbContextExtension
{
public static IShardingDbContextExecutor CreateShardingDbContextExecutor<TDbContext>(
this TDbContext shellDbContext)
where TDbContext:DbContext,IShardingDbContext
{
var shardingWrapOptionsExtension = shellDbContext.GetService<IDbContextOptions>().FindExtension<ShardingWrapOptionsExtension>();
if (shardingWrapOptionsExtension != null)
{
return new ShardingDbContextExecutor(shellDbContext);
}
return default;
}
public static bool IsUseReadWriteSeparation(this IShardingDbContext shardingDbContext)
{
return shardingDbContext.GetShardingExecutor().GetVirtualDataSource().UseReadWriteSeparation;
@ -39,7 +52,7 @@ namespace ShardingCore.Extensions
/// <returns></returns>
public static DbContext GetShareDbContext(this IShardingDbContext shardingDbContext,string dataSourceName,IRouteTail routeTail)
{
return shardingDbContext.GetDbContext(dataSourceName, CreateDbContextStrategyEnum.ShareConnection, routeTail);
return shardingDbContext.GetShardingExecutor().CreateDbContext(CreateDbContextStrategyEnum.ShareConnection,dataSourceName, routeTail);
}
/// <summary>
@ -51,7 +64,7 @@ namespace ShardingCore.Extensions
/// <returns></returns>
public static DbContext GetIndependentWriteDbContext(this IShardingDbContext shardingDbContext,string dataSourceName,IRouteTail routeTail)
{
return shardingDbContext.GetDbContext(dataSourceName, CreateDbContextStrategyEnum.IndependentConnectionWrite, routeTail);
return shardingDbContext.GetShardingExecutor().CreateDbContext(CreateDbContextStrategyEnum.IndependentConnectionWrite,dataSourceName, routeTail);
}
/// <summary>
/// 获取独立生命周期的读连接字符串的db context
@ -62,7 +75,7 @@ namespace ShardingCore.Extensions
/// <returns></returns>
public static DbContext GetIndependentQueryDbContext(this IShardingDbContext shardingDbContext,string dataSourceName,IRouteTail routeTail)
{
return shardingDbContext.GetDbContext(dataSourceName, CreateDbContextStrategyEnum.IndependentConnectionQuery, routeTail);
return shardingDbContext.GetShardingExecutor().CreateDbContext(CreateDbContextStrategyEnum.IndependentConnectionQuery,dataSourceName, routeTail);
}
}
}

View File

@ -108,7 +108,7 @@ namespace ShardingCore.Extensions
new Dictionary<DbContext, IEnumerable<TEntity>>()
{
{
shardingDbContext.CreateGenericDbContext(entitiesArray[0]),
shardingDbContext.GetShardingExecutor().CreateGenericDbContext(entitiesArray[0]),
entitiesArray
}
}

View File

@ -7,7 +7,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.EFCores;
using ShardingCore.Extensions;
namespace ShardingCore.Sharding
{
@ -23,391 +25,47 @@ namespace ShardingCore.Sharding
/// </summary>
public abstract class AbstractShardingDbContext : DbContext, IShardingDbContext
{
protected IShardingDbContextExecutor ShardingDbContextExecutor { get; }
public AbstractShardingDbContext(DbContextOptions options) : base(options)
{
var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
if (wrapOptionsExtension != null)
{
ShardingDbContextExecutor = new ShardingDbContextExecutor(this);
}
IsExecutor = wrapOptionsExtension == null;
}
private bool _createExecutor = false;
/// <summary>
/// 是否是真正的执行者
/// 构造函数
/// </summary>
public bool IsExecutor { get; }
public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail)
/// <param name="options"></param>
protected AbstractShardingDbContext(DbContextOptions options) : base(options)
{
return ShardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail);
}
/// <summary>
/// 根据对象创建通用的dbcontext
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public DbContext CreateGenericDbContext<TEntity>(TEntity entity) where TEntity : class
{
return ShardingDbContextExecutor.CreateGenericDbContext(entity);
}
private IShardingDbContextExecutor? _shardingDbContextExecutor;
public IShardingDbContextExecutor GetShardingExecutor()
{
return ShardingDbContextExecutor;
}
public override EntityEntry Add(object entity)
{
if (IsExecutor)
base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
public override EntityEntry<TEntity> Add<TEntity>(TEntity entity)
{
if (IsExecutor)
return base.Add(entity);
return CreateGenericDbContext(entity).Add(entity);
}
#if !EFCORE2
public override ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override ValueTask<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
#endif
#if EFCORE2
public override Task<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override Task<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
return base.AddAsync(entity, cancellationToken);
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
#endif
private Dictionary<DbContext, IEnumerable<TEntity>> AggregateToDic<TEntity>(IEnumerable<TEntity> entities) where TEntity:class
{
return entities.Select(o =>
if (!_createExecutor)
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext).ToDictionary(o => o.Key, o => o.Select(g => g.Entity));
}
public override void AddRange(params object[] entities)
{
if (IsExecutor)
{
base.AddRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.AddRange(aggregateKv.Value);
_shardingDbContextExecutor=this.CreateShardingDbContextExecutor();
_createExecutor = true;
}
return _shardingDbContextExecutor;
}
public override void AddRange(IEnumerable<object> entities)
{
if (IsExecutor)
{
base.AddRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.AddRange(aggregateKv.Value);
}
}
public override async Task AddRangeAsync(params object[] entities)
{
if (IsExecutor)
{
await base.AddRangeAsync(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.AddRangeAsync(aggregateKv.Value);
}
}
public override async Task AddRangeAsync(IEnumerable<object> entities, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
{
await base.AddRangeAsync(entities, cancellationToken);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
await aggregateKv.Key.AddRangeAsync(aggregateKv.Value,cancellationToken);
}
}
public override EntityEntry<TEntity> Attach<TEntity>(TEntity entity)
{
if (IsExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override EntityEntry Attach(object entity)
{
if (IsExecutor)
return base.Attach(entity);
return CreateGenericDbContext(entity).Attach(entity);
}
public override void AttachRange(params object[] entities)
{
if (IsExecutor)
{
base.AttachRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.AttachRange(aggregateKv.Value);
}
}
public override void AttachRange(IEnumerable<object> entities)
{
if (IsExecutor)
{
base.AttachRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.AttachRange(aggregateKv.Value);
}
}
//public override DatabaseFacade Database => _dbContextCaches.Any()
// ? _dbContextCaches.First().Value.Database
// : GetDbContext(true, string.Empty).Database;
public override EntityEntry<TEntity> Entry<TEntity>(TEntity entity)
{
if (IsExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry Entry(object entity)
{
if (IsExecutor)
return base.Entry(entity);
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
{
if (IsExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override EntityEntry Update(object entity)
{
if (IsExecutor)
return base.Update(entity);
return CreateGenericDbContext(entity).Update(entity);
}
public override void UpdateRange(params object[] entities)
{
if (IsExecutor)
{
base.UpdateRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.UpdateRange(aggregateKv.Value);
}
}
public override void UpdateRange(IEnumerable<object> entities)
{
if (IsExecutor)
{
base.UpdateRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.UpdateRange(aggregateKv.Value);
}
}
public override EntityEntry<TEntity> Remove<TEntity>(TEntity entity)
{
if (IsExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override EntityEntry Remove(object entity)
{
if (IsExecutor)
return base.Remove(entity);
return CreateGenericDbContext(entity).Remove(entity);
}
public override void RemoveRange(params object[] entities)
{
if (IsExecutor)
{
base.RemoveRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.RemoveRange(aggregateKv.Value);
}
}
public override void RemoveRange(IEnumerable<object> entities)
{
if (IsExecutor)
{
base.RemoveRange(entities);
return;
}
var aggregateToDic = AggregateToDic(entities);
foreach (var aggregateKv in aggregateToDic)
{
aggregateKv.Key.RemoveRange(aggregateKv.Value);
}
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
if (IsExecutor)
return base.SaveChanges(acceptAllChangesOnSuccess);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.AutoTransactionsEnabled&&Database.CurrentTransaction==null&&ShardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = Database.BeginTransaction())
{
i = ShardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
tran.Commit();
}
}
else
{
i = ShardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
if (IsExecutor)
return await base.SaveChangesAsync(acceptAllChangesOnSuccess,cancellationToken);
//ApplyShardingConcepts();
int i = 0;
//如果是内部开的事务就内部自己消化
if (Database.AutoTransactionsEnabled && Database.CurrentTransaction==null && ShardingDbContextExecutor.IsMultiDbContext)
{
using (var tran = await Database.BeginTransactionAsync(cancellationToken))
{
i = await ShardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
#if EFCORE2
tran.Commit();
#endif
#if !EFCORE2
await tran.CommitAsync(cancellationToken);
#endif
}
}
else
{
i = await ShardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
return i;
}
/// <summary>
/// 当前dbcontext是否是执行的dbcontext
/// </summary>
public bool IsExecutor => GetShardingExecutor() == default;
public override void Dispose()
{
if (IsExecutor)
{
base.Dispose();
}
else
{
ShardingDbContextExecutor.Dispose();
base.Dispose();
}
_shardingDbContextExecutor?.Dispose();
base.Dispose();
}
#if !EFCORE2
public override async ValueTask DisposeAsync()
{
if (IsExecutor)
if (_shardingDbContextExecutor!=null)
{
await base.DisposeAsync();
await _shardingDbContextExecutor.DisposeAsync();
}
else
{
await ShardingDbContextExecutor.DisposeAsync();
await base.DisposeAsync();
}
await base.DisposeAsync();
}
#endif
}

View File

@ -13,23 +13,6 @@ namespace ShardingCore.Sharding.Abstractions
*/
public interface IShardingDbContext
{
/// <summary>
/// create DbContext
/// </summary>
/// <param name="dataSourceName">data source</param>
/// <param name="strategy">生成db connection的策略,主要区别在于是否和主db connection一直或者是否需要缓存其connection还有是否是独立声明周期的区别</param>
/// <param name="routeTail"></param>
/// <returns></returns>
DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail);
/// <summary>
/// 创建通用的db context
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
DbContext CreateGenericDbContext<T>(T entity) where T : class;
IShardingDbContextExecutor GetShardingExecutor();

View File

@ -23,6 +23,22 @@ namespace ShardingCore.Sharding.Abstractions
#endif
{
/// <summary>
/// 使用对象创建db context的前执行
/// </summary>
event EventHandler<EntityCreateDbContextBeforeEventArgs> EntityCreateDbContextBefore;
/// <summary>
/// 使用对象创建db context的后执行
/// </summary>
event EventHandler<EntityCreateDbContextAfterEventArgs> EntityCreateDbContextAfter;
/// <summary>
/// 使用tail创建db context的前执行
/// </summary>
event EventHandler<CreateDbContextBeforeEventArgs> CreateDbContextBefore;
/// <summary>
/// 使用tail创建db context的后执行
/// </summary>
event EventHandler<CreateDbContextAfterEventArgs> CreateDbContextAfter;
/// <summary>
/// has multi db context
/// </summary>
@ -52,5 +68,7 @@ namespace ShardingCore.Sharding.Abstractions
int SaveChanges(bool acceptAllChangesOnSuccess);
DbContext GetShellDbContext();
}
}

View File

@ -0,0 +1,55 @@
using System;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
namespace ShardingCore.Sharding.Abstractions
{
public class EntityCreateDbContextBeforeEventArgs: EventArgs
{
public object Entity { get; }
public EntityCreateDbContextBeforeEventArgs(object entity)
{
Entity = entity;
}
}
public class CreateDbContextBeforeEventArgs: EventArgs
{
public CreateDbContextStrategyEnum Strategy { get; }
public string DataSourceName { get; }
public IRouteTail RouteTail { get; }
public CreateDbContextBeforeEventArgs(CreateDbContextStrategyEnum strategy, string dataSourceName, IRouteTail routeTail)
{
Strategy = strategy;
DataSourceName = dataSourceName;
RouteTail = routeTail;
}
}
public class CreateDbContextAfterEventArgs: EventArgs
{
public CreateDbContextStrategyEnum Strategy { get; }
public string DataSourceName { get; }
public IRouteTail RouteTail { get; }
public DbContext DbContext { get; }
public CreateDbContextAfterEventArgs(CreateDbContextStrategyEnum strategy, string dataSourceName, IRouteTail routeTail,DbContext dbContext)
{
Strategy = strategy;
DataSourceName = dataSourceName;
RouteTail = routeTail;
DbContext = dbContext;
}
}
public class EntityCreateDbContextAfterEventArgs: EventArgs
{
public object Entity { get; }
public DbContext DbContext { get; }
public EntityCreateDbContextAfterEventArgs(object entity,DbContext dbContext)
{
Entity = entity;
DbContext = dbContext;
}
}
}

View File

@ -37,10 +37,10 @@ namespace ShardingCore.Sharding.Enumerators.AggregateExtensions
}
else
{
var parameters = allProperties.Select(o => o.GetValue(source)).ToArray();
if (anonType.GetConstructors().Length == 1 &&
anonType.GetConstructors()[0].GetParameters().Length == allPropertyTypes.Length)
{
var parameters = allProperties.Select(o => o.GetValue(source)).ToArray();
return (TSource)Activator.CreateInstance(anonType, parameters);
}
else

View File

@ -35,6 +35,13 @@ namespace ShardingCore.Sharding.ShardingDbContextExecutors
/// <typeparam name="TShardingDbContext"></typeparam>
public class ShardingDbContextExecutor : IShardingDbContextExecutor
{
public event EventHandler<EntityCreateDbContextBeforeEventArgs> EntityCreateDbContextBefore;
public event EventHandler<EntityCreateDbContextAfterEventArgs> EntityCreateDbContextAfter;
public event EventHandler<CreateDbContextBeforeEventArgs> CreateDbContextBefore;
public event EventHandler<CreateDbContextAfterEventArgs> CreateDbContextAfter;
private readonly ILogger<ShardingDbContextExecutor> _logger;
private readonly DbContext _shardingDbContext;
@ -106,19 +113,29 @@ namespace ShardingCore.Sharding.ShardingDbContextExecutors
public DbContext CreateDbContext(CreateDbContextStrategyEnum strategy, string dataSourceName,
IRouteTail routeTail)
{
if (CreateDbContextBefore != null)
{
CreateDbContextBefore.Invoke(this,new CreateDbContextBeforeEventArgs(strategy,dataSourceName,routeTail));
}
DbContext dbContext;
if (CreateDbContextStrategyEnum.ShareConnection == strategy)
{
var dataSourceDbContext = GetDataSourceDbContext(dataSourceName);
return dataSourceDbContext.CreateDbContext(routeTail);
dbContext= dataSourceDbContext.CreateDbContext(routeTail);
}
else
{
var parallelDbContextOptions = CreateParallelDbContextOptions(dataSourceName, strategy);
var dbContext =
dbContext =
_dbContextCreator.CreateDbContext(_shardingDbContext, parallelDbContextOptions, routeTail);
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
return dbContext;
}
if (CreateDbContextAfter != null)
{
CreateDbContextAfter.Invoke(this,new CreateDbContextAfterEventArgs(strategy,dataSourceName,routeTail,dbContext));
}
return dbContext;
}
private DbContextOptions CreateParallelDbContextOptions(string dataSourceName,
@ -136,12 +153,25 @@ namespace ShardingCore.Sharding.ShardingDbContextExecutors
public DbContext CreateGenericDbContext<TEntity>(TEntity entity) where TEntity : class
{
if (EntityCreateDbContextBefore != null)
{
EntityCreateDbContextBefore.Invoke(this,new EntityCreateDbContextBeforeEventArgs(entity));
}
var realEntityType = _trackerManager.TranslateEntityType(entity.GetType());
var dataSourceName = GetDataSourceName(entity,realEntityType);
var tail = GetTableTail(dataSourceName, entity,realEntityType);
return CreateDbContext(CreateDbContextStrategyEnum.ShareConnection, dataSourceName,
var dbContext = CreateDbContext(CreateDbContextStrategyEnum.ShareConnection, dataSourceName,
_routeTailFactory.Create(tail));
if (EntityCreateDbContextAfter != null)
{
EntityCreateDbContextAfter.Invoke(this,new EntityCreateDbContextAfterEventArgs(entity,dbContext));
}
return dbContext;
}
public IVirtualDataSource GetVirtualDataSource()
@ -190,6 +220,11 @@ namespace ShardingCore.Sharding.ShardingDbContextExecutors
return i;
}
public DbContext GetShellDbContext()
{
return _shardingDbContext;
}
public void NotifyShardingTransaction()
{
foreach (var dbContextCache in _dbContextCaches)
@ -355,5 +390,7 @@ namespace ShardingCore.Sharding.ShardingDbContextExecutors
}
}
}
}
}

View File

@ -191,7 +191,7 @@ namespace ShardingCore.Sharding.ShardingExecutors
var strategy = !IsParallelQuery()
? CreateDbContextStrategyEnum.ShareConnection
: CreateDbContextStrategyEnum.IndependentConnectionQuery;
var dbContext = GetShardingDbContext().GetDbContext(sqlRouteUnit.DataSourceName,strategy , routeTailFactory.Create(sqlRouteUnit.TableRouteResult));
var dbContext = GetShardingDbContext().GetShardingExecutor().CreateDbContext(strategy,sqlRouteUnit.DataSourceName, routeTailFactory.Create(sqlRouteUnit.TableRouteResult));
_queryCompilerExecutor = new QueryCompilerExecutor(dbContext, GetQueryExpression());
}
}

View File

@ -175,7 +175,7 @@ namespace ShardingCore.Sharding.ShardingExecutors
var strategy = !IsParallelQuery()
? CreateDbContextStrategyEnum.ShareConnection
: CreateDbContextStrategyEnum.IndependentConnectionQuery;
var dbContext = _shardingDbContext.GetDbContext(virtualDataSource.DefaultDataSourceName, strategy, routeTailFactory.Create(string.Empty));
var dbContext = _shardingDbContext.GetShardingExecutor().CreateDbContext(strategy,virtualDataSource.DefaultDataSourceName, routeTailFactory.Create(string.Empty));
_queryCompilerExecutor = new QueryCompilerExecutor(dbContext, _queryExpression);
}
}

View File

@ -128,7 +128,7 @@ namespace ShardingCore.Sharding
{
var routeTail = _routeTailFactory.Create(sqlRouteUnit.TableRouteResult);
var dbContext = GetShardingDbContext().GetDbContext(sqlRouteUnit.DataSourceName, CreateDbContextStrategyEnum.IndependentConnectionQuery, routeTail);
var dbContext = GetShardingDbContext().GetShardingExecutor().CreateDbContext(CreateDbContextStrategyEnum.IndependentConnectionQuery,sqlRouteUnit.DataSourceName, routeTail);
_parallelDbContexts.TryAdd(dbContext, null);
return dbContext;

View File

@ -123,12 +123,11 @@ namespace ShardingCore
shardingConfigOptions.ShardingMigrationConfigure?.Invoke(dbContextOptionsBuilder);
var virtualDataSource = shardingRuntimeContext.GetVirtualDataSource();
var connectionString = virtualDataSource.GetConnectionString(virtualDataSource.DefaultDataSourceName);
var contextOptionsBuilder = virtualDataSource.ConfigurationParams
virtualDataSource.ConfigurationParams
.UseDbContextOptionsBuilder(connectionString, dbContextOptionsBuilder)
.UseShardingMigrator()
.UseSharding(shardingRuntimeContext);
virtualDataSource.ConfigurationParams.UseShellDbContextOptionBuilder(contextOptionsBuilder);
virtualDataSource.ConfigurationParams.UseShellDbContextOptionBuilder(dbContextOptionsBuilder);
return dbContextOptionsBuilder;
}
@ -210,18 +209,27 @@ namespace ShardingCore
return services;
}
#pragma warning disable EF1001
/// <summary>
///
/// </summary>
/// <param name="optionsBuilder"></param>
/// <param name="shardingRuntimeContext"></param>
/// <returns></returns>
public static DbContextOptionsBuilder UseSharding(
this DbContextOptionsBuilder optionsBuilder, IShardingRuntimeContext shardingRuntimeContext)
{
return optionsBuilder.UseShardingWrapMark().UseShardingOptions(shardingRuntimeContext)
.ReplaceService<IDbSetSource, ShardingDbSetSource>()
return optionsBuilder
.UseShardingWrapMark()
.UseShardingMigrator()
.UseShardingOptions(shardingRuntimeContext)
.ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
.ReplaceService<IChangeTrackerFactory, ShardingChangeTrackerFactory>()
.ReplaceService<IDbContextTransactionManager,
ShardingRelationalTransactionManager>()
.ReplaceService<IRelationalTransactionFactory,
ShardingRelationalTransactionFactory>();
.ReplaceService<IDbContextTransactionManager,ShardingRelationalTransactionManager>()
.ReplaceService<IStateManager,ShardingStateManager>()
.ReplaceService<IRelationalTransactionFactory,ShardingRelationalTransactionFactory>();
}
#pragma warning restore EF1001
public static DbContextOptionsBuilder UseShardingMigrator(
this DbContextOptionsBuilder optionsBuilder)
@ -246,12 +254,10 @@ namespace ShardingCore
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(shardingWrapExtension);
return optionsBuilder;
}
private static ShardingWrapOptionsExtension CreateOrGetShardingWrapExtension(
this DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.Options.FindExtension<ShardingWrapOptionsExtension>() ??
new ShardingWrapOptionsExtension();
private static ShardingOptionsExtension CreateOrGetShardingOptionsExtension(
this DbContextOptionsBuilder optionsBuilder, IShardingRuntimeContext shardingRuntimeContext) =>
optionsBuilder.Options.FindExtension<ShardingOptionsExtension>() ??

View File

@ -27,6 +27,9 @@
<Compile Include="..\..\src\ShardingCore\**\*.cs" />
<Compile Remove="..\..\src\ShardingCore\obj\**" />
<Compile Remove="..\..\src\ShardingCore\bin\**" />
<Compile Update="..\..\src\ShardingCore\EFCores\EFCore6x\ShardingStateManager.cs">
<Link>EFCores\EFCore6x\ShardingStateManager.cs</Link>
</Compile>
</ItemGroup>

View File

@ -1,10 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />