Whenever I add a foreign key entity to my previous entity by setting the ForeignKey-ID, the associated object is null.
Let me explain this:
In a previous step I've set the AddressId property to 28 and have saved the entity context by calling context.SaveChanges().
Now why is AddressId filled, but Address as the NavigationProperty (which should be an Address object of the Address table where Address.Id == 28) is null?
Entity Frameworks (EF) work this by design.
Updating the foreign key never updates the navigation property.
However, updating the navigation property will update the key. Also note that in this case the Address entity should come from the same context. If not .SaveChanges() will consider the Address entity as new and try to add it in the database.
As to the question of which method is better, well, it depends!
- Updating the Key is straightforward and is what we have been doing all along using Data Transfer Objects (DTOs) or even plain SQL. So is easier for newcomers to EF to grasp and use.
- Updating the navigation property is where you truly get an object based data model. The code looks cleaner and more readable. However you need to be very careful with the Context. In my little personal experience with EF, I find that trying to update the navigation property brings more complexity than value, especially in a multi-tier architecture where the Context is hidden behind the Data Access layer.
The most important benefit of EF, in my opinion, is in query operations using LINQ-to-Entities. I have compile-time syntax check for my queries and strong typing. I can easily create an object-based result set with multiple levels of children, data-bind ready without any additional code. I rarely write SQL anymore.
I sorta fixed this by re-creating my entities and reloading then. This seems to work and re-fetch the n:m relationship navigation properties. Weird.
Related
We are using NHibernate for DB operations and in our database there is a reference of Organisation in the Configuration table. This is existing code and cannot be changed.
Configuration table is heavily used throughout application so we want to include the OrganisationId in the Configuration model class, but not in table as it already has reference object. By doing this we will avoid loading whole organisation object when we just need OrganisationId.
//Configuration.hbm.xml
<many-to-one name="Organisation"
class="Organisation"
not-null="false"
foreign-key="FK_ConfigurationItem_Organisation"
unique-key="UniqueConfigurationItemName"/>
//Configuration.cs (Model Class)
public virtual Organisation Organisation { get; set; }
How do I modify hbm or model class so that we get organisationId without including this field in table?
I am new to NHibernate and stuck with this requirement.
Any suggestion will be appreciated.
Thanks
it should not be necessary to try this optimization manually, because NHibernate has multiple options to handle that on it's own, and that methods work especially well for heavily used object (aka. rows). If you use a session-object with a cache NHibernate will prefere to access the cache (aka. memory) and not load from the database (a second, third or ... time). If you use eager or join-loading then NHibernate will include simple foreigen keys in the first sql to the database, a much quicker way then lazy-loading (only bad if he foreigen key is never accessed).
BUT
This is a bit of a guess and I am not 100% sure that it is the intended way to do it, but I accidentally mapped a column twice and you could do that too for the organisation, once as a foreigen key and once as a simple column (of witch ever type organisationid is).
Make sure that the setter of the "simple column" property is not public!
Make sure to update the "simple column" in the setter of the foreign key.
Greetings
Juy Juka
I am using EFCore 5.0.0.
When I AddAsync (person); I should be getting a temporary ID, and I use this ID to add the PersonId for School (shown in code below). FInally, I will SaveChangesAsync() where everything will be saved. However, the PersonId is set to 0. I want to get the temporary ID stored instead. How can I do this.
await _dbContext.AddAsync(person);
School school = mySchool;
school.PersonId = person.Id;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();
Note: There are many SO post that talks about the temporary ID, but none is related to this post.
Currently accepted answer is valid, but technically incorrect. Assigning navigation property is valid approach, but not mandatory. It's even perfectly valid to not have navigation property at all. As well as explicit FK property. But there is always at least shadow FK property which can be used to setup/maintain the relationship.
So the temporary key concept is part of the EF Core from the very beginning. However EF Core 3.0 introduced a breaking change - Temporary key values are no longer set onto entity instances. The link contains an explanation of the old and new behaviors, the reason and possible solutions:
Applications that assign primary key values onto foreign keys to form associations between entities may depend on the old behavior if the primary keys are store-generated and belong to entities in the Added state. This can be avoided by:
Not using store-generated keys.
Setting navigation properties to form relationships instead of setting foreign key values.
Obtain the actual temporary key values from the entity's tracking information. For example, context.Entry(blog).Property(e => e.Id).CurrentValue will return the temporary value even though blog.Id itself hasn't been set.
Bullet #1 makes no sense, Bullet #2 is what is suggested in the other answer. Bullet #3 is the direct answer/solution to your question.
And applying it to your example requires just changing
school.PersonId = person.Id;
to
school.PersonId = _contexy.Entry(person).Property(e => e.Id).CurrentValue;
Of course when you have navigation property and the related entity instance, it's better to use it and let EF Core do its magic. The temporary key is really useful when you don't have navigation property, or you don't have related entity instance and know the key, but don't want to do roundtrip to load it from database (and using fake stub entity instance can lead to unexpected side effects/behaviors). It works well with both explicit and shadow FK properties.
I've never seen linking entities in EF Core using the temporary id.
Typically what you would do is assign the entity and let EF sort out the ids and relationships.
i.e. in this instance, the School will be linked to the Person.
await _dbContext.AddAsync(person);
School school = mySchool;
school.Person = person;
await _dbContext.AddAsync(school);
await _dbContext.SaveChangesAsync();
I have a POCO class (OPERATION) that is used as an Entity Framework entity. This class has a navigation property (OP) and a foreign key into the same related entity (OP_ID).
In a method, I get an OPERATION and on this OPERATION the OP_ID and OP are both null. When I set the OP_ID to a valid value for this foreign key, the OP navigation property remains null. When I explicitly detect changes in the context, the OP navigation property is now assigned with the correct value.
Sample code
public bool UpdateOperation(operationID)
{
IQueryable<OPERATION> operations = from o in base.ctx.OPERATION
select o;
OPERATION operation = operations
.Where(o => o.OPERATION_ID == operationID)
.Include("OP")
.FirstOrDefault();
if (operation != null)
{
operation.OP_ID = opId;
}
// operation.OP is null here
operation.GetContext().ChangeTracker.DetectChanges();
// operation.OP is populated here
}
I have confirmed that the operation is, in fact, a dynamic proxy. For what it's worth, once I detect changes, operation.OP also becomes a dynamic proxy. However, even then, assigning a different value to operation.OP_ID still requires an explicit DetectChanges() call in order to update the value of operation.OP.
Update
In response to the comment from #ErikPhilips, the documentation here seems to imply that this should happen. Specifically:
The following examples show how to use the foreign key properties and navigation properties to associate the related objects. With foreign key associations, you can use either method to change, create, or modify relationships. With independent associations, you cannot use the foreign key property.
By assigning a new value to a foreign key property, as in the following example.
course.DepartmentID = newCourse.DepartmentID;
...
When you change the relationship of the objects attached to the context by using one of the methods described above, Entity Framework needs to keep foreign keys, references, and collections in sync. Entity Framework automatically manages this synchronization (also known as relationship fix-up) for the POCO entities with proxies.
If you are using POCO entities without proxies, you must make sure that the DetectChanges method is called to synchronize the related objects in the context.
Some additional context may be useful, as well. This is a legacy application that used to work directly with an ObjectContext instead of a DbContext, though even then using EF 6. We are now migrating to the DbContext API. This particular code, without any modifications, used to demonstrate the behavior I'm expecting. Specifically, when OP_ID is assigned, I can see in the debugger that the OP property is automatically populated to point to the correct OPERATION.
In the end, I was doing exactly what the documentation described. I was
assigning a new value to a foreign key property.
Yes, Entity Framework does manage this in fix-up. And yes, the documentation does state this.
It turns out, though, that the egg is ultimately on my face. I had checked the classes generated from my T4 template, and seen that all navigation properties were marked virtual. I had not checked thoroughly enough to note that the foreign key properties were not marked virtual, however. It appears that this is the default behavior of the EF-provided T4 template used when working model- or database-first. I've addressed this by changing this line in the CodeStringGenerator.Property() method in the T4 template
Accessibility.ForProperty(edmProperty)
to
AccessibilityAndVirtual(Accessibility.ForProperty(edmProperty))
In the end, as usual, following the documentation (here, the requirements for EF change tracking on POCOs) often results in dependent code behaving as it is documented. Shame on me.
I want to persist a linked list of objects using in my ASP.Net Core application. For simplicity, I'll use blog and comments; although the real context is much more complex.
I scaffolded two tables, and changed ICollection<Comment> to LinkedList<Comment>. However, if I create an initial migration and apply to an empty database, I don't get anything "linked" in the database (no next or previous). Also, if I seed the data, and then do something like this:
var comments = _context.blogs.First().Comments
I get null. If I leave public virtual ICollection<Comment> Comments, I get the IEnumerable just fine.
I tried to use LinkedList<LinkedListNode<Comment>> instead, and it works nice unless I try to create a migration. Getting an error
No suitable constructor found for entity type 'LinkedListNode'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'value' in 'LinkedListNode(Comment value)'; cannot bind 'list', 'value' in 'LinkedListNode(LinkedList list, Comment value)'.
I couldn't find any guidance how to implement LinkedList in C#/.NET Core (obviously, I can do it manually, having next and prev fields - but I would very much prefer to use framework capabilities, if possible!)
I don't know much about MS SQL server, but the whole next and prev parts in mysql won't work because you can't map the keys it would require to track the fields properly because each link has to have an id in a database to link to. only thing I know of would be create the next/prev yourself and use some custom data persistence or data annotations. but the foreign key constraints I'm pretty sure on any relational database will prevent you from auto persisting those types of fields. reason being tracking deletes, inserts etc would be a nightmare because if you remove the middle of the chain, then the database has to try and guess where to link the ends to
I want to set a foreign key on an entity. I have the foreign entity exposed in my user control, and want to set it via WinForms data binding.
Here's the catch - the foreign entity was originally loaded from another repository/DbContext, as the user control populates itself independently using its own repository.
Unfortunately this doesn't work "out of the box", as this example demonstrates:
var repository1 = GetRepository();
var categoryFromRepository1 = repository1.GetAll<Category>().First();
var repository2 = GetRepository();
var appointmentFromRepository2 = repository2.GetNewAppointment();
appointmentFromRepository2 .Category = categoryFromRepository1;
repository2.Add(appointmentFromRepository2);
repository2.SaveChanges();
This fails on at Add() with the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
OK, so repository2 can't auto-attach the Category because it's attached to repository1. Great, so let's detach first:
repository1.Detach(categoryFromRepository1);
Which fails on SaveChanges() due to a validation error - whoops, turns out repository2 thinks it's an Added entry and trying to insert. Great, so let's attach as well to avoid this:
repository2.Attach(categoryFromRepository1);
And this works! Problem solved. I've now set the repository2-entity property to the repository1-entity, voila.
Except that this solution sucks swamp water... We have many data-bound self-populating user controls throughout the program, and manually detaching/reattaching all the foreign entity references prior to SaveChanges() is a horrible solution. Furthermore, supposing the repository we're saving via happens to have the object attached already then we get this error when we Attach():
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
None of the solutions I can think of are that great:
1) In our generic repository class on SaveChanges(), scan all foreign references on all modified entities for out-of-DbContext entity references, dynamically change them to the in-DbContext entity reference (load from DB if necessary)
2) Don't set the navigation property at all, just set the foreign key ID field (sucks0rz yo' b0x0rz)
3) Manually do these checks before save (violates DRY & persistence-ignorance principles)
4) Abandon data-binding to these properties, manually set properties & load entities from the main repository (terrible - means extra queries to the database for data we already have)
5) Fudge user controls so that they can load their data from a given repository, if required (poor solution, violates some basic design principle... but workable)
Any other ideas, plz?
Regards,
-Brendan
Given the presence of multiple DbContext instances, it seems you have multiple bounded contexts at play. Specifically, there are multiple aggregates at play, namely Category and Appointment. Due to issues such as the one you're having, it is desirable to implement references between aggregates using only the identity value - no direct object references. If Appointment references Category by ID alone, you wouldn't have this problem. It is likely though that you need the entire Category aggregate for display purposes. This requirement can be addressed with the use of the read-model pattern.
Take a look at Effective Aggregate Design for more on this.