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.
Related
I have a Many-To-Many relationship defined like this:
public class Post
{
//NOT SHOWN: Other properties
public virtual ICollection<Tag> Tags { get; set; }
}
All the examples I see would add to the many-to-many relationship like this:
//Get the tag with ID of 1
var tag = context.Tags.Find(1);
// associate tag with ID of 1 with myPost
myPost.Tags.Add(tag);
But this seems tedious/inefficient if I just know the id of the tag(s) I would like to associate with my post.
Ideally, I would just like to expose a List<int> TagIds on my Post entity and be able to add Tags by adding the Tag Ids to the list, but I've been unable to determine if this is possible using EF Code First.
Bottom line: What's the best way to add items to a many to many relationship given I just have the ids of the entities I want to relate. (e.g. if I have a list of Tag IDs, what's the best way to relate those Tags to a Post?)
Use code like this:
// Existing post without loading it from the database
var myPost = new Post() { Id = ... };
context.Posts.Attach(post);
// For each id of existing tag
foreach (int tagId in someExistingTagIds)
{
var tag = new Tag() { Id = tagId };
context.Tags.Attach(tag);
// Make a new relation
post.Tags.Add(tag);
}
context.SaveChanges();
This will allow you to create a M-N relation over existing entities without loading anything from the database. You just need to know Ids of existing entities.
Hopefully someone with more expertise in EF will be able to help you, but if it were me in this situation, I would model the M-M relationship myself (so, modelling the linking table). This way, you can have
myPost.PostTags.Add(new PostTagLink(){
TagID = 1
});
But then you now have to deal with the extra link table object. On the flipside, if you were going to add more properties to the relationship you'd have to do this anyway.
If there is a better way to work around this, I'd like to know too.
Why are you saying it is inefficient. If you declaring it as Virtual it will not loaded until you access it when you have enabled lazy loading. Let say if you have list of Id values. Then when you want to load all the list you need to call getById for all the values. That is very inefficient than using the lazy loading.
i'm struggling with LLBLGEN and i guess ORM's in general.
i have created an entity, lets use a library example to explain:
i want to display a book object and also return a list of users who have loaned the book.
so i need to return the book object which contains a list of users.
DTO Book::
int bookId,
string bookName
additionally i wish to return with my book a collection of users who have loaned the book:
List<user> Loans
loans table might look like this:
int id
int userid
int bookid
currently my loans entity has now created this:
DTO Loans
int id
User user // user entity
Book book // book entity
im struggling to understand how this example would work in llblgen. can anyone assist with guidance or point me in the way of a tutorial?
at the moment, when i come up to update my model Book with a new loan associated to a book, im getting stackoverflow errors. i assume this is creating some sort of loop when attempting to update my Book object.
thanks
i noticed when running a profiler on SQL that the sql statement didnt include any join statements onto my relationship entities.
this was because my domain query didnt include prefetch items for my relationships, i added the following to my query:
var query = new DomainSearch<T>
{
SearchText = searchText,
PrefetchItems =
{
new Prefetch(typeof(Users))
}
};
To make sure, you are looking for a list of User entities that have loaned a particular Book entity. Is this the correct use case, or are you looking for a list of User entities that have borrowed the particular book?
Regardless, LLBLGen's support for these cases is great with referencing relationships between entities and using related entities quickly and easily.
Presuming you're looking up a book by unique BookId/ISBN/etc....
// Get the specific book entity
BookEntity book = new BookEntity(bookId);
foreach(UserEntity user in book.users)
{
// do stuff with list of users
}
It's just that simple assuming you've defined your relationships between entities.
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();
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?
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.