【问题标题】:One to One and One to Many for the same entity type同一实体类型的一对一和一对多
【发布时间】:2018-05-29 21:42:11
【问题描述】:

我正在尝试设计以下实体关系

  • 程序
  • 组装

一个程序可以有多个程序集。其中一个组件将是主组件。每个程序集只属于一个程序。

类的建模如下:

public class Program
{
    public int Id {get;set;}
    public virtual ProgramAssembly MainAssembly {get;set;}
    public virtual ICollection<ProgramAssembly> Assemblies {get;set;}
}

public class ProgramAssembly
{
   public int Id {get;set;}
   public virtual Program Program {get;set;}
   public int ProgramId {get;set;}
}

我没有使用 FluentApi 指定任何内容(但是,我没有反对它)。
使用上面的代码,我得到了这个错误:

保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。

InnerException:无法确定相关操作的有效排序。由于外键约束、模型要求或存储生成的值,可能存在依赖关系。

我尝试将 ProgramAssembly 更改为以下内容

public class ProgramAssembly
{
    [ForeignKkey("Program")]
    public int Id {get;set;}
    public virtual Program Program {get;set;}
}

但是,我得到了这个错误:

ProgramAssembly_Program_Source::多重性在关系“ProgramAssembly_Program”中的角色“ProgramAssembly_Program_Source”中无效。因为从属角色是指关键属性,所以从属角色的多重性的上限必须是'1'。

我还尝试了以下流畅的 API 方法

modelBuilder.Entity<ProgramAssembly>()
   .HasRequired(a => a.Program)
   .WithMany(p => p.Assemblies);

public class ProgramAssembly
    {        
        public int Id { get; set; }
        public virtual Program Program { get; set; }
        [ForeignKey("Program")] //same error with or without this attribute
        public int ProgramId { get; set; }
   }

那么错误是:

保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。 UpdateException:无法确定相关操作的有效排序。由于外键约束、模型要求或存储生成的值,可能存在依赖关系。

如何在不改变在 Assembly 上添加“IsPrimary”属性的方法的情况下允许这种关系?

我在 SO 上看到了一些类似的问题,但它们要么是关于 EF 核心的,例如 this one,要么是建议的重大逻辑更改,例如 this one,甚至是甚至无法编译的 this one

【问题讨论】:

  • 你的模型能改变多少?我认为将IsPrimary 属性添加到Assembly 会比拥有两个关系看起来更好
  • @CamiloTerevinto - 谢谢。我可以对模型做任何我想做的事情,但是这种方法是我想要避免的。原因是我最终可能会将任意数量的程序集设置为主要程序集。另外,您如何看待访问程序的初级程序集?
  • 通过这种方式指定关系,您将无法做您想做的事情。为了获得类似的东西,您需要像 Camilo 所说的那样,向 ProgramAssembly 实体添加一个“IsMain”属性,如果您想在 Program 实体本身中获取它,您需要将 MainAssembly 属性更改为“public ProgramAssembly MainAssembly {get { return Assemblies.where(asm => asm.IsMain).FirstOrDefault(); }}
  • 那只是program.Assemblies.Single(x =&gt; x.IsMain)(或者您甚至可以将其作为导航属性),但是您必须在保存时验证它是对的。 EF Core 的答案应该使用 Fluent API 几乎相同。我很确定你不能通过 DataAnottations 做到这一点
  • @CamiloTerevinto - 谢谢。我尝试了 EF Core 方法,只是将“HasOne”更改为“HasRequired”,但没有奏效。

标签: c# entity-framework ef-code-first


【解决方案1】:

您需要告知 EF 您的主键和外键是如何工作的。您需要添加属性以定义主键 [Key] 并定义外键并将属性添加到导航属性以定义作为外键的字段。

public class Program
{
    [Key]
    public int Id {get;set;}
    public int MainAssemblyId {get;set;}
    [ForeignKey("MainAssemblyId ")]
    public virtual ProgramAssembly MainAssembly {get;set;}
    public virtual ICollection<ProgramAssembly> Assemblies {get;set;}
}

public class ProgramAssembly
{
   [Key]
   public int Id {get;set;}
   [ForeignKey("ProgramId")]
   public virtual Program Program {get;set;}
   public int ProgramId {get;set;}
}

【讨论】:

  • 嗯,到目前为止,代码给了我这个异常:Introducing FOREIGN KEY constraint 'FK_dbo.ProgramAssemblies_dbo.Programs_ProgramId' on table 'ProgramAssemblies' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
【解决方案2】:

以下代码将满足您的要求。

public class Program
{
    [Key]
    public int Id {get;set;}
    public virtual ICollection<ProgramAssembly> Assemblies {get;set;}
}

public class ProgramAssembly
{
   public int Id {get;set;}

   [ForeignKey("ProgramId")]
   public virtual Program Program {get;set;}
   public int ProgramId {get;set;}
   public bool IsMainProgramAssembly
}

如果需要,您可以将[Required] 用于ProgramId,或者您也可以将ProgramId 设为可为空。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 2017-02-13
    相关资源
    最近更新 更多