Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Entityframework auto assign navigation properties on create

When I run the below mentioned code EF saves the PersonAddress with the correct PersonId. I have NOT added the PersonAddress entities to the Person entity, even though I have not done this the records in my database are linked correctly.

My question is: Does EF automatically add the related entities even though I have not specified the entity that it belongs to? And if so can this not cause unwanted entity relations?

Update

It seems that the entities are saved correctly for the following reasons:

  1. When creating entity Person the PersonId field is 0
  2. PersonAddress.PersonId is also 0 at the time of creation.

By manually setting the Person.PersonId to any value at the time of creation, and then setting PersonAddress.PersonId to that same value of Person.PersonId EF saves the data correctly as they share the same PersonId.

So technically EF does not add the related entities automatically, they are related as they share the same PersonId.

Please see code below as reference:

        using (var context = new Models.TestEntities())
        {
            var person = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          = context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.PersonAddresses.Add(postalAddress);
            context.PersonAddresses.Add(residentialAddress);

            context.SaveChanges();
        }

When I add an extra Person to the code, I get the following error: enter image description here
Code:

        using (var context = new Models.TestEntities())
        {
            var person  = context.People.Create();
            var person2 = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          = context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.People.Add(person2);                

            context.PersonAddresses.Add(postalAddress);
            context.PersonAddresses.Add(residentialAddress);

            context.SaveChanges();
        }

As the error specified Entityframework can now not determine to whom the PersonAddress entities belong.

I can resolve this by modifying the code as below:

        using (var context = new Models.TestEntities())
        {
            var person  = context.People.Create();
            var person2 = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          =    context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.People.Add(person2);

            person.PersonAddresses.Add(residentialAddress);
            person.PersonAddresses.Add(postalAddress);

            context.SaveChanges();
        }

Please see the EDMX below:

enter image description here

Please see SQL script used to create the two tables:

CREATE TABLE Person
(
   PersonId INT IDENTITY(1,1) NOT NULL CONSTRAINT [PK_Person] PRIMARY KEY,
   FirstName VARCHAR(250)
)

CREATE TABLE PersonAddress
(
   PersonAddressId INT IDENTITY(1,1) NOT NULL CONSTRAINT [PK_PersonAddress]     PRIMARY KEY,
  PersonId INT NOT NULL,
  AddressLine1 VARCHAR(250)
)

  ALTER TABLE PersonAddress
  ADD CONSTRAINT [FK_PersonAddress_Person] FOREIGN KEY(PersonId)
  REFERENCES [Person](PersonId)

Please see Id columns screenshots below: enter image description hereenter image description hereenter image description here

Please see SQL Server Profiler trace below:

Person:
enter image description here

PersonAddress:
enter image description here

PersonAddress:
enter image description here

Please see the inserted records in SQL:

Person:
enter image description here

PersonAddress:
enter image description here

Thanks.

like image 937
Tjaart van der Walt Avatar asked Apr 08 '15 10:04

Tjaart van der Walt


People also ask

How do I get navigation property in Entity Framework?

Need function like the following. private string[] GetNaviProps(Type entityType)//eg typeof(Employee) { NorthwindEntities en = new NorthwindEntities(); //here I return all Properties only for example return entityType. GetProperties(). Select(p=>p.Name).

What is navigation property in entity data model?

A navigation property is an optional property on an entity type that allows for navigation from one end of an association to the other end. Unlike other properties, navigation properties do not carry data.

What are scalar and navigation properties in Entity Framework?

Basically a scalar property is mapped to a column (int, string, ...) A navigation property is mapped to a relation. e.g Order. OrderDetails brings you to all ORderDetails of a specific order.

Why navigation properties are virtual?

If you define your navigation property virtual , Entity Framework will at runtime create a new class (dynamic proxy) derived from your class and uses it instead of your original class. This new dynamically created class contains logic to load the navigation property when accessed for the first time.


2 Answers

Here's what happens:

var person = context.People.Create();

A Person is created having PersonId = 0.

var postalAddress           = context.PersonAddresses.Create();
postalAddress.AddressLine1  = "PostalAddressLine1";
var residentialAddress          = context.PersonAddresses.Create();
residentialAddress.AddressLine1 = "ResidentialAddressLine1";

Addresses are created, also having PersonId = 0.

context.People.Add(person);
context.PersonAddresses.Add(postalAddress);
context.PersonAddresses.Add(residentialAddress);

EF has executed relationship fixup, i.e. it has matched the addresses' PersonIds and the Person's PersonId (all 0) and established an association between them.

context.SaveChanges();

The database has assigned an identity value to Person.PersonId. EF read it back from the database into the entities.

In the second snippet there are two Persons having PersonId = 0, so now EF doesn't know which person to associate the addresses with.

What to do

Clearly, this is unexpected behavior. The best thing is to associate the entities explicitly, if they are intended to be related, as in your third snippet.

Once you know about these automatic associations you may want to prevent them by assigning a different default value to Person.PersonId other than 0, e.g. -1. Now EF won't match this id with any other foreign value having the default for integers, 0.

like image 158
Gert Arnold Avatar answered Sep 22 '22 06:09

Gert Arnold


I have tested the scenario by creating a new project against a new database that I created with your script. I then generated a model against it leaving, all the defaults.

Here is the code I used to create the person and addresses.

        var entities = new TestEntities();

        var person = entities.People.Create();
        person.FirstName = "xxx";
        entities.People.Add(person);

        var address1 = entities.PersonAddresses.Create();
        address1.AddressLine1 = "Line1";
        entities.PersonAddresses.Add(address1);

        var address2 = entities.PersonAddresses.Create();
        address2.AddressLine1 = "Line1";
        entities.PersonAddresses.Add(address2);

        entities.SaveChanges();

I can confirm that this code does indeed run and insert a person and two addresses linked to that person. It definitely looks like EF wires it up if there is only one entity that it could possibly link to. I'm not sure if that is supposed to be a feature, but it can make for some nasty surprises.

like image 44
Johann Strydom Avatar answered Sep 21 '22 06:09

Johann Strydom