This commit is contained in:
xuejiaming 2021-08-21 13:20:35 +08:00
parent 7aa0418763
commit 7f6a2f0ed1
10 changed files with 757 additions and 0 deletions

View File

@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.MySql", "samples\Sam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.SqlServer3x", "samples\Sample.SqlServer3x\Sample.SqlServer3x.csproj", "{447D5357-F095-45DE-9DA5-2D9997237366}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.AbpSharding", "samples\Samples.AbpSharding\Samples.AbpSharding.csproj", "{1136B8C9-3539-42FA-97FD-CAA6F146FCF0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -73,6 +75,10 @@ Global
{447D5357-F095-45DE-9DA5-2D9997237366}.Debug|Any CPU.Build.0 = Debug|Any CPU
{447D5357-F095-45DE-9DA5-2D9997237366}.Release|Any CPU.ActiveCfg = Release|Any CPU
{447D5357-F095-45DE-9DA5-2D9997237366}.Release|Any CPU.Build.0 = Release|Any CPU
{1136B8C9-3539-42FA-97FD-CAA6F146FCF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1136B8C9-3539-42FA-97FD-CAA6F146FCF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1136B8C9-3539-42FA-97FD-CAA6F146FCF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1136B8C9-3539-42FA-97FD-CAA6F146FCF0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -87,6 +93,7 @@ Global
{C34FCF48-1A98-4268-BFEE-6C9BFC7FD539} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
{90675788-D5C3-415A-9C18-FF159A75B4D5} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
{447D5357-F095-45DE-9DA5-2D9997237366} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
{1136B8C9-3539-42FA-97FD-CAA6F146FCF0} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291}

View File

@ -0,0 +1,556 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Abp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore;
using ShardingCore.Core;
using ShardingCore.Core.VirtualRoutes.TableRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
namespace Samples.AbpSharding
{
public abstract class AbstractShardingAbpDbContext<T> : AbpDbContext, IShardingTableDbContext<T> where T : DbContext, IShardingTableDbContext
{
private readonly string EMPTY_SHARDING_TAIL_ID = ShardingConstant.EMPTY_SHARDING_TAIL_ID + Guid.NewGuid().ToString("n");
private readonly ConcurrentDictionary<string, DbContext> _dbContextCaches = new ConcurrentDictionary<string, DbContext>();
private readonly IVirtualTableManager _virtualTableManager;
private readonly IShardingDbContextFactory _shardingDbContextFactory;
private readonly IShardingDbContextOptionsBuilderConfig _shardingDbContextOptionsBuilderConfig;
private DbContextOptions<T> _dbContextOptions;
private readonly object CREATELOCK = new object();
public AbstractShardingAbpDbContext(DbContextOptions options) : base(options)
{
_shardingDbContextFactory = ShardingContainer.GetService<IShardingDbContextFactory>();
_virtualTableManager = ShardingContainer.GetService<IVirtualTableManager>();
_shardingDbContextOptionsBuilderConfig = ShardingContainer
.GetService<IEnumerable<IShardingDbContextOptionsBuilderConfig>>()
.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType);
}
public abstract Type ShardingDbContextType { get; }
public Type ActualDbContextType => typeof(T);
private DbContextOptionsBuilder<T> CreateDbContextOptionBuilder()
{
Type type = typeof(DbContextOptionsBuilder<>);
type = type.MakeGenericType(ActualDbContextType);
return (DbContextOptionsBuilder<T>)Activator.CreateInstance(type);
}
private DbContextOptions<T> CreateShareDbContextOptions()
{
var dbContextOptionBuilder = CreateDbContextOptionBuilder();
var dbConnection = Database.GetDbConnection();
_shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(dbConnection, dbContextOptionBuilder);
return dbContextOptionBuilder.Options;
}
private DbContextOptions<T> CreateMonopolyDbContextOptions()
{
var dbContextOptionBuilder = CreateDbContextOptionBuilder();
_shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(dbContextOptionBuilder);
return dbContextOptionBuilder.Options;
}
private ShardingDbContextOptions GetShareShardingDbContextOptions(string tail)
{
if (_dbContextOptions == null)
{
lock (CREATELOCK)
{
if (_dbContextOptions == null)
{
_dbContextOptions = CreateShareDbContextOptions();
}
}
}
return new ShardingDbContextOptions(_dbContextOptions, tail);
}
private ShardingDbContextOptions CetMonopolyShardingDbContextOptions(string tail)
{
return new ShardingDbContextOptions(CreateMonopolyDbContextOptions(), tail);
}
public DbContext GetDbContext(bool track, string tail)
{
if (track)
{
if (!_dbContextCaches.TryGetValue(tail, out var dbContext))
{
dbContext = _shardingDbContextFactory.Create(ShardingDbContextType, GetShareShardingDbContextOptions(tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail));
_dbContextCaches.TryAdd(tail, dbContext);
}
return dbContext;
}
else
{
return _shardingDbContextFactory.Create(ShardingDbContextType, CetMonopolyShardingDbContextOptions(tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail));
}
}
public bool IsBeginTransaction => Database.CurrentTransaction != null;
public DbContext CreateGenericDbContext<T>(T entity) where T : class
{
var tail = EMPTY_SHARDING_TAIL_ID;
if (entity.IsShardingTable())
{
var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, entity.GetType()).RouteTo(new TableRouteConfig(null, entity as IShardingTable, null))[0];
tail = physicTable.Tail;
}
return GetDbContext(true, tail);
}
public override EntityEntry Add(object entity)
{
return CreateGenericDbContext(entity).Add(entity);
}
public override EntityEntry<TEntity> Add<TEntity>(TEntity entity)
{
return CreateGenericDbContext(entity).Add(entity);
}
public override ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
{
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override ValueTask<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
{
return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
}
public override void AddRange(params object[] entities)
{
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)
{
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)
{
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())
{
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)
{
return CreateGenericDbContext(entity).Attach(entity);
}
public override EntityEntry Attach(object entity)
{
return CreateGenericDbContext(entity).Attach(entity);
}
public override void AttachRange(params object[] entities)
{
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)
{
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)
{
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry Entry(object entity)
{
return CreateGenericDbContext(entity).Entry(entity);
}
public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
{
return CreateGenericDbContext(entity).Update(entity);
}
public override EntityEntry Update(object entity)
{
return CreateGenericDbContext(entity).Update(entity);
}
public override void UpdateRange(params object[] entities)
{
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)
{
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)
{
return CreateGenericDbContext(entity).Remove(entity);
}
public override EntityEntry Remove(object entity)
{
return CreateGenericDbContext(entity).Remove(entity);
}
public override void RemoveRange(params object[] entities)
{
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)
{
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()
{
var isBeginTransaction = IsBeginTransaction;
//如果是内部开的事务就内部自己消化
if (!isBeginTransaction)
{
Database.BeginTransaction();
}
int i = 0;
try
{
foreach (var dbContextCache in _dbContextCaches)
{
dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction());
i += dbContextCache.Value.SaveChanges();
}
if (!isBeginTransaction)
Database.CurrentTransaction.Commit();
}
finally
{
if (!isBeginTransaction)
{
Database.CurrentTransaction?.Dispose();
foreach (var dbContextCache in _dbContextCaches)
{
dbContextCache.Value.Database.UseTransaction(null);
}
}
}
return i;
}
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
var isBeginTransaction = IsBeginTransaction;
//如果是内部开的事务就内部自己消化
if (!isBeginTransaction)
{
Database.BeginTransaction();
}
int i = 0;
try
{
foreach (var dbContextCache in _dbContextCaches)
{
dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction());
i += dbContextCache.Value.SaveChanges(acceptAllChangesOnSuccess);
}
if (!isBeginTransaction)
Database.CurrentTransaction.Commit();
}
finally
{
if (!isBeginTransaction)
{
Database.CurrentTransaction?.Dispose();
foreach (var dbContextCache in _dbContextCaches)
{
dbContextCache.Value.Database.UseTransaction(null);
}
}
}
return i;
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
var isBeginTransaction = IsBeginTransaction;
//如果是内部开的事务就内部自己消化
if (!isBeginTransaction)
{
await Database.BeginTransactionAsync(cancellationToken);
}
int i = 0;
try
{
foreach (var dbContextCache in _dbContextCaches)
{
await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken);
i += await dbContextCache.Value.SaveChangesAsync(cancellationToken);
}
if (!isBeginTransaction)
await Database.CurrentTransaction.CommitAsync(cancellationToken);
}
finally
{
if (!isBeginTransaction)
{
}
if (Database.CurrentTransaction != null)
{
await Database.CurrentTransaction.DisposeAsync();
foreach (var dbContextCache in _dbContextCaches)
{
await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken);
}
}
}
return i;
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
var isBeginTransaction = IsBeginTransaction;
//如果是内部开的事务就内部自己消化
if (!isBeginTransaction)
{
await Database.BeginTransactionAsync(cancellationToken);
}
int i = 0;
try
{
foreach (var dbContextCache in _dbContextCaches)
{
await dbContextCache.Value.Database.UseTransactionAsync(Database.CurrentTransaction.GetDbTransaction(), cancellationToken: cancellationToken);
i += await dbContextCache.Value.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
if (!isBeginTransaction)
await Database.CurrentTransaction.CommitAsync(cancellationToken);
}
finally
{
if (!isBeginTransaction)
if (Database.CurrentTransaction != null)
{
await Database.CurrentTransaction.DisposeAsync();
foreach (var dbContextCache in _dbContextCaches)
{
await dbContextCache.Value.Database.UseTransactionAsync(null, cancellationToken: cancellationToken);
}
}
}
return i;
}
public override void Dispose()
{
foreach (var dbContextCache in _dbContextCaches)
{
try
{
dbContextCache.Value.Dispose();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
base.Dispose();
}
public override async ValueTask DisposeAsync()
{
foreach (var dbContextCache in _dbContextCaches)
{
try
{
await dbContextCache.Value.DisposeAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
await base.DisposeAsync();
}
}
}

View File

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Samples.AbpSharding.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Samples.AbpSharding
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:10431",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Samples.AbpSharding": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Abp.EntityFrameworkCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ShardingCore\ShardingCore.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Samples.AbpSharding
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace Samples.AbpSharding
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}