为什么我必须手动提供外键属性值(在这种情况下)?

时间:2013-01-28 00:41:32

标签: entity-framework .net-4.0 ef-code-first entity-framework-5

当我有这样的模特......

public class Order
{
    [Key, Column(Order = 1)]
    public int CustomerId { get; set; }
    [Key, Column(Order = 2)]
    public int OrderId { get; set; }

    public ICollection<OrderItem> Items { get; set; }
}

public class OrderItem
{
    [Key, Column(Order = 1)]
    public int CustomerId { get; set; }
    [Key, Column(Order = 2)]
    public int OrderId { get; set; }
    [Key, Column(Order = 3)]
    public int OrderItemId { get; set; }
}

...当我向数据库添加OrderItem.CustomerId图表时,我不需要手动提供外键值OrderItem.OrderIdOrder - OrderItems

var order = new Order
{
    CustomerId = 5,
    OrderId = 1,
    Items = new List<OrderItem>
    {
        // I don't need to set CustomerId and OrderId here
        new OrderItem { OrderItemId = 12 }
    }
};
context.Orders.Add(order);
context.SaveChanges();

SaveChanges将生成SQL语句以插入OrderOrderItem。对于OrderItem,它是:

exec sp_executesql N'insert
[dbo].[OrderItems]([CustomerId], [OrderId], [OrderItemId])
values (@0, @1, @2)
',N'@0 int,@1 int,@2 int',@0=5,@1=1,@2=12

因此,FK CustomerIdOrderId已正确设置(@0=5@1=1)。

但是,如果我通过向Customer添加导航属性,将Order实体的另一个关系添加到模型中......

public Customer Customer { get; set; }

...像这样的Customer类......

public class Customer
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int CustomerId { get; set; }
}

...并调用与上面相同的代码(假设数据库中存在Customer CustomerId = 5)我得到以下SQL:

exec sp_executesql N'insert
[dbo].[OrderItems]([CustomerId], [OrderId], [OrderItemId])
values (@0, @1, @2)
',N'@0 int,@1 int,@2 int',@0=0,@1=1,@2=12

几乎相同,但在这种情况下CustomerId0@0=0)会导致Order - OrderItem关系违反外键约束。如果存在Customer CustomerId = 0且此客户的Order OrderId = 1,则不会发生任何异常, OrderItem会被添加到错误的订单中,这可能比例外更糟糕。

可以通过在新CustomerId中设置OrderItem(这是FK的一部分)来解决问题:

Items = new List<OrderItem>
{
    // I don't need to set OrderId here, BUT CustomerId
    new OrderItem { CustomerId = 5, OrderItemId = 12 }
}

或者 - 可以通过将具有Id 5的客户附加到上下文来修复它(我不知道它为什么会起作用)。

是否有合理的解释为什么我必须手动提供FK(或本例中的部分FK)或这是一个错误?

(我已将EF 5.0与.NET 4.0一起用于此模型。)

1 个答案:

答案 0 :(得分:0)

你的模型对我来说很奇怪,我怀疑会引起你的困惑。

基于我所看到的我猜你有客户可以拥有多个订单,每个订单应该只有每种类型的OrderItem,但可以有许多OrderItems。一个订单不能属于多个客户。一个OrderItem可以属于多个订单。

如果这是真的那么我认为你需要一个类似的模型;

public class Customer
    {
        //Is the key by naming convention or you could use [Key]
        public Guid CustomerId { get; set; }
        public ICollection<Order> Orders { get; set; } 
    }
    public class Order
    {
        //Is the key by naming convention or you could use [Key]     
        public Guid OrderId { get; set; }
        public Guid CustomerId { get; set; } //This doesn't need to be part of the key 
        public Customer Customer { get; set; }
        public ICollection<OrderItem> OrderItems { get; set; } 
    }
    public class OrderItem
    {
        [Key, Column(Order = 0)]
        public Guid OrderItemId { get; set; }
        [Key, Column(Order=1)]
        public Guid OrderId { get; set; }
        public ICollection<Order> Orders { get; set; }
    }

然后,当您需要查询是否需要了解客户的OrderItems时,您可以使用Include或使用数据投影(例如

)来实现
OrderItems.Select(x=>new { 
CustomerId = x.Order.Customer.Id,
//this rest of your properties
});

或者如果你想要整个实体

  OrderItems.Include(x=>x.Order.Customer)