实体框架中有支持 TableGenerator id 的内置方法吗?如果没有,最好的实施方法是什么?
Hibernate 中的表生成器文档:
https://docs.oracle.com/javaee/6/api/javax/persistence/TableGenerator.html
定义一个主键生成器,当 生成器元素是为 generatedValue 注释指定的。 A 表生成器可以在实体类或主类上指定 关键字段或属性。生成器名称的范围是全局的 持久性单元(跨所有生成器类型)。
Example 1: @Entity public class Employee { ... @TableGenerator( name="empGen", table="ID_GEN", pkColumnName="GEN_KEY", valueColumnName="GEN_VALUE", pkColumnValue="EMP_ID", allocationSize=1) @Id @GeneratedValue(strategy=TABLE, generator="empGen") int id; ... }
这是示例实现以及我的实现想法。
在数据库中创建一个新表来存储生成的密钥。该表应至少有两列:一列用于键名称(例如“Table1”),一列用于当前键值。
定义一个新类来表示实体框架模型中的 TableGenerator 实体。此类应映射到数据库中的 TableGenerator 表。
public class TableGenerator
{
public string TableName { get; set; }
public int NextId { get; set; }
}
将 TableGenerator 实体的 DbSet 添加到 DbContext 类。
public DbSet<TableGenerator> TableGenerators { get; set; }
在 DbContext 类中创建一个方法来为给定的表名称生成新键。
public int GenerateKey(string tableName)
{
var generator = TableGenerators.SingleOrDefault(t => t.TableName == tableName);
if (generator == null)
{
generator = new TableGenerator { TableName = tableName, NextId = 1 };
TableGenerators.Add(generator);
}
generator.NextId++;
SaveChanges();
return generator.NextId;
}
使用GenerateKey方法为您的实体生成唯一的密钥。
var newId = dbContext.GenerateKey("Table1");
var entity = new MyEntity { Id = newId, Name = "Entity1" };
dbContext.MyEntities.Add(entity);
dbContext.SaveChanges();
如何改进上面的代码并添加锁定机制?
我使用 sql-server 作为数据库提供者
这里是 ef-core 中表生成器的更好实现,它覆盖了默认的 ValueGenerator:
定义一个新类来表示实体框架模型中的 TableGenerator 实体
public class TableId
{
[Key]
public required string TableName { get; set; }
public int NextId { get; set; }
}
价值生成器
public class TableGenerator : ValueGenerator<int>
{
private readonly object _lock = new object();
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
{
lock (_lock)
{
var dbContext = entry.Context;
var tableName = entry.Metadata.GetTableName() ?? throw new InvalidOperationException();
var tableIdValue = entry.Context.Set<TableId>().FirstOrDefault(t => t.TableName == tableName);
if (tableIdValue == null)
{
tableIdValue = new TableId { TableName = tableName, NextId = 1 };
entry.Context.Set<TableId>().Add(tableIdValue);
}
else
{
tableIdValue.NextId++;
dbContext.Entry(tableIdValue).State = EntityState.Modified;
}
dbContext.SaveChanges();
return tableIdValue.NextId;
}
}
}
添加没有身份列的自定义实体
public class CustomEntity
{
[Key]
public int Id { get; set; }
// Other properties
}
最后 OnModelConfiguring 重写以添加新的生成器
public class SampleDbContext : DbContext
{
public DbSet<TableId> TableIds { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomEntity>().Property(e => e.Id)
.ValueGeneratedNever()
.HasValueGenerator<TableGenerator>();
}