diff --git a/samples/Sample.SqlServer/Sample.SqlServer.csproj b/samples/Sample.SqlServer/Sample.SqlServer.csproj index 62930223..e0e67850 100644 --- a/samples/Sample.SqlServer/Sample.SqlServer.csproj +++ b/samples/Sample.SqlServer/Sample.SqlServer.csproj @@ -6,11 +6,11 @@ - + - + diff --git a/samples/Sample.SqlServer/Startup.cs b/samples/Sample.SqlServer/Startup.cs index fa80beec..cd4403b5 100644 --- a/samples/Sample.SqlServer/Startup.cs +++ b/samples/Sample.SqlServer/Startup.cs @@ -21,10 +21,10 @@ namespace Sample.SqlServer public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - services.AddDbContext(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDB;Integrated Security=True")); + //services.AddDbContext(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx3;Integrated Security=True")); - services.AddShardingDbContext(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx;Integrated Security=True;MultipleActiveResultSets=True;") + services.AddShardingDbContext(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx3;Integrated Security=True;MultipleActiveResultSets=True;") ,op => { op.EnsureCreatedWithOutShardingTable = true; diff --git a/src/ShardingCore/EFCores/ShardingDbSetSource.cs b/src/ShardingCore/EFCores/ShardingDbSetSource.cs index aa833350..ff04c0df 100644 --- a/src/ShardingCore/EFCores/ShardingDbSetSource.cs +++ b/src/ShardingCore/EFCores/ShardingDbSetSource.cs @@ -12,8 +12,11 @@ namespace ShardingCore.EFCores * @Date: Saturday, 14 August 2021 10:17:43 * @Email: 326308290@qq.com */ +#if !EFCORE2 public class ShardingDbSetSource:IDbSetSource { +#if EFCORE5 + private static readonly MethodInfo _genericCreateSet = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); @@ -41,12 +44,90 @@ namespace ShardingCore.EFCores private object CreateCore(DbContext context, Type type, string name, MethodInfo createMethod) => _cache.GetOrAdd( (type, name), - t => (Func)createMethod + t => (Func)createMethod .MakeGenericMethod(t.Type) .Invoke(null, null))(context, name); private static Func CreateSetFactory() where TEntity : class => (c, name) => new ShardingInternalDbSet(c, name); +#endif +#if EFCORE3 + + private static readonly MethodInfo _genericCreateSet + = typeof(ShardingDbSetSource).GetTypeInfo().GetDeclaredMethod(nameof(CreateSetFactory)); + + private readonly ConcurrentDictionary> _cache + = new ConcurrentDictionary>(); + + /// + /// 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. + /// + 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)createMethod + .MakeGenericMethod(t) + .Invoke(null, null))(context); + + private static Func CreateSetFactory() + where TEntity : class + => c => new ShardingInternalDbSet(c); +#endif } +#endif + +#if EFCORE2 + + /// + /// 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. + /// + 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> _cache + = new ConcurrentDictionary>(); + + /// + /// 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. + /// + public virtual object Create(DbContext context, Type type) + => CreateCore(context, type, _genericCreateSet); + + /// + /// 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. + /// + 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)createMethod + .MakeGenericMethod(t) + .Invoke(null, null))(context); + + private static Func CreateSetFactory() + where TEntity : class + => c => new ShardingInternalDbSet(c); + + private static Func> CreateQueryFactory() + where TQuery : class + => c => new ShardingInternalDbQuery(c); + } +#endif } \ No newline at end of file diff --git a/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs b/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs new file mode 100644 index 00000000..c6481315 --- /dev/null +++ b/src/ShardingCore/EFCores/ShardingInternalDbQuery.cs @@ -0,0 +1,25 @@ +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 + */ +#if EFCORE2 + + public class ShardingInternalDbQuery : InternalDbQuery where TQuery : class + { + public ShardingInternalDbQuery(DbContext context) : base(context) + { + } + } +#endif +} diff --git a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs b/src/ShardingCore/EFCores/ShardingInternalDbSet.cs index bf5cac77..6f191d41 100644 --- a/src/ShardingCore/EFCores/ShardingInternalDbSet.cs +++ b/src/ShardingCore/EFCores/ShardingInternalDbSet.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; + +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Internal; using ShardingCore.Core; @@ -17,15 +18,25 @@ namespace ShardingCore.EFCores * @Ver: 1.0 * @Email: 326308290@qq.com */ - public class ShardingInternalDbSet :InternalDbSet + + public class ShardingInternalDbSet : InternalDbSet where TEntity : class { private readonly DbContext _context; +#if EFCORE5 public ShardingInternalDbSet(DbContext context, string entityTypeName) : base(context, entityTypeName) { _context = context; } +#endif +#if !EFCORE5 + + public ShardingInternalDbSet(DbContext context) : base(context) + { + _context = context; + } +#endif /// /// 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 @@ -34,7 +45,7 @@ namespace ShardingCore.EFCores /// public override EntityEntry Add(TEntity entity) { - var genericDbContext = ((IShardingDbContext) _context).CreateGenericDbContext(entity); + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); return genericDbContext.Add(entity); } @@ -44,14 +55,24 @@ namespace ShardingCore.EFCores /// 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. /// +#if !EFCORE2 public override async ValueTask> AddAsync( TEntity entity, CancellationToken cancellationToken = default) { - var genericDbContext = ((IShardingDbContext) _context).CreateGenericDbContext(entity); + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); return await genericDbContext.AddAsync(entity, cancellationToken); } +#endif +#if EFCORE2 + public override async Task> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) + { + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); + return await genericDbContext.AddAsync(entity, cancellationToken); + } + +#endif /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -61,7 +82,7 @@ namespace ShardingCore.EFCores /// public override EntityEntry Attach(TEntity entity) { - var genericDbContext = ((IShardingDbContext) _context).CreateGenericDbContext(entity); + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); return genericDbContext.Attach(entity); } @@ -75,7 +96,7 @@ namespace ShardingCore.EFCores { Check.NotNull(entity, nameof(entity)); - var genericDbContext = ((IShardingDbContext) _context).CreateGenericDbContext(entity); + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); return genericDbContext.Remove(entity); } @@ -87,7 +108,7 @@ namespace ShardingCore.EFCores /// public override EntityEntry Update(TEntity entity) { - var genericDbContext = ((IShardingDbContext) _context).CreateGenericDbContext(entity); + var genericDbContext = ((IShardingDbContext)_context).CreateGenericDbContext(entity); return genericDbContext.Update(entity); } @@ -264,7 +285,7 @@ namespace ShardingCore.EFCores foreach (var group in groups) { - await group.Key.AddRangeAsync(group.Select(o => o.Entity)); + await group.Key.AddRangeAsync(group.Select(o => o.Entity)); } } @@ -276,7 +297,7 @@ namespace ShardingCore.EFCores /// public override void AttachRange(IEnumerable entities) { - + var groups = entities.Select(o => { var dbContext = ((IShardingDbContext)_context).CreateGenericDbContext(o); @@ -289,7 +310,7 @@ namespace ShardingCore.EFCores foreach (var group in groups) { - group.Key.AttachRange(group.Select(o => o.Entity)); + group.Key.AttachRange(group.Select(o => o.Entity)); } } @@ -346,4 +367,5 @@ namespace ShardingCore.EFCores } } -} + +} \ No newline at end of file diff --git a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs index 0bb39f55..227d8135 100644 --- a/src/ShardingCore/Sharding/AbstractShardingDbContext.cs +++ b/src/ShardingCore/Sharding/AbstractShardingDbContext.cs @@ -91,7 +91,7 @@ namespace ShardingCore.Sharding { if (!_dbContextCaches.TryGetValue(tail, out var dbContext)) { - dbContext = _shardingDbContextFactory.Create(ShardingDbContextType,CreateSameShardingDbContextOptions(tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail)); + dbContext = _shardingDbContextFactory.Create(ShardingDbContextType, CreateSameShardingDbContextOptions(tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail)); _dbContextCaches.TryAdd(tail, dbContext); } @@ -105,7 +105,7 @@ namespace ShardingCore.Sharding 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]; + var physicTable = _virtualTableManager.GetVirtualTable(ShardingDbContextType, entity.GetType()).RouteTo(new TableRouteConfig(null, entity as IShardingTable, null))[0]; tail = physicTable.Tail; } @@ -134,6 +134,8 @@ namespace ShardingCore.Sharding return CreateGenericDbContext(entity).Add(entity); } +#if !EFCORE2 + public override ValueTask> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) { return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); @@ -143,6 +145,19 @@ namespace ShardingCore.Sharding { return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); } +#endif +#if EFCORE2 + + public override Task> AddAsync(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) + { + return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); + } + + public override Task AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken()) + { + return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken); + } +#endif public override void AddRange(params object[] entities) { @@ -443,6 +458,8 @@ namespace ShardingCore.Sharding } return i; } +#if !EFCORE2 + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { @@ -481,6 +498,49 @@ namespace ShardingCore.Sharding } return i; } +#endif +#if EFCORE2 + + + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + + var isBeginTransaction = IsBeginTransaction; + //如果是内部开的事务就内部自己消化 + if (!isBeginTransaction) + { + await Database.BeginTransactionAsync(cancellationToken); + } + int i = 0; + + try + { + + foreach (var dbContextCache in _dbContextCaches) + { + dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); + i += await dbContextCache.Value.SaveChangesAsync(cancellationToken); + } + if (!isBeginTransaction) + Database.CurrentTransaction.Commit(); + } + finally + { + if (!isBeginTransaction) { } + if (Database.CurrentTransaction != null) + { + Database.CurrentTransaction.Dispose(); + foreach (var dbContextCache in _dbContextCaches) + { + dbContextCache.Value.Database.UseTransaction(null); + } + } + + } + return i; + } +#endif +#if !EFCORE2 public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()) { @@ -520,6 +580,48 @@ namespace ShardingCore.Sharding } return i; } +#endif +#if EFCORE2 + + public override async Task 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) + { + dbContextCache.Value.Database.UseTransaction(Database.CurrentTransaction.GetDbTransaction()); + i += await dbContextCache.Value.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } + if (!isBeginTransaction) + Database.CurrentTransaction.Commit(cancellationToken); + } + finally + { + if (!isBeginTransaction) + if (Database.CurrentTransaction != null) + { + Database.CurrentTransaction.Dispose(); + + foreach (var dbContextCache in _dbContextCaches) + { + dbContextCache.Value.Database.UseTransaction(null); + } + } + + } + return i; + } +#endif public override void Dispose() { @@ -537,6 +639,7 @@ namespace ShardingCore.Sharding base.Dispose(); } +#if !EFCORE2 public override async ValueTask DisposeAsync() { foreach (var dbContextCache in _dbContextCaches) @@ -553,6 +656,7 @@ namespace ShardingCore.Sharding await base.DisposeAsync(); } +#endif } } \ No newline at end of file diff --git a/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs b/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs index 96672780..8357cb33 100644 --- a/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs +++ b/src/ShardingCore/Sharding/StreamMergeEngines/AsyncEnumerableStreamMergeEngine.cs @@ -11,6 +11,10 @@ using ShardingCore.Sharding.Enumerators; using ShardingCore.Sharding.Enumerators.StreamMergeAsync; using ShardingCore.Sharding.Enumerators.StreamMergeSync; +#if EFCORE2 +using Microsoft.EntityFrameworkCore.Extensions.Internal; +#endif + namespace ShardingCore.Sharding.StreamMergeEngines { /* @@ -19,7 +23,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines * @Date: Saturday, 14 August 2021 22:07:28 * @Email: 326308290@qq.com */ - public class AsyncEnumerableStreamMergeEngine :IAsyncEnumerable,IEnumerable + public class AsyncEnumerableStreamMergeEngine : IAsyncEnumerable, IEnumerable { private readonly StreamMergeContext _mergeContext; @@ -29,59 +33,8 @@ namespace ShardingCore.Sharding.StreamMergeEngines _mergeContext = mergeContext; } - //public static IStreamMergeEngine Create(StreamMergeContext mergeContext) - //{ - // return new AsyncEnumerableStreamMergeEngine(mergeContext); - //} - //public async Task> GetAsyncEnumerator() - //{ - // var tableResult = _mergeContext.GetRouteResults(); - // var enumeratorTasks = tableResult.Select(routeResult => - // { - // if (routeResult.ReplaceTables.Count > 1) - // throw new ShardingCoreException("route found more than 1 table name s"); - // var tail = string.Empty; - // if (routeResult.ReplaceTables.Count == 1) - // tail = routeResult.ReplaceTables.First().Tail; - - // return Task.Run(async () => - // { - // try - // { - // //using (var scope = _mergeContext.CreateScope()) - // //{ - // //var shardingContext = ShardingContext.Create(routeResult); - // //scope.ShardingAccessor.ShardingContext = shardingContext; - - // var shardingDbContext = _mergeContext.CreateDbContext(tail); - // var newQueryable = (IQueryable) _mergeContext.GetReWriteQueryable() - // .ReplaceDbContextQueryable(shardingDbContext); - - // var asyncEnumerator = await GetAsyncEnumerator(newQueryable); - // return new StreamMergeAsyncEnumerator(asyncEnumerator); - // //} - // } - // catch (Exception e) - // { - // Console.WriteLine(e); - // throw; - // } - // }); - // }).ToArray(); - - // var streamEnumerators = await Task.WhenAll(enumeratorTasks); - // if (_mergeContext.HasSkipTake()) - // return new PaginationStreamMergeAsyncEnumerator(_mergeContext, streamEnumerators); - // if (_mergeContext.HasGroupQuery()) - // return new MultiAggregateOrderStreamMergeAsyncEnumerator(_mergeContext, streamEnumerators); - // return new MultiOrderStreamMergeAsyncEnumerator(_mergeContext, streamEnumerators); - //} - //public IStreamMergeEnumerator GetEnumerator() - //{ - // throw new NotImplementedException(); - //} - +#if !EFCORE2 private async Task> GetAsyncEnumerator(IQueryable newQueryable) { @@ -89,8 +42,33 @@ namespace ShardingCore.Sharding.StreamMergeEngines await enumator.MoveNextAsync(); return enumator; } +#endif +#if EFCORE2 + + private async Task> GetAsyncEnumerator(IQueryable newQueryable) + { + var enumator = newQueryable.AsAsyncEnumerable().GetEnumerator(); + await enumator.MoveNext(); + return enumator; + } +#endif +#if !EFCORE2 public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken()) + { + return GetShardingEnumerator(); + } +#endif + +#if EFCORE2 + + IAsyncEnumerator IAsyncEnumerable.GetEnumerator() + { + return GetShardingEnumerator(); + } +#endif + + private IAsyncEnumerator GetShardingEnumerator() { var tableResult = _mergeContext.GetRouteResults(); var enumeratorTasks = tableResult.Select(routeResult => @@ -105,18 +83,12 @@ namespace ShardingCore.Sharding.StreamMergeEngines { try { - //using (var scope = _mergeContext.CreateScope()) - //{ - //var shardingContext = ShardingContext.Create(routeResult); - //scope.ShardingAccessor.ShardingContext = shardingContext; - var shardingDbContext = _mergeContext.CreateDbContext(tail); var newQueryable = (IQueryable)_mergeContext.GetReWriteQueryable() - .ReplaceDbContextQueryable(shardingDbContext); + .ReplaceDbContextQueryable(shardingDbContext); var asyncEnumerator = await GetAsyncEnumerator(newQueryable); return new StreamMergeAsyncEnumerator(asyncEnumerator); - //} } catch (Exception e) { @@ -126,7 +98,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines }); }).ToArray(); - var streamEnumerators = Task.WhenAll(enumeratorTasks).GetAwaiter().GetResult(); + var streamEnumerators = Task.WhenAll(enumeratorTasks).GetAwaiter().GetResult(); if (_mergeContext.HasSkipTake()) return new PaginationStreamMergeAsyncEnumerator(_mergeContext, streamEnumerators); if (_mergeContext.HasGroupQuery()) @@ -134,10 +106,11 @@ namespace ShardingCore.Sharding.StreamMergeEngines return new MultiOrderStreamMergeAsyncEnumerator(_mergeContext, streamEnumerators); } - private IEnumerator GetEnumerator(IQueryable newQueryable) + + private IEnumerator GetEnumerator(IQueryable newQueryable) { var enumator = newQueryable.AsEnumerable().GetEnumerator(); - enumator.MoveNext(); + enumator.MoveNext(); return enumator; } public IEnumerator GetEnumerator() @@ -151,29 +124,24 @@ namespace ShardingCore.Sharding.StreamMergeEngines if (routeResult.ReplaceTables.Count == 1) tail = routeResult.ReplaceTables.First().Tail; - return Task.Run( () => - { - try - { - //using (var scope = _mergeContext.CreateScope()) - //{ - //var shardingContext = ShardingContext.Create(routeResult); - //scope.ShardingAccessor.ShardingContext = shardingContext; + return Task.Run(() => + { + try + { - var shardingDbContext = _mergeContext.CreateDbContext(tail); - var newQueryable = (IQueryable)_mergeContext.GetReWriteQueryable() - .ReplaceDbContextQueryable(shardingDbContext); + var shardingDbContext = _mergeContext.CreateDbContext(tail); + var newQueryable = (IQueryable)_mergeContext.GetReWriteQueryable() + .ReplaceDbContextQueryable(shardingDbContext); - var enumerator = GetEnumerator(newQueryable); - return new StreamMergeEnumerator(enumerator); - //} - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - }); + var enumerator = GetEnumerator(newQueryable); + return new StreamMergeEnumerator(enumerator); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + }); }).ToArray(); var streamEnumerators = Task.WhenAll(enumeratorTasks).GetAwaiter().GetResult(); @@ -188,5 +156,6 @@ namespace ShardingCore.Sharding.StreamMergeEngines { return GetEnumerator(); } + } } diff --git a/src/ShardingCore/ShardingCore.csproj b/src/ShardingCore/ShardingCore.csproj index c5b47284..3525589b 100644 --- a/src/ShardingCore/ShardingCore.csproj +++ b/src/ShardingCore/ShardingCore.csproj @@ -16,6 +16,5 @@ - diff --git a/src2x/ShardingCore.2x/ShardingCore.2x.csproj b/src2x/ShardingCore.2x/ShardingCore.2x.csproj index 74f0df2a..e9e4ea4f 100644 --- a/src2x/ShardingCore.2x/ShardingCore.2x.csproj +++ b/src2x/ShardingCore.2x/ShardingCore.2x.csproj @@ -25,7 +25,6 @@ - diff --git a/src3x/ShardingCore.3x/ShardingCore.3x.csproj b/src3x/ShardingCore.3x/ShardingCore.3x.csproj index 4de4b67a..5d97b635 100644 --- a/src3x/ShardingCore.3x/ShardingCore.3x.csproj +++ b/src3x/ShardingCore.3x/ShardingCore.3x.csproj @@ -28,9 +28,8 @@ - - - + +