更改层次结构中的“类型”

3

TL/DR - 有没有一种方法可以强制EF Core允许我更新Discriminator列?

我第一次尝试使用Entity Framework Core,并尝试实现一个简单的系统,其中我需要管理三种类型的客户。其中两种类型在其类型值本身上只有意义上的不同(type1type2)。第三种类型(special)具有更多可能关系的扩展集。

因此,在数据库中,我创建了一个单独的表来存储核心数据:

CREATE TABLE [Clients].[Clients](
    [ClientId] [varchar](30) NOT NULL,
    [ClientName] [varchar](100) NOT NULL,
    [ProtocolType] [varchar](30) NOT NULL,
    [ClientLogoUrl] [varchar](max) NULL,
 CONSTRAINT [PK_Clients_Clients] PRIMARY KEY ([ClientId]),
 CONSTRAINT [UQ_Clients_Client_Names] UNIQUE ([ClientName]),
 CONSTRAINT [UQ_Clients_Client_Protocol_XRef] UNIQUE ([ClientId],[ProtocolType]),
 CONSTRAINT [FK_Clients_Client_Protocol] FOREIGN KEY([ProtocolType])
     REFERENCES [Clients].[Protocols] ([ProtocolType])
)

在我的代码中,我创建了以下这些模型:

  public class Client
  {
    public string ClientId { get; set; }
    public string ClientName { get; set; }
    public string ProtocolType { get; set; }
    public string ClientLogoUrl { get; set; }
    public Protocol Protocol { get; set; }
  }

对于那些具有更多可能关系的客户子类型:

  public class SpecialClient : Client
  {
    public List<Service> Services { get; set; }
    //And more
  }

最初我将此映射为OnModelCreating中的:

  modelBuilder.Entity<Clients.Client>()
    .HasDiscriminator<string>("ProtocolType")
    .HasValue<Clients.SpecialClient>("special");

但是,这样只会查询数据库中协议类型为specialClient的记录 - 这不是我想要的。我本以为EF会为“所有其他值”使用基类,但它并没有这样做。因此,我尝试了以下方法:

  modelBuilder.Entity<Clients.Client>()
    .HasDiscriminator<string>("ProtocolType")
    .HasValue<Clients.SpecialClient>("special")
    .HasValue<Clients.Client>("type1")
    .HasValue<Clients.Client>("type2");

但是这样做实际上只获取了最后提供的值,并且只查询了数据库中的specialtype2。所以,我最终接受了需要引入一个多余的额外类的事实,该类没有任何额外功能,只是为了让映射器满意:

public class Type2Client : Client {}

并且:

  modelBuilder.Entity<Clients.Client>()
    .HasDiscriminator<string>("ProtocolType")
    .HasValue<Clients.SpecialClient>("special")
    .HasValue<Clients.Client>("type1")
    .HasValue<Clients.Type2Client>("type2");
最后——EF感到高兴,一切都很好,我可以继续我的生活了。然而,下一个挑战是客户端可以更改协议

所以,我加载一个客户端。我更改它的协议类型。我保存它:

      _context.Update(client);
      await _context.SaveChangesAsync();

而 EF 生成的 SQL 如下:

SET NOCOUNT ON;
UPDATE [Clients].[Clients] SET [ClientLogoUrl] = @p0, [ClientName] = @p1
WHERE [ClientId] = @p2;
SELECT @@ROWCOUNT;

嗯,ProtocolType列没有SET关键字。有什么巧妙的方法可以让它工作吗?

1个回答

2
您需要更改鉴别器列的默认“保存后”行为。默认情况下,鉴别器列的AfterSaveBehavior设置为Throw。这意味着如果设置了显式值或更改了值,则在尝试保存更改时会引发异常。这也意味着Update不会将此类属性标记为已修改,因为这将在SaveChanges期间导致异常。
因此,在您将该列设置为鉴别器之后,请将保存行为更改为Save
modelBuilder.Entity<Client>()
   .Property(c => c.ProtocolType).Metadata.AfterSaveBehavior = PropertySaveBehavior.Save;

它应该能够按照您的期望工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接