【问题标题】:Create one to one relationship optional on both sides in EF6在 EF6 中创建双方可选的一对一关系
【发布时间】:2015-05-22 16:14:57
【问题描述】:

有没有办法使用 Entity Framework 6 中的流式 API 将两个实体映射为双方可选的一对一关系?

代码示例

// Subscription (has FK OrderId)
this.HasOptional(t => t.Order)
    .WithOptionalDependent(t => t.Subscription)
    .HasForeignKey(d => d.OrderId); // does not compile

上下文:我为什么要这样做?我在一个现有系统中工作,其中有支付订单来购买订阅。当订单获得付款时,会创建订阅并将其关联,这意味着订阅对于订购是可选的。此外,还有其他创建订阅的方法,这意味着订阅的顺序是可选的。

【问题讨论】:

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


【解决方案1】:

通常在一对一(或零)关系中,两个实体共享相同的 PK,在依赖关系中,PK 也被指定为 FK。检查此link 以获取有关此的更多信息。但是,如果您的实体不共享相同的 PK,则您无法在依赖实体中添加 FK 属性。如果你这样做,EF 将抛出一个与多重性相关的异常,说它必须是 *。

关于关系的配置,只有一种方法可以将双方的一对一关系配置为可选,这是您目前使用 Fluent Api 所拥有的。这样,您还可以使用 Map 方法将 EF 在依赖表中按约定创建的 FK 列重命名为您在 DB 中的 Subscription 表中已有的名称。

更新

如果您没有绑定到现有数据库,您可以执行以下操作:

public class Subscription
{
    public int SubscriptionId { get; set; }

    public int? OrderId { get; set; }
    public virtual Order Order { get; set; }
}

public class Order
{
    public int OrderId { get; set; }
    public int SubscriptionId { get; set; }

    public virtual Subscription Subscription { get; set; }
}

配置是这样的:

 modelBuilder.Entity<Subscription>()
            .HasOptional(s => s.Order)
            .WithMany()
            .HasForeignKey(s=>s.OrderId);

 modelBuilder.Entity<>(Order)
            .HasOptional(s => s.Subscription)
            .WithMany()
            .HasForeignKey(s=>s.SubscriptionId);

通过这种方式,您可以使用 OrderIdFK(以及 SubscriptionId),就像它是一对一的关系一样。这里的问题是您必须分别设置和保存两个关联。

【讨论】:

  • 使用Map 我不允许定义一个属性OrderId 来处理FK,所以我将不得不更改很多在linq 查询中使用subscription.OrderId 的代码。有解决方法吗?感谢您的帮助。
  • 你确定你有一对一的关系吗?如果我能看到这两个表的图表会很有帮助,
  • 引用我自己的话:“上下文:我为什么要这样做?我在一个现有系统中工作,其中有支付订单来购买订阅。当订单获得支付时,订阅就会创建并与之关联,这意味着订阅是可选的。此外,还有其他方法可以创建订阅,这意味着订阅是可选的“
  • 并定义业务:一单订阅(若有),一单付款后,生成一订阅。
  • 急诊室是这样的:[Subscription] 0..1 ----- 0..1 [Order]
【解决方案2】:

请在数据库上下文类中尝试此代码

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Subscription>()
            .HasOptional(x => x.Order)
            .WithOptionalPrincipal()
            .Map(x => x.MapKey("SubscriptionId"));

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

    }

我的测试模型如下

public class Order
{
    [Key]
    public int OrderId { get; set; }
    public string Description { get; set; }

    public virtual Subscription Subscription { get; set; }
}
public class Subscription
{
    [Key]
    public int SubscriptionId { get; set; }

    public virtual Order Order { get; set; }
}

编辑: 我对数据库进行了逆向工程,试图通过使用双 1 到多关系来达到所需的代码结构,以便像你想要的那样工作。生成的代码如下所示。但是,这样做是个坏主意。

public partial class Order
{
    public Order()
    {
        this.Subscriptions = new List<Subscription>();
    }

    public int OrderId { get; set; }
    public string Description { get; set; }
    public Nullable<int> SubscriptionId { get; set; }
    public virtual Subscription Subscription { get; set; }
    public virtual ICollection<Subscription> Subscriptions { get; set; }
}
public partial class Subscription
{
    public Subscription()
    {
        this.Orders = new List<Order>();
    }

    public int SubscriptionId { get; set; }
    public Nullable<int> OrderId { get; set; }
    public virtual ICollection<Order> Orders { get; set; }
    public virtual Order Order { get; set; }
}
public class OrderMap : EntityTypeConfiguration<Order>
{
    public OrderMap()
    {
        // Primary Key
        this.HasKey(t => t.OrderId);

        // Properties
        // Table & Column Mappings
        this.ToTable("Orders");
        this.Property(t => t.OrderId).HasColumnName("OrderId");
        this.Property(t => t.Description).HasColumnName("Description");
        this.Property(t => t.SubscriptionId).HasColumnName("SubscriptionId");

        // Relationships
        this.HasOptional(t => t.Subscription)
            .WithMany(t => t.Orders)
            .HasForeignKey(d => d.SubscriptionId);

    }
}
public class SubscriptionMap : EntityTypeConfiguration<Subscription>
{
    public SubscriptionMap()
    {
        // Primary Key
        this.HasKey(t => t.SubscriptionId);

        // Properties
        // Table & Column Mappings
        this.ToTable("Subscriptions");
        this.Property(t => t.SubscriptionId).HasColumnName("SubscriptionId");
        this.Property(t => t.OrderId).HasColumnName("OrderId");

        // Relationships
        this.HasOptional(t => t.Order)
            .WithMany(t => t.Subscriptions)
            .HasForeignKey(d => d.OrderId);

    }
}
public partial class EFOrdersContextContext : DbContext
{
    static EFOrdersContextContext()
    {
        Database.SetInitializer<EFOrdersContextContext>(null);
    }

    public EFOrdersContextContext()
        : base("Name=EFOrdersContextContext")
    {
    }

    public DbSet<Order> Orders { get; set; }
    public DbSet<Subscription> Subscriptions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new OrderMap());
        modelBuilder.Configurations.Add(new SubscriptionMap());
    }
}

【讨论】:

  • 它不起作用,实体框架抱怨:Subscription_Order_Source: : Multiplicity 在关系“Subscription_Order”中的角色“Subscription_Order_Source”中无效。因为从属角色属性不是关键属性,所以从属角色的多重性的上限必须是“*”。
  • 它只有在你隐藏OrderId pk 时才有效,这是我做不到的。无论如何,谢谢你的帮助。
猜你喜欢
  • 1970-01-01
  • 2011-12-02
  • 1970-01-01
  • 2020-04-08
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多