如何在使用 TPT 层次结构时首先在 EF 代码中实现并发

Posted

技术标签:

【中文标题】如何在使用 TPT 层次结构时首先在 EF 代码中实现并发【英文标题】:How to implement Concurrency in EF code first while using a TPT hierarchy 【发布时间】:2011-04-08 09:56:55 【问题描述】:

大家好!我正在尝试首先使用实体​​框架代码实现乐观并发检查(通过 nuget 安装,所以我假设它是最后一个 RC)。但是,我似乎无法让它工作。我有以下类层次结构

public abstract class DbEntityBase

    /// <summary>
    /// Autogenerated primary key for the object
    /// </summary>
    public Guid Id  get; private set; 

    protected DbEntityBase()
    
        Id = Guid.NewGuid();
    

    //Some override for equality, omitted for brievity


public class Drawing : DbEntityBase

    public string Name  get; set; 

    //Just an implementation of ICollection<DrawingVersion>
    public DrawingVersionCollection Versions  get; private set; 

    public Drawing()
    
        Name = "New Drawing";
        Versions = new DrawingVersionCollection(this);         
    



public class DrawingVersion : DbEntityBase

    public string Name  get; set; 

    public DateTime Started  get; set; 

    public DateTime LastModified  get; set; 

    public DrawingFile File  get; private set; 

    public Drawing Owner  get; internal set; 

    internal DrawingVersion()
    
        Name = "New Version";
        Started = DateTime.Now;
        LastModified = DateTime.Now;
        File = new DrawingFile();
    

我正在使用以下 DbContext 来持久化它们

public class PortfolioContext : DbContext

    public DbSet<Drawing> Drawings  get; set; 

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        //Key mapping
        modelBuilder.Entity<DbEntityBase>()
            .HasKey(e => e.Id);

        //Table Mapping
        modelBuilder.Entity<DrawingVersion>().ToTable("DrawingVersion");
        modelBuilder.Entity<Drawing>().ToTable("Drawing");

        //Ignored fields
        modelBuilder.Entity<DrawingVersion>()
            .Ignore(v => v.Owner);
    

所有这些工作都很好,花花公子。如果我检查生成的模式,我会看到表格,一个用于绘图,一个用于 DrawingVersion。我可以毫无问题地阅读、更新和删除。

当我想为我的实体添加时间戳时出现了问题。起初我做了一件很幼稚的事情。我在 DbEntityBase 中添加了以下内容

    public byte[] Timestamp  get; set; 

在我的上下文中那些行

        //Versionning mapping
        modelBuilder.Entity<DbEntityBase>()
            .Property(e => e.Timestamp)
            .IsConcurrencyToken()
            .HasColumnType("timestamp")
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

但是,在模式生成期间,它失败并出现以下异常

System.NotSupportedException : 存储生成的模式“计算”是 不支持的属性 不是“时间戳”类型或 '行版本'。

那时我有点讽刺。 “哎呀,我就是这样,把 timestamp 放在列类型中,而我应该把 timestamp 代替”。我试着改用“rowversion”,但还是不行。我也直接在属性上尝试了 TimestampAttribute,但同样的错误。

我试图将时间戳数据放在子类上,但后来我遇到了以下异常

System.Data.Entity.ModelConfiguration.ModelValidationException :一个或多个验证错误是 在模型生成期间检测到

System.Data.Edm.EdmEntityType: : 类型 'Portfolio.Repositories.Drawing' 是 从类型派生 'Portfolio.Repositories.DbEntityBase' 那是 EntitySet 的类型 'PortfolioContext.DbEntityBases'。类型 'Portfolio.Repositories.Drawing' 定义新的并发要求 不允许用于子类型的 基本 EntitySet 类型。

所以我想我也做不到。

我必须说,我很困惑。为什么异常告诉我应该使用时间戳,因为这正是我正在做的?这可能是框架中的错误吗?如果不是,他们是否有任何其他方式来使用 Table PerType 映射添加并发检查?

我使用 SQL Serve Ce 4.0 作为我的数据库。

【问题讨论】:

【参考方案1】:

这看起来像是定义 TPC 继承的问题。您必须修改您的子实体映射以使用来自父实体的映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

    ...

    modelBuilder.Entity<DrawingVersion>()
                .Map(m => m.MapInheritedProperties())
                .ToTable("DrawingVersion");

    modelBuilder.Entity<Drawing>()
                .Map(m => m.MapInheritedProperties())
                .ToTable("Drawing");

    ...

【讨论】:

以上是关于如何在使用 TPT 层次结构时首先在 EF 代码中实现并发的主要内容,如果未能解决你的问题,请参考以下文章

使用 EF Code First 继承 – 每个类型的表 (TPT)

代码优先 TPT 和级联删除

EF Core 5 TPT 和身份 (CRUD)

EF Core从TPH迁移到TPT

EF Core 5 TPT - 具有不同 ID 名称的继承对象

Inheritance with EF Code First: Part 2 – Table per Type (TPT)