实体框架核心+SQL Server中的TableGenerator策略

问题描述 投票:0回答:1

实体框架中有支持 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 作为数据库提供者

c# sql-server entity-framework-core
1个回答
-1
投票

这里是 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>();
    }
© www.soinside.com 2019 - 2024. All rights reserved.