0

I am using entity framework 4, WCF and Silverlight.

I have a many to many relationship of Order to Products.

 [Order]*--*[Product]

Using Model first, Entity Framework automatically creates the join table for me. When I create everything on the server side, things work ok. The problem comes when using Silverlight.

I bring back the list of products and display them on the screen. The user creates a new Order, and selects the Products that should be apart of the order. Then it is submitted to WCF.

var order = new order();
order.CustomerName = customerTextBox.Text;
...
order.Products = new ObservableCollection<Product>();

foreach (var product in selectedProducts)
{
   order.Products.Add(product);
}
var proxy = new OrderService();
proxy.CreateOrderAsync(order);

On the server side, it is just a very simple add method

var db = new Model1Container();
db.Products.AddObject(order);
db.SaveChanges();

However something strange happens. When this is saved, almost everything in the database gets duplicated a few times. My guess would be because Entity Framework is traversing the cyclic links a few times.

I tried changing it to db.Categories.Attatch(order). But this resulted in nothing being saved. I also tried looping and manually attaching each of the products before saving the order. But this results in an exception being thrown, stating that objects have already been attached to the context.


Edit: Doing this stops Products being duplicated. But all the Orders in the table are still duplicated. Must be traversing all the relationships still?

db.Orders.AddObject(order);
db.ObjectStateManager.ChangeObjectState(order, EntityState.Added);

foreach (var product in order.Products)
{
    db.ObjectStateManager.ChangeObjectState(product, EntityState.Unchanged);
}

db.SaveChanges();
David Burela
  • 444
  • 3
  • 11

2 Answers2

1

Try this instead:

var db = new Model1Container();
db.Categories.Attach(order);
db.ObjectStateManager.ChangeObjectState(order, EntityState.Added);
db.SaveChanges();

The problem is that AddObject inserts every untracked entity in object graph. When using EF you must explicitly say EF what has changed. You will still have problems if you let users modify their orders.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Didn't effect anything. Everything in both the Order and Product tables are still duplicated – David Burela Apr 28 '11 at 13:36
  • I took what you suggested, and extended it a bit. Now products aren't duplicated, but all the orders in the table are still duplicated. db.Orders.AddObject(order); db.ObjectStateManager.ChangeObjectState(order, EntityState.Added); foreach (var product in order.Products) { db.ObjectStateManager.ChangeObjectState(product, EntityState.Unchanged); } db.SaveChanges(); – David Burela Apr 28 '11 at 13:59
  • What do you mean by all orders? You are inserting single order, don't you? – Ladislav Mrnka Apr 28 '11 at 14:08
  • Yes. But something crazy happens. And when I add that single order (without the above modification) everything in the products table seems to be duplicated, and everything in the Orders table is duplicated. It is like it is traversing all the relationships and re-adding everything. Even though they have existing Id. – David Burela Apr 28 '11 at 14:50
  • Well this smells. Can you use SQL profiler and check what is going on under the hood? How are WCF-RIA-Services related to this problem? – Ladislav Mrnka Apr 29 '11 at 07:22
  • I've only got SQL management studio express installed, so no profiler. And RIA is in no was related to this at all, the other poster was just misleading the conversation. – David Burela Apr 29 '11 at 10:51
  • It is still having issues. I just changed to a 1-to-many for this scenario. But will mark this as the closest answer. Thanks for helping @Ladislav – David Burela Apr 29 '11 at 12:05
0

Are you using WCF RIA? If not, you should consider it. The DomainServices take a way a lot of the pain.

Also, why don't you create a table 'order_position'? Like this you don't have a many to many relationship anymore. One order_position has only one order and only one product. You can also assign a price to it, which makes it save for use in statistics (what happens when you want to see how much money you made of product A, but you changed its price in the database a few times?).

LueTm
  • 2,366
  • 21
  • 31
  • I use RIA in all other scenarios. In this case I need to use it. Also Entity Framework already creates the join table under the covers, so that it isn't *really* a many-to-many relationship. And yes I know the limitations of not having quantity, etc, in the join table. I renamed the tables to product/order for simplicity. – David Burela Apr 28 '11 at 13:36
  • OK. The reason I'd use an order_position table anyway is that such problems get really easy and you're program is stays scalable. You would simply do: Context = new Model1DomainContext(); order = new order(); Context.orders.Add(order); ... foreach (var product in products) { product.Order = order; Context.products.Add(order); } var submitOperation = Context.SubmitChanges(); submitOperation.Completed += ... – LueTm Apr 28 '11 at 13:39
  • this is actually a very specific, once off example to demonstrate to people a way that it *could* be done, before showing them the easy way of doing it with Entity Framework. – David Burela Apr 28 '11 at 13:43