使用TPC现在不会级联删除子对象。

huangapple go评论49阅读模式
英文:

Using TPC it now does not cascade delete child objects

问题

这是你要翻译的代码部分:

所以我有一个Effort的基类

public abstract class Effort 
{
    public int Id { get; private set; }
    public ICollection<Signup>? Signups { get; set; }
    // ...
}

public class EffortConfiguration : IEntityTypeConfiguration<Effort>
{
    public void Configure(EntityTypeBuilder<Effort> builder)
    {
        builder.UseTpcMappingStrategy();
    }
}

它有2个子类:

public class Job : Effort
{
    // 所有内容都在基类中
}

public class Event : Effort
{
    public ICollection<Shift>? Shifts { get; set; }
    // ...
}

然后我们有子类(Signup和Shift):

public class Signup 
{
    public int Id { get; private set; }
    public Effort Effort { get; set; }
    // ...
}

public class Shift : IPkIsId
{
    public int Id { get; private set;  }
    public Event Event { get; set; } = default!;
    // ...
}

所以当删除Event时,所有Shift子记录都会被删除。但是当删除Event或Job时,子记录Signup **不会** 被删除。

如果我不调用 `builder.UseTpcMappingStrategy()`,它们会被删除。

我尝试了使用Fluent API。在SignupConfiguration中,我尝试了:

builder.HasOne(s => s.Effort)
    .WithMany(e => e.Signups)
    .OnDelete(DeleteBehavior.Cascade);

Event和JobConfiguration中,我尝试了(也尝试了在Effort中):

builder.HasMany(s => s.Signups)
    .WithOne(e => (Job?)e.Effort)
    .OnDelete(DeleteBehavior.Cascade);

除了这个问题,TPC运行得很好。我的其他单元测试都通过了。我得到了两个不同的表。

我还尝试将 `public ICollection<Signup>? Signups { get; set; }` 移动到Event和Job中,并从Effort中删除它。但在update-database期间,它报告了一个错误,说它正在尝试创建重复的函数名称。

我需要在这里做什么?
英文:

So I have a base class of Effort:

public abstract class Effort 
{
public int Id { get; private set; }
public ICollection&lt;Signup&gt;? Signups { get; set; }
// ...
}
public class EffortConfiguration : IEntityTypeConfiguration&lt;Effort&gt;
{
public void Configure(EntityTypeBuilder&lt;Effort&gt; builder)
{
builder.UseTpcMappingStrategy();
}
}

And it has 2 sub classes:

public class Job : Effort
{
// everything is in the base class
}
public class Event : Effort
{
public ICollection&lt;Shift&gt;? Shifts { get; set; }
// ...
}

And then we have the children (Signup & Shift):

public class Signup 
{
public int Id { get; private set; }
public Effort Effort { get; set; }
// ...
}
public class Shift : IPkIsId
{
public int Id { get; private set;  }
public Event Event { get; set; } = default!;
// ...
}

So when an Event is deleted, all of the Shift children are deleted. But when an Event or Job is deleted, the children Signup records are not deleted.

They are deleted if I don't call builder.UseTpcMappingStrategy().

I have tried fluent. In the SignupConfiguration I tried:

builder.HasOne(s =&gt; s.Effort)
.WithMany(e =&gt; e.Signups)
.OnDelete(DeleteBehavior.Cascade);

In the Event & JobConfiguration I tried (also tried it in Effort):

builder.HasMany(s =&gt; s.Signups)
.WithOne(e =&gt; (Job?)e.Effort)
.OnDelete(DeleteBehavior.Cascade);

Aside from this one problem TPC is working great. All my other unit tests pass. I get the two distinct tables.

I also tried moving public ICollection&lt;Signup&gt;? Signups { get; set; } to Event & Job, removing it from Effort. But that got an error during update-database where it said it was trying to create a duplicate function name.

What do I need to do here?

答案1

得分: 0

不,类似这样的方法在TPC中不起作用。原因是与TPC和TPH/TPT中键的工作方式有关。

在TPH/TPT中,您将拥有一个Effort表,所有子类都共享一个Effort.Id。在TPT中,您将拥有Job和Event表以及Id列,但是这些Id列将与来自Effort的Id列匹配。例如,如果您将列命名为EffortId,那么Job和Event表的PK也将是EffortId,处于一对一的关系中。

在TPC中,没有Effort表来协调PK,因此每个子类表将为其自己的Id PK提供服务。从一个子类到另一个常见类(如Signups)的任何关系现在都不能基于Effort子类。从C#的角度来看,这是完全可以的,但从关系数据库的角度来看,不再如此。Signup将希望将FK返回到Job或Event,并且FK不能指向“其中一个表”。在TPH/TPT中,Signup.EffortId FK可以返回到Effort表,在TPC中,它没有单一的表可以返回。

对于TPC,您需要像JobSignup和EventSignup这样的内容,其中FK返回到Job或Event。这可以是具有相同通用Signup详细信息的不同表,或者您可以将其用作Job/Event和单个Signup表之间的多对多表,但在每个连接表中添加唯一约束以强制仅将Signup链接到一个Job或Event。然而,这只是防止一个Signup与两个或更多个Job或两个或更多个Event相关联,数据库仍然允许一个Signup同时链接到一个Job和一个Event。您需要在代码中在应用程序/领域级别强制执行此操作,并可以定期运行维护作业以查找跨引用的Signup作为调查和修复的报告。Job和Event都可以拥有一个Signup实体的集合,但它不能是双向的。就EF而言,它是一对多关系,它将被视为一对一关系。

使用不同的表将取消这一点,虽然这意味着有两个几乎相同的表,就像决定使用TPC来拆分子类的存储和索引而不是将所有内容放在一个表中或跨两个连接的表中一样,将JobSignup与EventSignup分开仍然相当于将相同的记录总数分成两个表,而不是一个表,其FK的索引分别为其各自的表。(而不是每次在整个组合集合上进行搜索/索引) 这意味着您将不会拥有一个单一的Signup实体,但Job.Signups将是一个JobSignup集合,而Event.Signups将是一个EventSignup集合。从技术上讲,这两个Signup类仍然可以扩展一个基本的Signup,其中子类只管理回到其各自的Job/Event的FK。

英文:

No, something like that won't work in TPC. The reason will be because of the way Keys work in TPC vs TPH/TPT

In TPH/T You will have one table for Effort that all subclasses share with an Effort.Id. In TPT you would have tables for Job and Event with Id columns, but those Id columns would match the Id column coming from Effort. For instance if you named the column EffortId, the Job and Event tables would have a PK of EffortId as well in a 1-to-1 relationship.

In TPC you have no Effort table to coordinate the PK so each subclass table will service its own Id PK. Any relationship from a Subclass to another common class like Signups can now not be based on the Effort subclass. From C# this is perfectly fine, but from a relational database it no longer is. Signup will want a FK back to a Job or Event, and a FK cannot point back to "one table or the other". In TPH/TPT the Signup.EffortId FK can go back to an Effort table, in TPC it has no single table to go back to.

For TPC you would need something like a JobSignup and EventSignup where the FK goes back to Job or Event respectively. This can be either separate tables with the same general Signup details, or you could use it as a many-to-many table between Job/Event and a single Signup table, but add a unique constraint to the SignupId in each of the joining tables to enforce that a Signup is only ever linked to one Job or Event. However, this just prevents a Signup being linked to two or more Jobs, or two or more Events, the database would still allow a Signup to be linked to one Job and one Event at the same time. You would need to enforce this at the application/domain level in code, and could have a maintenance job run periodically to look for Signups that are cross-referenced as a report for investigation and repair. Both Job and Event could have a collection of a Signup entity, but it couldn't be bi-directional. As far as EF is concerned it is a many-to-many relationship, it will just be treated as a one-to-one.

Using separate tables would negate this and while it does mean having two tables that are virtually identical, like the decision to use TPC to split storage and indexing for the subclasses rather than having everything in one table or across a joined two tables, splitting JobSignup from EventSignup still amounts to the same total # of records split between two tables instead of one with indexes for the FKs of their respective tables. (rather than searching/indexing across the entire combined set each time) It does mean that you wouldn't have a single Signup entity, but Job.Signups would be a collection of JobSignup while Event.Signups would be a collection of EventSignup. Technically these two Signup classes could still extend a base Signup, where the subclasses just manage the FK back to their respective Job/Event.

huangapple
  • 本文由 发表于 2023年7月17日 11:52:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定