如何在Entity Framework中配置自定义字符串ID生成器
解决方案
首先请先将你需要使用的自定义字符串ID生成器实现引入到项目中,本文示例中我们将该生成器类命名为DefaultIdGenerator,其提供GenerateUniqueId()方法返回唯一字符串ID。
方法1:重写DbContext的SaveChanges方法(推荐,适配性强)
该方法无需修改模型配置,所有包含字符串类型Id属性的实体会自动统一适配规则:
- 将ID生成器注册到依赖注入容器
.NET 5 中在Startup.cs的ConfigureServices方法中添加如下代码:
services.AddSingleton<DefaultIdGenerator>();
- 在自定义DbContext中注入生成器,并重写保存方法
public class AppDbContext : DbContext { private readonly DefaultIdGenerator _idGenerator; public AppDbContext(DbContextOptions<AppDbContext> options, DefaultIdGenerator idGenerator) : base(options) { _idGenerator = idGenerator; } public DbSet<Product> Products { get; set; } public override int SaveChanges() { GenerateIdsForAddedEntities(); return base.SaveChanges(); } public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { GenerateIdsForAddedEntities(); return base.SaveChangesAsync(cancellationToken); } private void GenerateIdsForAddedEntities() { // 筛选所有处于新增状态的实体 var addedEntities = ChangeTracker.Entries() .Where(e => e.State == EntityState.Added); foreach (var entry in addedEntities) { // 检查是否存在字符串类型且为空的Id属性 var idProperty = entry.Property("Id"); if (idProperty.Metadata.ClrType == typeof(string) && string.IsNullOrWhiteSpace(idProperty.CurrentValue as string)) { idProperty.CurrentValue = _idGenerator.GenerateUniqueId(); } } } }
方法2:使用EF Core原生自定义值生成器
该方法基于EF Core官方的值生成机制实现,规则配置更灵活:
- 实现自定义值生成器类
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ValueGeneration; public class CustomStringIdGenerator : ValueGenerator<string> { private readonly DefaultIdGenerator _idGenerator; public CustomStringIdGenerator(DefaultIdGenerator idGenerator) { _idGenerator = idGenerator; } // 标记生成的是永久值,不是EF临时值 public override bool GeneratesTemporaryValues => false; public override string Next(EntityEntry entry) { return _idGenerator.GenerateUniqueId(); } }
- 在
OnModelCreating方法中配置值生成规则
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 单独为Product实体配置 modelBuilder.Entity<Product>() .Property(p => p.Id) .HasValueGenerator<CustomStringIdGenerator>(); // 如需批量为所有带string类型Id的实体配置,可使用如下代码 /* var stringIdEntities = modelBuilder.Model.GetEntityTypes() .Where(t => t.FindProperty("Id")?.ClrType == typeof(string)); foreach (var entityType in stringIdEntities) { entityType.FindProperty("Id") .SetValueGeneratorFactory((_, __) => new CustomStringIdGenerator(new DefaultIdGenerator())); } */ }
注意:实体的Id属性不要配置为数据库生成(比如不要加
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]注解),否则EF会忽略自定义生成的ID值,等待数据库返回ID。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

