Adding & Removing Associations - Entity Framework - entity-framework

I'm trying to get to grips with EF this week and I'm going ok so far but I've just hit my first major snag. I have a table of items and a table of categories. Each item can be 'tagged' with many categories so I created a link table. Two columns, one the primary ID of the item, the other the primary ID of the category. I added some data manually to the DB and I can query it all fine through EF in my code.
Now I want to 'tag' a new item with one of the existing categories. I have the category ID to add and the ID of the Item. I load both as entities using linq and then try the following.
int categoryToAddId = Convert.ToInt32(ddlCategoriesRemaining.SelectedValue);
var categoryToAdd = db.CollectionCategorySet.First(x => x.ID == categoryToAddId);
currentCollectionItem.Categories.Add(categoryToAdd);
db.SaveChanges();
But I get "Unable to update the EntitySet 'collectionItemCategories' because it has a DefiningQuery and no element exists in the element to support the current operation."
Have I missed something? Is this not the right way to do it? I try the same thing for removing and no luck there either.

I think I have managed to answer this one myself. After alot of digging around it turns out that the Entity Framework (as it comes in VS2008 SP1) doesn't actually support many to many relationships very well. The framework does create a list of objects from another object through the relationship which is very nice but when it comes to adding and removing the relationships this can't be done very easily. You need to write your own stored procedures to do this and then register them with Entity Framework using the Function Import route.
There is also a further problem with this route in that function imports that don't return anything such as adding a many to many relationship don't get added to the object context. So when your writing code you can't just use them as you would expect.
For now I'm going to simply stick to executing these procedures in the old fashioned way using executenonquery(). Apparently better support for this is supposed to arrive in VS2010.
If anyone feels I have got my facts wrong please feel free to put me right.

After you have created your Item object, you need to set the Item object to the Category object on the Item's Categories property. If you are adding a new Item object, do something like this:
Using (YourContext ctx = new YourContext())
{
//Create new Item object
Item oItem = new Item();
//Generate new Guid for Item object (sample)
oItem.ID = new Guid();
//Assign a new Title for Item object (sample)
oItem.Title = "Some Title";
//Get the CategoryID to apply to the new Item from a DropDownList
int categoryToAddId = Convert.ToInt32(ddlCategoriesRemaining.SelectedValue);
//Instantiate a Category object where Category equals categoryToAddId
var oCategory = db.CategorySet.First(x => x.ID == categoryToAddId);
//Set Item object's Categories property to the Category object
oItem.Categories = oCategory;
//Add new Item object to db context for saving
ctx.AddtoItemSet(oItem);
//Save to Database
ctx.SaveChanges();
}

Have you put foreign keys on both columns in your link table to the item and the category or defined the relationship as many to many in the Mapping Details?

Related

Attaching an related entity to new entity, without retrieving from database

I've just started working with Web API this week, and I'm struggling with something which I think should be quite simple, but haven't been able to find the answer for yet. Perhaps I'm searching using the wrong terms.
One of the calls to the API passes through a GUID. I need to create a new entity (using Entity Framework) and set one of the relations to this newly passed in GUID. This GUID is the ID of a different entity in the database.
I'm struggling to attach the entity via the relation without fetching the whole entity too.
For example,
public void DoWork(IList<Guid> userGuids)
{
Order order = new Order() // This is an entity
{
CreateDate = DateTime.Now,
CreatedBy = "Me",
Items = (from i in this.Model.Items
where i.Id == userGuid
select i).ToList<Item>();
}
Model.Orders.Add(order);
Model.SaveAll();
}
In the above, I have to do a database call to attach the Item entities to the Order. Is there not a way around this? Seems very redundant to retrieve the whole entity objects when I only require their IDs (which I already have anyway!)!
One solution is stub entities as asked here: Create new EF object with foreign key reference without loading whole rereference object
Link to the source blog referenced: http://blogs.msdn.com/b/alexj/archive/2009/06/19/tip-26-how-to-avoid-database-queries-using-stub-entities.aspx
Snip from the blog - to be applied to your situation:
Category category = new Category { ID = 5};
ctx.AttachTo(“Categories”,category);
Product product = new Product {
Name = “Bovril”,
Category = category
};
ctx.AddToProducts(product);
ctx.SaveChanges();
This way (in the example) the Product is saved without ever loading the Category object.

Entity Framework 5 Foreign Key New Record on SaveChanges

I'm using .NET4.5/EF5 and have created the model from an existing database.
I'm using the following code:
Order currentOrder = new Order();
using (var db = new ILSEntities())
{
try
{
Event currentEvent = db.Events.OrderByDescending(u => u.EventID).FirstOrDefault();
currentOrder.Event = currentEvent;
db.Orders.Add(currentOrder);
db.SaveChanges();
And I'm seeing that a duplicate record is being created of the Event object I find, which is not what I wanted to happen.
I've read a lot of posts relating to similar problems, but where the context of the two participants in the foreign key relationships are different. Here, I'm saving with the same context I use to find one, and the other object is new.
I've also tried:
currentOrder.Event.EventID = currentEvent.EventID;
but that fails as well as I get an EF validation error telling me it needs values for the other members of the Event object.
I've also tried specifically setting the EntityState of the object being duplicated to Detached, Modified etc. after adding the Order object but before SaveChanges without success.
I'm sure this is a basic problem, but it's got me baffled
In my understanding, both parent and child objects have to be in the context before you assign any relationship between them to convince the entity framework that an entity exists in the database already. I guess you are trying to add new Order object to Database, to add new object you should be using AddObject method, Add() method is used to establish relation between entitties. In your code, currentOrder is not in the context. Try to hook it in the same context and then assign a relation. Your code should look like this :
Order currentOrder = new Order();
using (var db = new ILSEntities())
{
try
{
Event currentEvent = db.Events.OrderByDescending(u => u.EventID).FirstOrDefault();
db.Orders.Attach(currentOrder); //attach currentOrder to context as it was not loaded from the context
currentOrder.Events.Add(currentEvent);//establish relationship
db.ObjectStateManager.ChangeObjectState(currentOrder, EntityState.Added);
db.SaveChanges();
}
}
OK, I did in the end figure this out, and it was my fault.
The problem was that the Order object is FK'd into another table, Shipments, which is also FK'd into Events. The problem was that it was the Event reference in the Shipment object that was causing the new record. The solution was to let EF know about these relationships by adding them all within the same context.
The code assembling the object graph was spread over a number of webforms and the responses here made me take a step back and look at the whole thing critically so whilst no one of these answers is correct, I'm voting everybody who replied up

Adding object to navigation property collection creates new entity

I am using Entity Framework 4.
I am trying to associate a new entity with an existing entity. The system ends up creating a new child entity when in fact I just want to add a reference to the child object to the parent.
There is a many to many relationship between the two entities so I cannot simply set the FK property of the parent entity. I have tried parent.ChildCollection.Add(child) which simply creates a new child object in the database. This is what I am trying to avoid.
I must be doing something obviously wrong.
thanks
updated code sample
Code sample for my Self-Tracking-Entities that I have to do client side
Right now I have something like this to get all children from server then loop through to find the one i want, then add it to the object collection
List<Service.Child> childs = _client.GetChildren();
I have to loop through that collection to find the right one to add to the parent.childs collection ie.
List<Service.Child> childList = new List<Service.Child>();
foreach (Service.Child child in childList) {
if (child.ChildId == childId)
childList.Add(child);
}
contact.Childs = childList;
If an entity originally came from the database and has its own EntityKey properties populated, using Add to link it to another entity will change its EntityState to Added. Even though it is a preexisting entity, SaveChanges will create an insert command for this entity. You should consider using Attach instead:
parent.ChildCollection.Attach(child);
Using the Attach method, you can define relationships between entities that already
exist in the ObjectContext but that have not been connected automatically.

Entity Framework - add to join with only foreign key values

OK, I have 3 tables, call them:
Person
PersonID
Name
Store
StoreID
Name
PersonStore
PersonID
StoreID
Now, I have a form which allows you to add stores to a person. However, I am getting the store ID back from the form. I don't really want to do a query to get the store object from Entity Framework. I just want to add to the table using the StoreID and the Person object which I have.
By default in EF this join table won't appear as an entity instead you'll get a many to many relationship which will show up as two navigation properties
i.e.
Person.Stores
Store.People
If you want to build a many to many relationship without retrieving the entities then attaching stub entities is the best way.
var person = // you already have the person
var store = new Store{StoreID = 5} // you know the storeID
ctx.AttachTo("Stores", store);
ctx.AttachTo("People", person); // assuming the person isn't already attached
person.Stores.Add(store);
ctx.SaveChanges();
The only problem with this code is it will fail if the relationship already exists, so you need to be sure you are creating a new relationship
For more on using Stub entities like this check out my post.
Hope this helps.
Alex
Edit from OP:
Since I am using EF4, I used the following code to remove the string from the attach (thanks to tip 13 from the link).
var person = // you already have the person
var store = new Store{StoreID = 5} // you know the storeID
ctx.Stores.Attach(store);
person.Stores.Add(store);
ctx.SaveChanges();

Using the entity framework to add existing entities to a collection on a newly created entity

I am using the Entity framework to create a new order. The order contains a collection of contacts, a many to many relationship. I want to add a reference to an existing contact on the order on creation of the order. Both Order and Contact a Entity Objects.
Order order = new Order();
//set details on order
Contact contact = new Contact();
EntityKey contactKey =
new EntityKey("OrderDetails.Contact",
"contact_id", contact.Key.Id);
contact.EntityKey = contactKey;
contact.contact_id = contact.Key.Id;
order.Contact.Attach(contact); // throws an exception!
OrderDetails ordTable = new OrderDetails();
ordTable.AddToOrder(order);
int result = orgTable.SaveChanges();
When I go to attach, this exception is thrown:
"Attach is not a valid operation when the source object associated with this related end is in an added, deleted, or detached state. Objects loaded using the NoTracking merge option are always detached."
I know I'm probably missing a step or not fully understanding how the entity framework handles many-to-many relationships.
"Attach" is not allowed because you haven't saved the order yet. Calling "Add" tells Entity Framework that you want to insert a new contact. So you are left with only one option. You need to load the contact.
Here's the fastest way to do that:
OrderDetails context = new OrderDetails();
Contact contact = context.GetObjectByKey(new EntityKey("OrderDetails.Contact", "contact_id", existingContactId));
order.Contact.Add(contact);
If Order has a property Contact, then you can do:
order.Contact.Add(contact);
I would suggest making the property called Contacts rather than Contact, though.