【问题标题】:How do I Create multiple not null properties of same type using fluent mapping in Entity Framework Code First如何在 Entity Framework Code First 中使用流利的映射创建相同类型的多个非空属性
【发布时间】:2013-03-01 11:23:59
【问题描述】:

我正在使用带有代码优先和流畅映射方法的 EF5。这是我的场景,为了清楚起见,进行了精简:

public class SecuritySettings
{
   public int Id { set; set }
   public Company Company { get; set; }
   public int MaximumInvalidPasswordAttempts { get; set;
   // etc.
}

public class Company
{
   public int Id { set; set }
   public string Name { get; set; }
   public SecuritySettings InternalUserSecuritySettings { get; set; }
   public SecuritySettings ExternalUserSecuritySettings { get; set; }
}

仅凭上述内容,我在 Company 表中获得了两个 FK:

   InternalUserSecuritySettings_Id int, null
   ExternalUserSecuritySettings_Id int, null

到目前为止,一切都很好。

我有用于设置流利映射的配置类 - 一个用于公司,一个用于 SecuritySettings。这些规定了所需的属性。在公司配置中,如果我执行以下操作:

HasRequired(x => x.InternalUserSecurityConfiguration);

这会导致:

InternalUserSecuritySettings_Id int,不为空 ExternalUserSecuritySettings_Id int, null

但是,如果我再添加这个:

HasRequired(x => x.ExternalUserSecurityConfiguration);

我得到以下异常:

在表“Companies”上引入 FOREIGN KEY 约束“FK_dbo.Companies_dbo.SecuritySettings_ExternalUserSecurityConfiguration_Id”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

我试过了:

  1. .WithRequiredDependent(y => y.Company).WillCascadeOnDelete(false)
  2. .WithRequiredDependent(y => y.Company).Map(m => m.MapKey("ExternalUSC_Id"))

在其他此类排列中,但要么我得到一个异常,要么只创建了一个 FK。 我想在实体配置的流畅映射中执行此操作 - 最好是公司之一。 我是否遗漏了一些明显的东西。从使用非空值如此简单和琐碎的事情到发现很难简单地规定我想要一个密钥,并且两者都不能为空值,这似乎是一种耻辱。

我确实找到了一种方法,通过为实际的外键使用单独的属性来使其工作,但首先,对于非空要求来说,这似乎是一项额外的工作,其次,我必须在OnModelCreating 方法使用“WithMany()”映射,这对我来说是违反直觉的!

【问题讨论】:

    标签: entity-framework ef-code-first


    【解决方案1】:

    您可以使用以下流利的方法使其与非空属性一起使用:

            modelBuilder.Entity<Company>().
                HasRequired(x => x.InternalUserSecuritySettings)
                .WithMany()
                .WillCascadeOnDelete(false);
    
            modelBuilder.Entity<Company>().
                HasRequired(x => x.ExternalUserSecuritySettings)
               .WithMany()
                .WillCascadeOnDelete(false);
    

    产生这个数据库:

    但是 - 我有点担心您的 SecuritySettings 实体上的 Company 属性。您是否希望该导航属性映射到内部/外部SecuritySettings 属性的另一端?如果是这样 - 这不能用 EF 完成(你不能在一个实体上拥有一个属性,映射到其他 2 个属性)。考虑原因:如果您将 Company 添加到 SecuritySetting -

    var CompanyA = new Company;
    CompanyA.SecuritySettings(new SecuritySettings());
    

    它应该映射到SecuritySettings 实体的哪一端(内部还是外部?)因为无法通过上述场景判断它是无效的。

    您可以将 2 个不同的 SecuritySettings 集合添加到您的公司,然后您将获得正确的映射。

    【讨论】:

      【解决方案2】:

      RE:Mark Oreta - 当我查看源代码控制历史时,Company 属性最初并不存在 - 它一定是我的“让我们试试这个看看会发生什么”的尝试之一!我已经删除了它,因为我不需要在公司环境中引用安全设置。我确实有一个与您类似的版本,但我将您的版本作为答案,因为我能够从您的答案中引导至少能够在我的 CompanyConfiguration 类中添加所有映射,而不是在 OnModelCreating 方法中.现在唯一的痛点是这些仍然是一对一的映射,WithMany() 不会推断或强制执行。因此,我可以强制执行此操作的唯一方法是使用独特的约束。不是很漂亮!感谢您的时间和回答。

      编辑:

      只是为了分享我对这个问题的当前答案:

      1. 公司配置类(DataEntityBaseConfiguration继承自EntityTypeConfiguration)
      public class CompanyConfiguration : DataEntityBaseConfiguration<Company>
         {
          public CompanyConfiguration()
          {
              Property(x => x.Name).IsRequired();
              Property(x => x.Name).HasMaxLength(256);
              HasRequired(x => x.InternalUserSecurityConfiguration).WithMany().WillCascadeOnDelete(false);
              HasRequired(x => x.ExternalUserSecurityConfiguration).WithMany().WillCascadeOnDelete(false);
      
          }
         }
      

      然后,在我的 Context Initialiser 类中,我添加以下代码行以对两个属性强制执行唯一约束:

      AddUniqueConstraint<Company>(() => new Company().ExternalUserSecurityConfigurationId);
      AddUniqueConstraint<Company>(() => new Company().InternalUserSecurityConfigurationId);
      

      AddUniqueConstraint 是我编写的用于执行 sql 以创建唯一约束的方法 - 关注 here 以了解更多信息。

      【讨论】:

        猜你喜欢
        • 2012-08-09
        • 2016-02-14
        • 2011-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多