I am rather new to the Entity Framework, so I am probably overlooking something simple here.
In my controller class I am adding a new Category entity to the database, next I am using that entity as a property on a Course entity. When I save the Course entity, the Category is saved to the database AGAIN, while I was hoping the new Course would reference the Category that was already inserted.
The (simplified) controller code that saves the first Category:
// Create and save the category
Category category = new Category {Name = "Test category"};
category = context.Categories.Add(category);
context.SaveChanges(); // The category object now has a CategoryId (the pk of the record)
// Create and save the course
Course course = new Course {
FullDescription = "This is a new course",
Name = "My new course",
Category = category // Hoping this will have EF make a link to the just inserted category
};
context.Courses.Add(course);
context.SaveChanges(); // Saves the Course AND creates a **new** Category in the db
The problem seems to be that I call saveChanges() twice. What works is removing the first call to context.saveChanges(), BUT, this is not my actual code. In my application I use a repository pattern and adding a category is done by calling categoryRepository.AddCategory(Category category). And saving the Course is done in exactly the same way, by calling courseRepo.AddCourse(Course course) that also contains a call to saveChanges().
public Category AddCategory(Category category)
{
category = context.Categories.Add(category);
context.SaveChanges();
return category;
}
I don't want to remove the calls to saveChanges() in AddCourse() and AddCategory(), because I want these to be atomic operations.
I was hoping that returning the category and subsequently using the category as a property on a new Course would link that course to the category, but apparantly that is not the case. How do I link my Course to a category that is already present in the database?
I'm not sure how your data model is structured but you could do something like this.
course.CategoryId = category.CategoryId;
That way you map the actual foreign key in the relationship and it does the same thing.
Category category = new Category {Name = "Test category"};
Course course = new Course {
FullDescription = "This is a new course",
Name = "My new course",
Category = category
};
courseRepo.AddCourse(course);
You can use only AddCourse for adding both entities, if yours repositories has the same context. If each repository has their own context, you should attach category to the courseRepo context or load entity into it (but I suppose it not suitable for you because you have different repositories).
Related
I use entity framework and I have many to many relationship between 2 objects.
Lets say class "question" and class "tag", when I create a new question with many tags, I have list of selected tags in viewmodel. I need to create the question so these tags are added to question but I do not want to reload all the tags.
I mean :
foreach(var id in tagUIds)
{Tag tag = gettag(id);
question.tags.add(tag); }
is not very efficient, is there a way to avoid loading the tag and adding the reference just by id of tag?
Thanks
You can create intermediate entity TagToQuestionLink, map it to many-to-many table and use Collection of this entity in question instead of collection of Tags:
public class TagToQuestionLink
{
TagId{get;set;}
QuestionId{get;set;}
}
Then you can use:
foreach(var id in tagUIds)
{
var tagLink = new TagToQuestionLink{QuestionId = question.Id, TagId = id};
question.tags.add(tagLink);
}
Do not forget, that in this solution question.tags should be ICollection<TagToQuestionLink>.
I have a REST interface for a datamodel that has several one-to-many and many-to-many relationships between entities. While many-to-many relationships seem easy to manage statelessly, I'm having trouble with one-to-many. Consider the following one-to-many relationship:
Employee:
#ManyToOne
#JoinColumn(name = "Company_id")
private Company company;
Company:
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval=true)
public Set<Employee> employees = new HashSet<Employee>();
When a company is updated, its employee collection may have been updated as well (employees removed or added) but since the REST interface only allows updating the company as a whole, I cannot explicitly delete or add employees.
Simply replacing the collection does not work, but I found that this seems to work:
public void setEmployees(Set<Employee> employee) {
this.employees.clear(); // magic happens here?
this.employees.addAll(employees);
for (Iterator<Employee> iterator = employees.iterator(); iterator.hasNext();) {
Employee employee = (Employee) iterator.next();
employee.setCompany(this);
}
}
Is this the way it should be done, or is there a better way?
EDIT: In fact the above does not work! It appears to work at first, but then it will break with:
Exception in thread "main" java.lang.IllegalStateException: An entity copy was already assigned to a different entity.
I assume this happens because the db already contains a set of employees and if any of the "old" employees are also part of the replacement set, they collide with the ones in the database.
So what is the right way to replace the set?
First make sure equals is implemented properly. As per hibernate spec: http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch04.html#persistent-classes-equalshashcode
I had a similar problem doing a merge. Essentially I had to fetch the existing employees associated with the company. I had to merge any changes to existing employees, and then add any new employees.
Query query = em.createQuery("select e from Employee e where e.company = '" + company.getId() + "'");
Collection<Employee> existingEmployees = new LinkedList<Employee>();
try{
Iterables.addAll(existingEmployees, (Collection<Employee>) query.getResultList());
}
catch(NoResultException nre){
//No results
}
for(Employee existingEmployee : existingEmployees){
for(Employee employee : company.getEmployees()){
if(existingEmployee.name().equals(employee.name())){
employee.setId(existingEmployee.getId());
}
employee.setCompany(company);
}
}
i think you have no better choice then to replace the existing collection and simply set the new one provided by the REST response.
First I'd like to show the corresponding code snippet. When it comes to objCtx.AttachTo() it throws me an error:
Error: "The object cannot be attached because the value of a property that is a part of the EntityKey does not match the corresponding value in the EntityKey."
// convert string fragIds to Guid fragIds
var fragIdsGuids = docGenResult.FragIds.Select(c => new Guid(c)).ToList();
//add each fragment to document))))
foreach (Guid fragIdsGuid in fragIdsGuids)
{
var fragment = new Fragment() { EntityKey = new EntityKey("DocTestObjectContext.Fragments", "ID", fragIdsGuid) };
objCtx.AttachTo("Fragments", fragment);
}
objCtx.SaveChanges();
I've checked everything and I'm not missing any primary key.
However I need some words to explain why I think I have to do it this way.
I'm using EF4 in a C# Environment.
I have a many to many relationship between two tables, Document and Fragments(Primary key "ID") (Documents can have many fragments and a fragment can be a part of many documents)
The Entity Model works fine for me.
However when I try to add a new document to the DB I already have the IDs of the related Fragments in my hand. For adding a new document to the DB I have to call each Fragmentobject and add it to the mapped reference in my document-object. This is a bottleneck because a document can have more than 1000 fragments. The Consequence is that I need 1sec per document. Not much, but I have to create more than 3000 documents and saving this second would result in more speed.
Hopefully you know what's wrong in here.
Thanks.
Thomas
1st edit:
here is the solution wich actually works. I would like to avoid to load all the fragments and instead just save the fragment GUID I already have in the mapping table.
// convert string fragIds to Guid fragIds
var fragIdsGuids = docGenResult.FragIds.Select(c => new Guid(c)).ToList();
// get responding entities from Fragment table
var fragmentList = objCtx.Fragments.Where(c => fragIdsGuids.Contains(c.ID)).ToList();
foreach (var fragment in fragmentList)
{
doc.Fragment.Add(fragment);
}
objCtx.SaveChanges();
2nd edit:
I have the feeling that it is not really clear what I try to do.
However I would like to link/reference existing fragments in a Fragment-table to a coressponding Document in a Document table. The Document I'd like to reference is a new one. The document to Fragment table has an many to many relationship. This relationship has a linking table on the database. In the model it is correctly modeled as a many to many relationship. That's fine.
So far so good. What works is what you can see under my first edit. I have to load all the necessary fragments for a document by their id
// get responding entities from Fragment table
var fragmentList = objCtx.Fragments.Where(c => fragIdsGuids.Contains(c.ID)).ToList();
After that I'm able to add them to my document entity:
foreach (var fragment in fragmentList)
{
doc.Fragment.Add(fragment);
}
But why the hell do I have to load the whole entity (fragments) only to link it to a new document. Why do not tell the EntityStateManager "Dude, here you have some Fragment IDs, link them!"?
Further I tried to follow the MSDN article mentioned by Adrian in the comments. This doesn't worked out for me.
I'll try this:
var fragment = new Fragment {ID = fragIdsGuid};
//fragment.EntityKey.Dump(); // -- this should be null
objCtx.AttachTo("Fragments", fragment);
//fragment.EntityKey.Dump(); // -- shows the EntityKey object, created after the object is attached
The Dump function is from LinqPad
I use
p.AuthorsReference.EntityKey = new System.Data.EntityKey("PetitionsContainer.Authors", "Id", authorId);
but I get entities in PetitionsContainer.Questions participate in the QuestionAuthor relationship.
0 related 'Author' were found. 1 'Author' is expected.
Now, the Author with the Id authorId is already in the database.
It is true that each question must have 1 author.
Though, can't I use AuthorsReference instead of something like p.Authors.Add(new Author())?
If you set up the reference you must also fill the author. You can try using this:
// Attach a dummy author to the context so that context believes that you
// loaded the author from the database
Author a = new Author { Id = authorId };
context.Authors.Attach(a);
// Now assign existing author to the question
question.Author = a;
I would like to add a record to a SQL Server table using the Entity Framework. My table's entity has foreign keys and so has navigational properties for those fields. When adding a new record/entity, how do I populate the foreign key fields since they don't appear as properties of the entity?
The easiest way is to do a query for the related entities and use the Navigation Properties:
i.e.
Product p = new Product{
ID = 5,
Name = "Bovril",
Category = ctx.Categories.First( c => c.ID == 5)
};
ctx.AddToProducts(p);
ctx.SaveChanges();
If you want to avoid the database query the easiest approach is probably to use a STUB entity i.e.
// this is a stub, a placeholder for the real entity
Category c = new Category {ID = 5};
// attach the stub to the context, similar to do a query
// but without talking to the DB
ctx.AttachTo("Categories", c);
Product p = new Product{
ID = 5,
Name = "Bovril",
Category = c
};
ctx.AddToProducts(p);
ctx.SaveChanges();
If you want more help on this stub technique check out this blog post on the topic.