【问题标题】:Relation Many to Many EF6 Fluent API关系多对多 EF6 Fluent API
【发布时间】:2019-11-14 12:58:46
【问题描述】:

我正在使用带有 Fluent API 的 EF6 开发应用程序,但在管理多对多关系时遇到了问题。 由于某些内部原因,联接表具有特定格式,包括 4 个字段 - 左标识(FK) - 右 ID (FK) - 开始日期(日期时间) - EndDate(日期时间)

删除链接实际上是将 EndDate 设置为非空,但我现在不知道如何在 EF6 中配置它。 另一方面,在读取链接时不应考虑 EndDate 为 Not NULL 的记录。

你能给我一个解决方案吗?

谢谢。

【问题讨论】:

标签: c# entity-framework entity-framework-6 ef-fluent-api


【解决方案1】:

联接表和 EF

EF 会为您自动执行一些操作。为此,它使用约定优于配置。如果你遵守约定,你可以跳过一大堆常见的配置。

例如,如果您的实体有一个名为 Id 的属性,EF 会固有地假定这是 PK。

类似地,如果两种实体类型具有相互引用的导航道具(并且两个实体之间仅存在一个直接链接),则 EF 将自动假定这些导航道具是单个多对-很多关系。 EF 会在数据库中创建一个连接表,但它会对你隐藏它,让你自己处理这两种实体类型。

由于某些内部原因,Join 表具有特定格式,包括 4 个字段 - Left Id (FK) - Right Id (FK) - StartDate (dateTime) - EndDate (datetime)

您的联接表不再符合常规和自动生成的 EF 联接表的内容。您期望 EF 基于盲目约定无法提供的自定义配置级别,这意味着您必须显式配置它。

其次,您拥有这些附加列这一事实意味着您希望在某些时候使用这些数据(大概是为了显示两个实体之间的历史关系。因此,依赖 EF 的自动连接表没有意义因为连接表和它的内容将对应用程序/开发人员隐藏。

如果您不需要应用程序获取已结束的条目,则第二个考虑可能对您无效。但总体观点仍然存在。

这里的解决方案是让连接记录成为它自己的显式实体。本质上,您在这里处理的不是多对多,而是处理具有两个一对多关系(两种实体类型中的每一种关系)的特定实体(连接元素)。

这使您能够准确地实现您想要的。在这种情况下,您对 EF 可以为您自动化的期望根本不适用。


软删除

删除链接实际上是将 EndDate 设置为非空,但我现在不知道如何在 EF6 中配置它。

一般来说,这称为“软删除”行为,尽管此处可能略有不同。在常规的软删除模式中,当一个条目被删除时,数据库会秘密保留该条目,但应用程序不知道这一点,也不会再次看到该条目。

尚不清楚您是否打算让已结束的条目仍显示在应用程序中,例如关系史。如果不是这种情况,那么您的情况是正是软删除行为。

这不是您在模型级别配置的内容,而是您在数据库的 SaveChanges 行为中覆盖的内容。我如何实现软删除的一个简单示例:

public override int SaveChanges()
{
    // Get all entries of the change trackes (of a given type)
    var entries = ChangeTracker.Entries<IAuditedEntity>().ToList();

    // Filter the entries that are being deleted
    foreach (var entry in entries.Where(entry.State == EntityState.Deleted))
    {
        // Change the entry so it instead updates the entry and does not delete it
        entry.Entity.DeletedOn = DateTime.Now;
        entry.State = EntityState.Modified;
    }

    return base.SaveChanges();
}

这允许您防止删除您希望应用此功能的实体,这是实现软删除的最安全方法,因为这可以作为来自使用此数据库上下文的任何消费者的数据库删除的全部内容。

您的问题的解决方案几乎相同。假设您将连接实体命名为(参见上一章)JoinEntity

public override int SaveChanges()
{
    var entries = ChangeTracker.Entries<JoinEntity>().ToList();

    // Filter the entries that are being deleted
    foreach (var entry in entries.Where(entry.State == EntityState.Deleted))
    {
        // Change the entry so it instead updates the entry and does not delete it
        entry.Entity.Ended = DateTime.Now;
        entry.State = EntityState.Modified;
    }

    return base.SaveChanges();
}

警告词

软删除往往是所有实体的包罗万象(或至少是数据库的重要部分)。因此,像我在这里所做的那样在数据库上下文级别捕获它是有意义的。

但是,如果这个实体是唯一的,因为它是软删除的,那么这更像是一个业务逻辑实现,而不是一个 DAL 架构。如果您开始为不同类型的实体编写许多自定义规则,那么 db 上下文逻辑将会变得杂乱无章,并且使用起来也不会很好,因为您需要考虑在SaveChanges 期间发生的多种可能的操作。

请注意不要将本应属于业务逻辑的决策推送给 DAL。我不能为你画这条线,这取决于你的上下文。但是评估数据库上下文是否是实现此行为的最佳位置。

【讨论】:

  • 感谢您对 EF 实际工作方式的解释。关于这一点,我改变了主意,尝试另一种方式来实现我想做的事情......
【解决方案2】:

你能给我一个解决方案吗?

如果您的链接表有额外的列,您必须将其建模为实体,并且导航的 EndDate 逻辑需要明确。 EF 不会为您做任何事情。

【讨论】:

    【解决方案3】:

    我不想让它成为实体,因为 2 个额外的字段是技术字段(用于记录目的)而不是功能字段。我的应用程序不应该知道它们的存在。

    我想知道我是否不能使用 MapToProcedure() 函数但我不知道该怎么做???

    【讨论】:

    • 这不是答案。这是问题的第二部分。请相应地编辑您的问题并删除此答案。
    猜你喜欢
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 2023-03-31
    • 2012-09-22
    相关资源
    最近更新 更多