使用EF Core Code First操作Spanner数据库时遇主键检测异常
问题
使用Entity Framework Core的Code First模式创建Google Cloud Spanner数据库时,调用EnsureCreated方法抛出如下异常:
System.InvalidOperationException: 'No primary key was found in the database for table Table DistributionChannel. Call SpannerModelValidationConnectionProvider.Instance.EnableDatabaseModelValidation(false) to disable model validation if this error is a false positive.'
相关代码如下:
实体类DistributionChannel
public class DistributionChannel { public Guid Id { get; set; } public string Name { get; set; } }
上下文SpannerContext
public class SpannerContext : DbContext, IDesignTimeDbContextFactory<SpannerContext> { public SpannerContext() { } public SpannerContext(DbContextOptions options) : base(options) { } public DbSet<DistributionChannel> DistributionChannels { get; set; } public SpannerContext CreateDbContext(string[] args) { var builder = new DbContextOptionsBuilder<SpannerContext>(); var configBuilder = new ConfigurationBuilder() .SetBasePath($"{AppContext.BaseDirectory}/Configurations") .AddJsonFile("migrations.json") .Build(); var configuration = configBuilder.GetSpannerConfiguration(); var databaseName = DatabaseName.FromProjectInstanceDatabase(configuration.ProjectId, configuration.InstanceId, configuration.DatabaseId); var dataSource = $"Data Source={databaseName}"; var connectionString = new SpannerConnectionStringBuilder(dataSource).ConnectionString; builder.UseSpanner(connectionString, config => { config.CommandTimeout(configuration.Timeout); }); return new SpannerContext(builder.Options); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.EnableSensitiveDataLogging(); base.OnConfiguring(optionsBuilder); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new DistributionChannelConfiguration()); modelBuilder.AddJsonFields(); } }
配置类DistributionChannelConfiguration
internal class DistributionChannelConfiguration : IEntityTypeConfiguration<DistributionChannel> { public void Configure(EntityTypeBuilder<DistributionChannel> entity) { entity.ToTable("DistributionChannel"); entity.HasKey(x => x.Id); entity.Property(e => e.Name) .IsRequired() .HasMaxLength(255) .IsUnicode(false); } }
Startup调用代码
//rest of code using var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope(); using var context = scope.ServiceProvider.GetRequiredService<SpannerContext>(); context.Database.EnsureCreated();
解决办法
1. 显式指定Guid主键的数据库类型
Spanner没有原生Guid类型,通常用STRING(36)存储Guid值。修改DistributionChannelConfiguration中的主键配置,明确指定列类型:
internal class DistributionChannelConfiguration : IEntityTypeConfiguration<DistributionChannel> { public void Configure(EntityTypeBuilder<DistributionChannel> entity) { entity.ToTable("DistributionChannel"); entity.HasKey(x => x.Id); // 添加主键类型配置 entity.Property(x => x.Id) .HasColumnType("STRING(36)") .ValueGeneratedOnAdd(); // 如需自动生成Guid,保留此行 entity.Property(e => e.Name) .IsRequired() .HasMaxLength(255) .IsUnicode(false); } }
2. 临时禁用模型验证(仅用于排查问题)
如果是Spanner元数据同步延迟导致的验证误报,可以临时关闭模型验证,确认表是否实际创建成功:
在上下文的CreateDbContext方法中修改Spanner配置:
builder.UseSpanner(connectionString, config => { config.CommandTimeout(configuration.Timeout); // 禁用模型验证 SpannerModelValidationConnectionProvider.Instance.EnableDatabaseModelValidation(false); });
注意:此方法仅用于排查,不推荐长期使用,避免跳过真实的模型配置问题。
3. 检查Spanner基础状态
确保Google Cloud Spanner实例和数据库已完成初始化,程序拥有操作Spanner的足够权限,且网络连接正常。
内容的提问来源于stack exchange,提问作者Vinicius Andrade
相关产品推荐
相关产品推荐

