37

我有两个 POCO 课程:

订单类别:

public class Order
{
    public int Id { get; set; }
    public int? QuotationId { get; set; }
    public virtual Quotation Quotation { get; set; }
    ....
}

报价类:

public class Quotation
{
    public int Id { get; set; } 
    public virtual Order Order { get; set; }
    ....   
}
  • 每一个Order 都可以由一个或零个引用组成,并且
  • 每个报价都可能导致一个订单。

所以我有一个“一或零”到“一或零”的关系,我该如何通过API在EF代码中实现这一点?Fluent

4

7 回答 7

38

通过将 pocos 更改为:

public class Order
{
    public int OrderId { get; set; }
    public virtual Quotation Quotation { get; set; }
}
public class Quotation
{
    public int QuotationId { get; set; }
    public virtual Order Order { get; set; }
}

并使用这些映射文件:

public class OrderMap : EntityTypeConfiguration<Order>
{
    public OrderMap()
    {
        this.HasOptional(x => x.Quotation)
            .WithOptionalPrincipal()
            .Map(x => x.MapKey("OrderId"));
    }
}

public class QuotationMap : EntityTypeConfiguration<Quotation>
{
    public QuotationMap()
    {
        this.HasOptional(x => x.Order)
            .WithOptionalPrincipal()
            .Map(x => x.MapKey("QuotationId"));
    }
}

我们将拥有这个数据库(这意味着 0..1-0..1):

在此处输入图像描述

特别感谢(瓦希德·纳西里先生

于 2013-02-11T19:18:29.560 回答
28

@Masoud 的程序是:

modelBuilder.Entity<Order>()
            .HasOptional(o => o.Quotation)
            .WithOptionalPrincipal()
            .Map(o => o.MapKey("OrderId"));

modelBuilder.Entity<Quotation>()
            .HasOptional(o => o.Order)
            .WithOptionalPrincipal()
            .Map(o => o.MapKey("QuotationId"));

它给:

在此处输入图像描述

通过将代码更改为:

modelBuilder.Entity<Order>()
            .HasOptional(o => o.Quotation)
            .WithOptionalPrincipal(o=> o.Order);

它给:

在此处输入图像描述

于 2014-11-07T06:38:27.800 回答
8

请参阅http://msdn.microsoft.com/en-us/data/jj591620 EF 关系

一本优秀的书 http://my.safaribooksonline.com/book/-/9781449317867

这是 2010 年 12 月开发人员的帖子。但仍然相关 http://social.msdn.microsoft.com/Forums/uk/adonetefx/thread/aed3b3f5-c150-4131-a686-1bf547a68804 上面的文章是一个很好的总结或这里的可能组合。

依赖表具有来自主表的键的解决方案是可能的。

如果您想要在 PK/FK 场景中两者都是主体的独立密钥,我认为您不能使用 Fluent API 在代码中执行此操作。如果他们共享一个密钥,你就可以了。1:1 可选假设依赖项使用来自 Primary 的密钥。

但是因为您需要先保存其中一个表。您可以使用代码检查其中一个外键。或者在代码首先创建它之后将第二个 Foreign 添加到数据库。

你会靠近的。但是,如果您希望两者都是外键,EF 会抱怨外键冲突。本质上,A 取决于 B 取决于 A EF 不喜欢,即使这些列在 DB 上可以为空并且在技术上是可行的。

这里使用这个测试程序来试一试。只需注释掉 Fluent API 的内容即可尝试一些选项。 我无法让 EF5.0 与 INDEPENDENT PK/FK 0:1 到 0:1 一起工作, 但当然有一些合理的折衷方案,正如所讨论的那样。

using System.Data.Entity;
using System.Linq;
namespace EF_DEMO
{
class Program
{
    static void Main(string[] args) {
        var ctx = new DemoContext();
        var ord =  ctx.Orders.FirstOrDefault();
        //. DB should be there now...
    }
}
public class Order
{
public int Id {get;set;}
public string Code {get;set;}
public int? QuotationId { get; set; }   //optional  since it is nullable
public virtual Quotation Quotation { get; set; }
  //....
}
public class Quotation
{
 public int Id {get;set;}
 public string Code{get;set;}
// public int? OrderId { get; set; }   //optional  since it is nullable
 public virtual Order Order { get; set; }
 //...
}
public class DemoContext : DbContext
{
    static DemoContext()
    {
    Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DemoContext>());
    }
    public DemoContext()
        : base("Name=Demo") { }
    public DbSet<Order> Orders { get; set; }
    public DbSet<Quotation> Quotations { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       modelBuilder.Entity<Order>().HasKey(t => t.Id)
                    .HasOptional(t => t.Quotation)
                    .WithOptionalPrincipal(d => d.Order)
                    .Map(t => t.MapKey("OrderId"));  // declaring here  via MAP means NOT declared in POCO
        modelBuilder.Entity<Quotation>().HasKey(t => t.Id)
                    .HasOptional(q => q.Order)
            // .WithOptionalPrincipal(p => p.Quotation)  //as both Principals
            //        .WithOptionalDependent(p => p.Quotation) // as the dependent
            //         .Map(t => t.MapKey("QuotationId"));    done in POCO.
            ;
    }   
}
}
于 2013-02-05T13:43:11.260 回答
5

改编自这个答案,试试这个。

首先,修复你的类:

public class Order
{
  public int Id {get; set;}
  public virtual Quotation Quotation { get; set; }
  // other properties
}

public class Quotation
{
  public int Id {get; set;}
  public virtual Order Order { get; set; }
  // other properties
}

然后像这样使用流利的API:

modelBuilder.Entity<Quotation>()
.HasOptional(quote => quote.Order)
.WithRequired(order=> order.Quotation);

基本上,对于 1:1 或 [0/1]:[0/1] 关系,EF 需要共享主键。

于 2013-02-05T13:34:02.420 回答
1
public class OfficeAssignment
{
    [Key]
    [ForeignKey("Instructor")]
    public int InstructorID { get; set; }
    [StringLength(50)]
    [Display(Name = "Office Location")]
    public string Location { get; set; }

    public virtual Instructor Instructor { get; set; }
}

关键属性

Instructor 和 OfficeAssignment 实体之间存在一对零或一的关系。办公室分配仅与分配给它的讲师相关,因此它的主键也是其对 Instructor 实体的外键。但实体框架无法自动将 InstructorID 识别为该实体的主键,因为它的名称不遵循 ID 或 classnameID 命名约定。因此,使用 Key 属性将其标识为键:

https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net- mvc-应用程序

于 2016-04-28T02:59:57.603 回答
0

使用数据注释:

public class Order
{
       [Key]
       public int Id {get; set;}

       public virtual Quotation Quotation { get; set; }
}

public class Quotation
{
     [Key, ForeignKey(nameof(Order))]
     public int Id {get; set;}

     public virtual Order Order { get; set; }
}
于 2020-01-29T06:24:08.673 回答
0

(请注意,这是使用 EF 6.4.4。)

只要您不想要外键属性,指定起来就相当简单:

modelBuilder
.Entity<Order>()
.HasOptional(o => o.Quotation)
.WithOptionalPrincipal(q => q.Order);

modelBuilder
.Entity<Quotation>()
.HasOptional(q => q.Order)
.WithOptionalDependent(o => o.Quotation);

WithOptionalPrincipal请注意这里和的用法WithOptionalDependent。这应该在依赖端为您提供一个外键列(示例中的引号),但没有外键属性。如果您想要另一边的外键,请切换“依赖”和“主体”。

(请注意,没有必要同时拥有以上两个定义;WithOptionalDependent这意味着另一方是主体,反之亦然,因此如果需要,您可以只使用其中一个,但我发现指定双方的关系有助于防止错误通过双重声明;任何冲突都会导致模型错误,让你知道你错过了一些东西。)

虽然外键列上有索引,但该索引没有唯一约束。虽然可以添加自己的唯一约束(这需要Key IS NOT NULL过滤器),但它似乎不起作用,并且在某些情况下更新关系时会出现异常。我认为这与 EF 将在单独的查询中执行其更新的“交换问题”有关,因此强制唯一性将阻止 EF 分两步“移动”密钥。

EF 似乎在内部处理关联本身,没有唯一的 DB 约束:

  • 在任一方面,分配已使用的引用会导致引用的其他用法被自动删除。(所以如果你打开上下文的时候已经是A1 <=> B1,然后你写A1 => B2,那么A1 <=> B1被去掉,A1 <=> B2被添加,不管哪边轮到你了。)
  • 如果您尝试通过多次分配相同的引用来创建重复键,EF 将抛出一个异常,说“违反多重约束”。(所以在相同的上下文中,您同时编写了 A1 => B1 和 A2 => B1,或者一些类似的冲突映射。)
  • 如果您手动更新数据库以创建重复键的情况,当 EF 遇到这种情况时,它会抛出一个异常,说“发生关系多重性约束违规......这是一个不可恢复的错误。”

在 EF6 中似乎不可能将属性映射到外键列(至少使用 Fluent API)。尝试这样做会导致非唯一的列名异常,因为它尝试分别对属性和关联使用相同的名称。

另请注意,拥有两个外键(即:两边各一个)在技术上是不正确的。这样的安排实际上是两个0..1 到 0..1 的关联,因为没有什么可以说两端的键应该匹配。如果您通过 UI 和/或某种数据库约束以其他方式强制执行关系,这可能会起作用。

我还注意到,对于 0..1 到 0..1 关联究竟是什么,可能存在误解/误解。这意味着,根据我的理解以及 EF 似乎也考虑它的方式,它是一个 1 对 1 的关联,双方都是可选的。因此,您可以在任何一方拥有没有关系的对象。(而 1 到 0..1 的关联,一侧的对象可以在没有关系的情况下存在,但另一侧的对象总是需要一个对象来关联。)

但是 0..1 到 0..1 并不意味着您可以让关联在一个方向而不是另一个方向上传播。如果 A1 => B1,则 B1 => A1 (A1 <=> B1)。如果不使 A1 与 B1 相关,则无法将 B1 分配给 A1。这就是为什么这个关联可以只使用一个外键。我认为有些人可能试图建立一个不正确的关联(A1 与 B1 相关,但 B1 与 A1 无关)。但这实际上不是一个关联,而是两个 0..1 到 0..1 关联。

于 2021-09-19T20:55:21.513 回答