以下程序失败,但有例外。它运行必须是一个非常常见的用例,所以它似乎不太可能是框架中的错误(LINQ to SQL并不是全新的)。
有人能说出我做错了吗?
我知道总是可以选择转储当前的DataContext并继续使用新的DataContext,但在我的场景中这样做太浪费了(因为这会迫使我重新加载数千个实体)。
(我没有使用LINQ to SQL设计器,因为这段代码在某些时候也应该在WP7.1上运行)。
using System;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.IO;
sealed class Context : DataContext
{
internal Context(string databaseFile) : base(databaseFile)
{
this.Customers = this.GetTable<Customer>();
this.Orders = this.GetTable<Order>();
this.OrderDetails = this.GetTable<OrderDetail>();
}
internal readonly Table<Customer> Customers;
internal readonly Table<Order> Orders;
internal readonly Table<OrderDetail> OrderDetails;
}
[Table]
sealed class Customer
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
private int Id { get; set; }
[Column]
private int Whatever { get; set; }
private EntitySet<Order> orders;
public Customer()
{
this.orders = new EntitySet<Order>(
order => order.Associate(this), order => order.Associate(null));
}
[Association(Storage = "orders", OtherKey = "CustomerId")]
internal EntitySet<Order> Orders { get { return this.orders; } }
}
[Table]
sealed class Order
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
private int Id { get; set; }
[Column]
private int CustomerId { get; set; }
private EntityRef<Customer> customer;
private readonly EntitySet<OrderDetail> orderDetails;
public Order()
{
this.orderDetails = new EntitySet<OrderDetail>(
detail => detail.Associate(this),
detail => detail.Associate(null));
}
internal void Associate(Customer newCustomer) { this.customer.Entity = newCustomer; }
[Association(Storage = "customer", ThisKey = "CustomerId", IsForeignKey = true)]
internal Customer Customer { get { return this.customer.Entity; } }
[Association(Storage = "orderDetails", OtherKey = "OrderId")]
internal EntitySet<OrderDetail> OrderDetails { get { return this.orderDetails; } }
}
[Table]
sealed class OrderDetail
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
private int Id { get; set; }
[Column]
private int OrderId { get; set; }
private EntityRef<Order> order;
internal void Associate(Order newOrder) { this.order.Entity = newOrder; }
[Association(Storage = "order", ThisKey = "OrderId", IsForeignKey = true)]
internal Order Order { get { return this.order.Entity; } }
}
class Program
{
static void Main()
{
var exeDirectory = Path.GetDirectoryName(
typeof(Program).Assembly.ManifestModule.FullyQualifiedName);
var dataDirectory = Path.Combine(exeDirectory, Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(dataDirectory);
var dataFile = Path.Combine(dataDirectory, "DB.sdf");
using (var context = new Context(dataFile))
{
context.CreateDatabase();
// Insert a Customer
var customer = new Customer();
context.Customers.InsertOnSubmit(customer);
context.SubmitChanges();
// Insert the first Order
var order1 = new Order();
customer.Orders.Add(order1);
context.SubmitChanges();
// Insert the first OrderDetail
var detail1 = new OrderDetail();
order1.OrderDetails.Add(detail1);
context.SubmitChanges();
// Insert the second OrderDetail
order1.OrderDetails.Add(new OrderDetail());
context.SubmitChanges();
// Delete the first OrderDetail
context.OrderDetails.DeleteOnSubmit(detail1);
order1.OrderDetails.Remove(detail1);
// Everything works as expected up to this point. For all the
// changes above, context.GetChangeSet() has always been
// showing the expected changes.
// This succeeds. As expected, we now have a single Customer
// with a single Order and a single OrderDetail in the database.
context.SubmitChanges();
// Add a second Order
var order2 = new Order();
customer.Orders.Add(order2);
// The following fails with an InvalidOperationException with
// the message:
// An attempt was made to remove a relationship between a
// Customer and a Order. However, one of the relationship's
// foreign keys (Order.CustomerId) cannot be set to null.
//
// It is absolutely unclear why an attempt is made to
// delete/remove an Order. In the code above we're only
// ever deleting an OrderDetail, *not* an Order.
context.SubmitChanges();
}
}
}
答案 0 :(得分:2)
嗯,事实证明,当你没有命名你的关联时,LINQ to SQL并不特别喜欢它。要使上面的代码正常工作,需要将名称添加到所有Association属性,如下所示:
[Association(Name = "Customer_Order", Storage = "orders", OtherKey = "CustomerId")]
internal EntitySet<Order> Orders { get { return this.orders; } }
(当然,Order和OrderDetail之间的关联名称需要不同。)
在我的书中,这显然是一个错误。即使没有名称,它也很清楚什么是AssociationAttributes对形成了一个关联。创建的DB确认了这一点,LINQ to SQL显然能够完美地确定关联。但是,确定变更集的算法似乎在某种程度上混淆了这两种关联。
最后,如果LINQ to SQL要求设置Name属性,它应该通过抛出异常来进行通信。
答案 1 :(得分:0)
两个订单可能具有相同的主键。我没有在代码中看到AutoSync = System.Data.Linq.Mapping.AutoSync.OnInsert属性。数据库中的密钥在提交时可能没有恢复到第一个顺序。添加第二个将给你两个具有相同密钥的订单,这可能会破坏关系。