Open Closed

ABP pro 9.3.1 中使用ShardCore 遇到创建分表无法获取到实体LogicTableName #10660


User avatar
0
2250368121 created
  • Steps to reproduce the issue: 我在目前框架ABP pro 9.3.1 中想使用ShardingCore 来进行分表 当我配置好后运行发现 自动创建分表始终无法获取表的 LogicTableName table :_202605 maybe created. System.ArgumentNullException: Value cannot be null. (Parameter 'QRSPC.SpcService.Entities.SpcAutoAnalyses.SpcAutoAnalyse: not found logic table name。') 一下我分享我的ShardingCore 配置:是否ABP有限制导致shardingCore 拿不到实体的LogicTableName?
Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

13 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    ShardingCore 是第三方库,和 ABP 的 AbpDbContext 在 DI 注册和 DbContextOptions 构造上有几处时序冲突,我这边帮你定位并跑通了"启动建表 + 分表 INSERT/SELECT"的基本场景,下面给一份方案你可以照着试试。需要提醒的是,第三方库与 ABP 深度集成的场景有可能我们后续也很难持续支持——比如 ABP 多租户连接串切换、事件 Inbox/Outbox、IUnitOfWork 嵌套事务等情况,遇到时可能需要你自己根据 ShardingCore 行为再做调整。

    根因

    你的报错 not found logic table name 来自 ShardingCore.EFCores.ShardingModelCustomizer.MappingToTable —— 它读 EntityMetadata.LogicTableName 拿到空字符串。再往上追:

    • LogicTableName 只在 ShardingDbSetInitializer.InitializeSets 内、并且 context.IsShellDbContext() == true 时才会被填充。
    • IsShellDbContext() 判断的是 DbContextOptions 里有没有 ShardingWrapOptionsExtension
    • 这个扩展只会通过 UseSharding(...) 加进去,而 services.AddShardingDbContext<T>() 内部用的是 EF Core 的 AddDbContext(TryAdd 注册 DbContextOptions<T>)。
    • ABP 抢先用 services.TryAddTransient(DbContextOptionsFactory.Create<TDbContext>) 注册了 DbContextOptions<TDbContext>,所以 ShardingCore 这边的 TryAdd 被跳过,最终 DbContextOptions<AppDbContext> 是 ABP 工厂产出的,不含 ShardingCore 任何扩展
    • IsShellDbContext() == false → metadata 永远不被初始化 → LogicTableName 永远是空。

    所以你日志里 table :_202605 maybe created. 前面空白就是 LogicTableName 没拿到。

    兼容方案(两处改造)

    改造 1:让 ABP 工厂产出的 DbContextOptions 也加 UseSharding

    在你的 ABP 模块 ConfigureServices 里:

    Configure<AbpDbContextOptions>(opts =>
    {
        opts.PreConfigure<SpcServiceDbContext>(ctx =>
        {
            var src = ctx.ServiceProvider
                .GetRequiredService<IShardingRuntimeContext<SpcServiceDbContext>>();
            ctx.DbContextOptions.UseSharding(src);   // 关键:补上 ShardingWrapOptionsExtension、ReplaceService<IDbSetInitializer> 等
        });
    
        opts.UseSqlServer();
    });
    

    只做这一步,LogicTableName 就能正确填充、启动建表能成功;但你写数据时还会撞下一个错(AbpDbContext.SaveChangesAsyncAbpEfCoreNavigationHelper NRE),原因是 ShardingCore 反射 new 内部分表 DbContext 时绕开了 ABP 属性注入。需要改造 2。

    改造 2:替换 ShardingCore 的 IDbContextCreator

    让分表内部 DbContext 走 ABP DI 构造,并把 LazyServiceProvider 手动补回去:

    public class AbpAwareDbContextCreator<TShardingDbContext> : IDbContextCreator
        where TShardingDbContext : DbContext, IShardingDbContext
    {
        public DbContext CreateDbContext(DbContext shellDbContext, ShardingDbContextOptions shardingDbContextOptions)
        {
            var lazySp = ((TShardingDbContext)shellDbContext)
                .As<AbpDbContext<TShardingDbContext>>().LazyServiceProvider;
            var sp = lazySp.LazyGetRequiredService<IServiceProvider>();
    
            var dbContext = (TShardingDbContext)ActivatorUtilities.CreateInstance(
                sp, typeof(TShardingDbContext), shardingDbContextOptions.DbContextOptions);
    
            // ActivatorUtilities 只做参数注入,不做属性注入;手动补 LazyServiceProvider
            dbContext.As<AbpDbContext<TShardingDbContext>>().LazyServiceProvider =
                sp.GetRequiredService<IAbpLazyServiceProvider>();
    
            if (dbContext is IShardingTableDbContext s && s.RouteTail == null)
                s.RouteTail = shardingDbContextOptions.RouteTail;
    
            _ = dbContext.Model;
            return dbContext;
        }
    
        public DbContext GetShellDbContext(IShardingProvider shardingProvider)
        {
            try { return shardingProvider.GetService<TShardingDbContext>(); }
            catch (Exception ex)
            {
                throw new ShardingCoreInvalidOperationException(
                    $"cant get shell db context, override {nameof(IDbContextCreator)}.{nameof(IDbContextCreator.GetShellDbContext)}", ex);
            }
        }
    }
    

    然后在 services.AddShardingDbContext<...>() 链上加一句 ReplaceService<IDbContextCreator, ...>()

    services.AddShardingDbContext<SpcServiceDbContext>()
        .ReplaceService<IDbContextCreator, AbpAwareDbContextCreator<SpcServiceDbContext>>()
        .UseRouteConfig(o => { o.AddShardingTableRoute<SpcAutoAnalysesMonthRoute>(); })
        .UseConfig(o =>
        {
            o.UseShardingQuery((conn, builder) => builder.UseSqlServer(conn));
            o.UseShardingTransaction((conn, builder) => builder.UseSqlServer(conn));
            o.AddDefaultDataSource("ds0", connectionString);
        })
        .AddShardingCore();
    

    ShardingAbpDbContext 基类里两个可以删掉的过时代码

    ShardingCore 7.x 的 IShardingTableDbContext 接口只剩 RouteTail 一个属性,下面两行运行时已经没人读:

    public IShardingRuntimeContext ShardingRuntimeContext { get; set; }   // 可删
    public IShardingTableDbContext ShardingTableContext { get => this; set { } }   // 可删
    

    另外 GetShardingExecutor() 建议参考 ShardingCore 官方 AbstractShardingDbContext 的写法,缓存而不是每次 new:

    private IShardingDbContextExecutor _executor;
    public IShardingDbContextExecutor GetShardingExecutor()
        => _executor ??= this.CreateShardingDbContextExecutor();   // 仅 Shell DbContext 返回非 null
    

    最小可工作样例

    我在本地搭了个 ABP 9.3.1 + ShardingCore 7.9.2.1 + SQL Server 的最小项目实测,启动时按月自动建出 Orders_202512 ~ Orders_202605,INSERT 三条不同月的记录分别落到对应物理表,跨月 SELECT 能正确 union 出 3 行。完整代码可以参考下面这些文件直接拷贝试:

    Order.cs

    using System.ComponentModel.DataAnnotations.Schema;
    using Volo.Abp.Domain.Entities;
    
    [Table("Orders")]
    public class Order : Entity<Guid>
    {
        public string Code { get; set; }
        public DateTime CreationTime { get; set; }
    }
    

    OrderMonthRoute.cs

    using ShardingCore.Core.EntityMetadatas;
    using ShardingCore.VirtualRoutes.Months;
    
    public class OrderMonthRoute : AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
    {
        public override DateTime GetBeginTime() => new DateTime(2025, 12, 1);
    
        public override void Configure(EntityMetadataTableBuilder<Order> builder)
            => builder.ShardingProperty(o => o.CreationTime);
    
        public override bool AutoCreateTableByTime() => true;
    }
    

    AppDbContext.cs

    using Microsoft.EntityFrameworkCore;
    using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
    using ShardingCore.Extensions;
    using ShardingCore.Sharding.Abstractions;
    using Volo.Abp.Data;
    using Volo.Abp.EntityFrameworkCore;
    
    [ConnectionStringName("Default")]
    public class AppDbContext : AbpDbContext<AppDbContext>, IShardingDbContext, IShardingTableDbContext
    {
        public DbSet<Order> Orders { get; set; }
    
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    
        public IRouteTail RouteTail { get; set; }
    
        private IShardingDbContextExecutor _executor;
        public IShardingDbContextExecutor GetShardingExecutor()
            => _executor ??= this.CreateShardingDbContextExecutor();
    }
    

    AppModule.cs

    using Microsoft.Extensions.DependencyInjection;
    using ShardingCore;
    using ShardingCore.Core.DbContextCreator;
    using ShardingCore.Core.RuntimeContexts;
    using Volo.Abp;
    using Volo.Abp.Autofac;
    using Volo.Abp.Data;
    using Volo.Abp.EntityFrameworkCore;
    using Volo.Abp.EntityFrameworkCore.SqlServer;
    using Volo.Abp.Modularity;
    
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpEntityFrameworkCoreSqlServerModule)
    )]
    public class AppModule : AbpModule
    {
        public const string Cs = "Server=...;Database=...;User Id=...;Password=...;TrustServerCertificate=True;";
    
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var services = context.Services;
    
            Configure<AbpDbConnectionOptions>(opts => { opts.ConnectionStrings.Default = Cs; });
    
            services.AddAbpDbContext<AppDbContext>(options =>
            {
                options.AddDefaultRepositories(includeAllEntities: true);
            });
    
            // 改造 1
            Configure<AbpDbContextOptions>(opts =>
            {
                opts.PreConfigure<AppDbContext>(ctx =>
                {
                    var src = ctx.ServiceProvider.GetRequiredService<IShardingRuntimeContext<AppDbContext>>();
                    ctx.DbContextOptions.UseSharding(src);
                });
                opts.UseSqlServer();
            });
    
            // 改造 2
            services.AddShardingDbContext<AppDbContext>()
                .ReplaceService<IDbContextCreator, AbpAwareDbContextCreator<AppDbContext>>()
                .UseRouteConfig(o => { o.AddShardingTableRoute<OrderMonthRoute>(); })
                .UseConfig(o =>
                {
                    o.ThrowIfQueryRouteNotMatch = false;
                    o.UseShardingQuery((conn, builder) => builder.UseSqlServer(conn));
                    o.UseShardingTransaction((conn, builder) => builder.UseSqlServer(conn));
                    o.AddDefaultDataSource("ds0", Cs);
                })
                .AddShardingCore();
        }
    
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            context.ServiceProvider.UseAutoTryCompensateTable();
        }
    }
    

    几点注意

    1. ABP 多租户场景未验证。ABP 用 IConnectionStringResolver 按 tenant 动态切换连接串,而 ShardingCore 的 IVirtualDataSource 自己管连接串和数据源切换,两套机制在多租户下会再冲突一次,需要你自己设计 dataSourceName 与 tenantId 的映射,方案上面给的代码不覆盖。
    2. ShardingCore 在 NuGet 上 net9 没 official build,可用 7.9.2.1(net8.0,可被 net9 项目引用)或 7.10.2.1(net10.0)。请按你 ABP 9.3.1 实际 TFM 选一个验证。
    3. 如果后续遇到事件 Inbox/Outbox、ABP 仓储跨分表查询、UoW 嵌套事务等其他不兼容点,我们这边可能也无法直接给出现成方案,需要你结合 ShardingCore 的实际行为 case-by-case 处理。

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    感谢您专业的答复! 按照您给的示例 发现这一步AbpAwareDbcontextCreator 引用不到斌且没有相关的引用推荐 并且在尝试AbpAwareDbcontextCreator <SpcServiceDbContext>去掉AbpAwareDbcontextCreator 运行时 发生了错误

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    两个问题分别说明下:

    1. AbpAwareDbContextCreator 找不到引用

    这是需要你自己新建的一个类文件,不是 ShardingCore 自带的,IDE 当然引用不到。把上一条回复里那段 public class AbpAwareDbContextCreator<TShardingDbContext> : IDbContextCreator { ... } 整段贴到你项目里一个新的 .cs 文件(比如 AbpAwareDbContextCreator.cs),namespace 改成你项目自己的,加上下面这些 using 就能编译:

    using System;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.DependencyInjection;
    using ShardingCore.Core.DbContextCreator;
    using ShardingCore.Core.ServiceProviders;
    using ShardingCore.Exceptions;
    using ShardingCore.Sharding.Abstractions;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.EntityFrameworkCore;
    

    注意类名是 AbpAwareDbContextCreator(中间的 Context 是大写 C,你截图里写成了小写 c),编译器对大小写敏感。

    ReplaceService 那行不能去掉泛型参数,必须写成:

    .ReplaceService<IDbContextCreator, AbpAwareDbContextCreator<SpcServiceDbContext>>()
    

    AbpAwareDbContextCreator<T> 是开放泛型,必须显式闭合到 SpcServiceDbContext

    2. Autofac.Core.DependencyResolutionException

    这是 Autofac 在激活 IShardingRuntimeContext<SpcServiceDbContext>内部抛了别的异常被包装出来了,截图里看到的只是外层信息,真正的根因在 InnerException 里。麻烦你按下面任一种方式把内层异常贴出来:

    • 在 Visual Studio 的异常对话框里点 View Details,展开到底,把 InnerExceptionMessageStackTrace 复制出来;
    • 或者在调试时勾选 Enable Just My Code = false、勾选所有 CLR 异常,让程序中断在最内层异常处再看。

    另外有个关键点:你截图里 UseShardingQuery((conn, builder) => builder.UseMySql(conn)) 用了小写UseMySql —— 那是 Pomelo.EntityFrameworkCore.MySql 的扩展方法。但 ABP 9.3.1 的 Volo.Abp.EntityFrameworkCore.MySQL 模块依赖的是 Oracle 官方 MySql.EntityFrameworkCore(扩展方法是大写 UseMySQL),两个 provider 不能混用。如果你项目里同时引了 Pomelo 和 Oracle 两个 provider,ShardingCore 内部初始化时就会撞你截图里那个 Autofac.Core.DependencyResolutionException

    正确写法应该统一用 ABP 那个 provider:

    o.UseShardingQuery((conn, builder) =>
        Microsoft.EntityFrameworkCore.MySQLDbContextOptionsExtensions.UseMySQL(builder, conn));
    o.UseShardingTransaction((conn, builder) =>
        Microsoft.EntityFrameworkCore.MySQLDbContextOptionsExtensions.UseMySQL(builder, conn));
    

    (用全路径是因为 ABP Volo.Abp.EntityFrameworkCore 命名空间里也有同名的 UseMySQL 扩展,避免歧义。)

    完整可运行样例

    我这边按你的配置(ABP 9.3.1 + ShardingCore 7.9.2.1 + MySQL 8)搭了个最小可运行项目并完整跑通:启动时自动建出 Orders_202512 ~ Orders_202605 共 6 张月度表,INSERT 3 条不同月份记录会落到正确的物理表,跨月 SELECT 能 union 出 3 行。完整项目 zip 可以从这里下载(WeTransfer 临时链接,注意有有效期):

    https://we.tl/t-mJncvNtn6AdkiLWh

    解压后 dotnet run 即可对照。zip 里 README.md 有完整说明 + Docker 起 MySQL 命令。如果还有问题再贴 InnerException 详情。

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    抱歉 是我阅读回复不够仔细!,经过调整初始化已经可以完成 。非常感谢。但是在插入新数据验证时发现 Swagger 调试 返回200 正常的状态 实际数据库上主表和分表都没有没有新数据

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    初始化能跑通就好。新插入数据查不到,我现在信息不够下不了结论,麻烦你把下面几项贴出来一起看:

    1. EF Core SQL 日志。把日志级别开到 Information

      "Logging": {
        "LogLevel": {
          "Microsoft.EntityFrameworkCore.Database.Command": "Information"
        }
      }
      

      再调一次 swagger 的 Create 接口,把这次调用过程中控制台/日志里输出的所有内容贴出来(重点看:有没有 INSERT INTO ...、INSERT 的表名是带后缀的分表还是无后缀的主表、有没有被吞掉的 exception/warning)。

    2. _spcAutoAnalysesManager.CreateAsync(...) 的实现代码。看 manager 内部到底做了什么、有没有把实体加进仓储。

    3. CreateAsync 应用服务方法的 UoW 配置。是用 ABP 默认的自动 UoW,还是手动加了 [UnitOfWork] / [UnitOfWork(IsDisabled = true)] / IsTransactional = false 之类的属性?同时贴一下这个应用服务类的基类是什么。

    4. 你这个项目是不是多租户(启用了 AbpMultiTenancyOptions.IsEnabled = true)。如果是多租户,连接串路由还会和 ShardingCore 的 IVirtualDataSource 再冲突一次,需要单独处理。

    5. 完整的 AppModule(或 SpcServiceModule)的 ConfigureServices 代码,特别是 Configure<AbpDbContextOptions>AddShardingDbContext<...>().ReplaceService<IDbContextCreator, ...>()...AddShardingCore() 这两段。确认两处改造都按之前给的样例落地了,没漏掉哪一行。

    另外,你第 4 张截图看的是主表 tblSpcAutoAnalyses,里面 CreationTime 最新只到 2026-05-11,应该是分表配置生效前的历史数据,跟这次插入无关。分表生效后新数据应该出现在 tblSpcAutoAnalyses_yyyyMM 那些分表里。

    如果方便的话,直接打包一个能复现这个现象的最小测试项目(去掉敏感代码,留下 DbContext / 实体 / 路由 / 应用服务 / Module 配置 + 一个 README 写清复现步骤)发给我们更快,比一条条贴信息高效很多。打包发到 WeTransfer 之类的临时分享平台,把链接贴在这里就行。

    把以上信息(或最小复现项目)发上来后再继续诊断。

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    好的 确实是多租户的的微服务项目 因为项目引用太多 我整理一个能复现问题的测试项目 稍后发给您 帮忙看下 非常感谢!

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    好的

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    1 我照着您昨天的例子写了一个 发现运行查表和新增都是可以的 2 所以我现在把项目打包给您帮忙看下是不是Module设置或者DBContext哪里有不对, 3 还有您提到的 Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = true; }); 在测试Moudle也加了多租户的设置,和 ShardingCore 的 IVirtualDataSource的冲突该怎么处理 4 项目临时链接 https://limewire.com/d/7HKQ6#3xTkBz3Pag Thanks!

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    你的项目我下载下来看了,根因已经定位到,能 100% 在本地复现。

    根因

    SpcServiceDbContext.cs 第 218-221 行,GetShardingExecutor 写错了一个字符:

    private IShardingDbContextExecutor _executor;
    
    public IShardingDbContextExecutor GetShardingExecutor()
    {
        return _executor ?? this.CreateShardingDbContextExecutor();   // ← bug: ?? 不会赋值
    }
    

    应该改成 ??=(空合并赋值):

    public IShardingDbContextExecutor GetShardingExecutor()
    {
        return _executor ??= this.CreateShardingDbContextExecutor();
    }
    

    只改这一行,新增数据就能正常落到 tblSpcAutoAnalyses_yyyyMM 分表了。

    为什么 ?? 会导致 INSERT 静默丢失

    ?? 是空合并:_executor ?? expr 只是返回值,不会把 expr 赋给 _executor,所以每次 GetShardingExecutor() 都 new 一个全新的 ShardingDbContextExecutor 实例。

    ABP UoW 调 SaveChangesAsync 时走的是 ShardingCore 替换的 ShardingStateManager.SaveChangesAsync → 它每次都从 dbContext.GetShardingExecutor() 拿 executor 来分发实体到分表 DbContext。新 new 出来的 executor 没有任何已分发的实体/分表 DbContext 缓存,路由结果是"空",于是一条 INSERT 也不发,最终 ABP UoW 看到的是"0 行受影响、操作成功",HTTP 返回 200,但 DB 里什么都没写入。

    我把你 DbContext 关键的几个特性都搬到本地最小项目里做了对照验证:DbContext 实现 IHasEventInbox + IHasEventOutbox,模块里 Configure<AbpMultiTenancyOptions>(opts => opts.IsEnabled = true) 开多租户,Configure<AbpDistributedEventBusOptions> 把 Inbox/Outbox 都挂到 DbContext,实体继承 CreationAuditedEntity<Guid> + IMultiTenant,应用服务走 OrderManager.CreateAsyncIRepository.InsertAsync(autoSave: true) 链路,和你完全一致。

    ?? 版(复刻你的写法):

    OK: ABP + ShardingCore + MySQL initialized successfully.
    Query returned 0 rows:
      Orders_202512: []
      Orders_202601: []
      ...
    

    改成 ??= 后:

    INSERT INTO `Orders_202512` (`Id`, `Code`, `CreationTime`, `CreatorId`, `TenantId`) VALUES (...)
    INSERT INTO `Orders_202601` (`Id`, `Code`, `CreationTime`, `CreatorId`, `TenantId`) VALUES (...)
    INSERT INTO `Orders_202605` (`Id`, `Code`, `CreationTime`, `CreatorId`, `TenantId`) VALUES (...)
    Query returned 3 rows:
      Orders_202512: [D-2512]
      Orders_202601: [J-2601]
      Orders_202605: [M-2605]
    

    现象和你完全一致:?? 静默 0 行写入、??= 正常路由到月份分表。

    注意你项目里还有一个基类文件 ShardingAbpDbContext.cs(line 28-30)写的是正确的 ??=,但 SpcServiceDbContext 没继承这个基类,而是直接继承 AbpDbContext<> 然后重新写了一遍 GetShardingExecutor——这次复制粘贴时把 ??= 抄成了 ??

    关于多租户 + ShardingCore IVirtualDataSource 冲突

    这个我没有现成可工作的方案验证。两套机制冲突在于:

    • ABP IConnectionStringResolver 按当前 tenant 解析 connection string(每个 tenant 可以是不同的物理库);
    • ShardingCore 的 IVirtualDataSource 自己维护 dataSourceName → connection string 的字典,启动时通过 AddDefaultDataSource("ds0", connStr) 静态注册。

    如果你的多租户场景是"所有 tenant 共享一个物理库 + 按 tenant 分表",那其实不用动 IVirtualDataSource,让 ABP 的 connStr 统一指向同一个库即可,分表后缀走 ShardingCore 的路由就行。

    如果是"每个 tenant 一个物理库 + 库内再按月分表",那需要在 tenant 切换时动态注入新的 dataSource 到 IVirtualDataSource,并实现 IVirtualDataSourceRoute 把 tenantId 路由到 dataSourceName。这块 ShardingCore 自己有 AddPhysicDataSource 之类的 API,但要和 ABP 的 CurrentTenant.Change(...) 流程配合,我没在本地验证过,遇到具体问题再单独看。

    建议你先把第一个 ?? 的 bug 改了,确认插入和查询都没问题之后再处理多租户的设计。

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    哈哈 感谢您的回复!??= 改成 ?? 其实这里是我自己改掉的 , 1 因为我在调试的时候发现 ,没引入ShardingCore时 InsertAsync 默认的AutoSave就是false 并且数据可以正常写入 且SpcAutoAnalyse继承了SpcAutoAnalyseBase 也无需手动给并发戳, 2 现在的情况是 注册了分表的 SpcAutoAnalyse 这个实体 InsertAsync方法 参数 autoSave设置true 时 ,再手动赋值 ConcurrencyStamp 并发戳 后确实可以进行数据的新增了。但是其他没有配置分表的实体比如:SPCHandAnalysis 也无法进行数据新增了 , 3 我在想ShardingCore 应该只会接管 注册过的实体吧比如 :UseRouteConfig(o => { o.AddShardingTableRoute<SpcAutoAnalysesMonthRoute>(); o.AddShardingTableRoute<SPCOOCMonthRoute>(); })

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    回到本地复现一下你这次的现象——我把 Customer 加成"非分表实体"放进同一个 AppDbContext(和 SpcAutoAnalyse 等价的 sharded 实体并存),manager 内部用默认的 autoSave: falseIRepository.InsertAsync(customer),模块里同样开 AbpMultiTenancyOptions.IsEnabled = true 和 Inbox/Outbox:

    | 实体 | 是否注册分表路由 | InsertAsync 参数 | 结果 | |---|---|---|---| | Order(分表) | ✅ OrderMonthRoute | autoSave: true | INSERT 到 Orders_yyyyMM ✅ | | Customer(非分表) | ❌ | autoSave: false(默认) | UoW Complete 时批量 INSERT 到 Customers ✅ |

    SQL 输出:

    INSERT INTO `Orders_202512` (`Id`, `Code`, `CreationTime`, `CreatorId`, `TenantId`) VALUES (...)
    INSERT INTO `Orders_202601` (...) VALUES (...)
    INSERT INTO `Orders_202605` (...) VALUES (...)
    INSERT INTO `Customers` (`Id`, `CreationTime`, `CreatorId`, `Name`, `TenantId`) VALUES (...), (...)
    

    这个最小 stack 比你实际项目少了不少东西(AuditLogging / Identity / Saas / 自定义 PermissionChecker / 自定义 ExceptionFilter / 你自己的 RedisMessageProcessor 等模块都没复刻),所以只能说明在等价的核心 stack 下非分表实体能正常写入,不能直接断定你那边的问题不是 ShardingCore 引起的。需要看真实日志才能继续定位,麻烦你做两步:

    1. 看 EF Core SQL 日志。把日志级别开到 Information

      "Logging": {
        "LogLevel": {
          "Microsoft.EntityFrameworkCore.Database.Command": "Information",
          "Microsoft.EntityFrameworkCore": "Information"
        }
      }
      

      然后调一次 SPCHandAnalysis 的 swagger create,把这次调用过程的所有日志贴上来。关注 3 件事:

      • 有没有 INSERT INTO tblSPCHandAnalyses ... 这条 SQL
      • 有没有 EF Core 的 warning/error/DbUpdateException
      • 有没有 ABP UoW 相关的日志(Begin UowComplete UowRollback Uow
    2. 顺便把 SPCHandAnalysisManager.CreateAsync(...) 最后一行的 InsertAsync 也加上 autoSave: true 再跑一次:

      return await _sPCHandAnalysisRepository.InsertAsync(sPCHandAnalysis, true);
      

      这样 SaveChanges 时机被提前到 manager 里,SQL 日志和异常更直观。

    把 1 的完整日志贴上来后再继续定位。

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    2250368121 created

    抱歉 我发现了问题 非分表实体 插入数据是正常的,是创建时间没有赋值导致数据在最后面去了,问题已经解决,感谢您耐心专业的指导!目前多租户指向的是一个数据库地址,后续我再使用中会观察数据情况!再次感谢您!

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    好的

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.