Entity Framework the use of reference - entity-framework

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;

Related

Why can't EF handle two properties with same foreign key, but separate references/instances?

Apparently, EF6 doesn't like objects that have multiple foreign key properties that use the same key value, but do not share the same reference. For example:
var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
When I attempt to insert this record, EF throws this exception:
Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]
I've discovered that doing this resolves the issue:
var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference
I could write some helper code to normalize the references, but I'd rather EF just know they're the same object based on the ID alone.
As for why EF does this, one explanation could be that its trying to avoid doing multipe CRUD operations on the same object since separate instances of the same entity could contain different data. I'd like to be able to tell EF not to worry about that.
Update
So it's as I suspected per my last paragraph above. In absense of a means to tell EF not to do CRUD on either instance, I will just do this for now:
if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
address.ModifiedBy = address.CreatedBy;
}
Works well enough so long as I am not trying to do CRUD on either.
Update2
I've previously resorted to doing this to prevent EF from validating otherwise-required null properties when all I need is the child entity's ID. However, it doesn't keep EF from going into a tizzy over separate instances with the same ID. If it's not going to do CRUD on either AppUser object, why does it care if the instances are different?
foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
db.Entry(o).State = EntityState.Unchanged;
}
If you get AppUser from context, then you will not need to do anything, because Entity Framework will track entities:
var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);
var address = new Address
{
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
};
Now, they both will point to same objects and will not cause to conflict.
You can add two extra properties to have the Id for the main objects which is the AppUser, then you can use only one AppUser object and reference it for both the created and modified by properties.
CreatedById = user1.Id,
ModifiedById = user1.Id
Otherwise, your code will end up by saving two instances of AppUser with the same primary key.
Another approach is to set both the foreign key properties to only one AppUserobject
The explanation is that EF's change tracker is an identity map. I.e. a record in the database is mapped to one, and only one, CLR object.
This can be demonstrated easily by trying to attach two objects with the same key:
context.AppUsers.Attach(new AppUser { Id = 1 });
context.AppUsers.Attach(new AppUser { Id = 1 });
The second line will throw an exception:
Attaching an entity of type 'AppUser' failed because another entity of the same type already has the same primary key value.
This also happens if you assign
CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference
Somewhere in the process, user1 and user2 must be attached to the context, giving rise to the exception you get.
Apparently, you have a function that receives two Id values that can be different or identical. Admittedly, it would be very convenient if you could simply create two AppUser instances from these Ids, not having to worry about identical keys. Unfortunately, your solution ...
if (address.ModifiedBy.Id == address.CreatedBy.Id)
... is necessary. Solid enough, though.

Entity Framework - many to many relationship - add reference with id

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>.

Entity Framework: linking entity A to an existing entity B

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).

How to create and store a (self-tracking) entity object on the server side?

I am trying to achieve the following using Entity framework 4.0 and self-tracking entities:
1) The client application request a book form the server by providing an ISBN number
2) The server performs a query on its database to see if the book is already present
3a) If the book is in the database, it returns it.
3b) If the book is not in the database, it will query Amazon for info, extract the required attributes, create a new book, store it in the database, and return it to the client
Now, 3b) is where the problems are... I can't find any information on how I can create an entity object (a book) on the server side, add it to the context and store it in the database. I have tried all sorts of things:
public class BookBrowserService : IBookBrowserService {
public Book GetBook(string ISBN) {
using (var ctx = new BookBrowserModelContainer()) {
Book book = ctx.Books.Where(b => b.ISBN == ISBN).SingleOrDefault();
if (book == null) {
book = new Book();
book.ISBN = ISBN; // This is the key
book.Title = "This title would be retrieved from Amazon";
Author author = new Author();
author.Name = "The author's name would be retrieved from Amazon";
book.Authors.Add(author);
ctx.Books.AddObject(book);
ctx.SaveChanges(); // This one always throws an exception...
}
return book;
}
}
}
Could anyone tell me what I am doing wrong?
It looks like the problem is related to the EDMX model.
I have a Book entity and an Author entity, with a many-to-many relationship.
The Book entity's Key is ISBN, which is a string of Max length 13.
StoreGeneratedPattern is set to None.
The Author entity's Key is Id, which is a Guid.
StoreGeneratedPattern is Identity.
The exception message is:
"Cannot insert the value NULL into column 'Id', table 'BookBrowser.dbo.Authors'; column does not allow nulls. INSERT fails. The statement has been terminated. "
But since StoreGeneratedPattern is set to Identity, shouldn't an Id value be created automatically?
Thanks,
Peter
It looks that the problem was that I used a Guid as Key in combination with StoreGeneratedPattern = Identity.
When I set StoreGeneratedPattern to None and create my own Guid using Id = Guid.NewGuid(), the problem is gone.
Apparently, the SQL server cannot generate Guids...
you can use StoreGeneratedPattern=Identity, but generated sql script based on your edmx doesn`t contain newid() in describing primary key(GUID). you can do this manually in generated sql script. 'BookId uniqueidentifier NOT NULL
DEFAULT newid()'. So id value will create GUID automatically.

Entity Framework-How To Add To Entites With Navigational Properties

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.