JARRRRG
JARRRRG

Reputation: 926

EF6 - Virtual Property not saving to database after savechanges in single transaction

I have 3 entities set up in the follow way.

public class Person
{
    [Key]
    public int PersonId { get; set; }
    public virtual ICollection<Location> Locations { get; set; }
}

public class Location
{
    [Key]
    public int LocationId { get; set; }
    public int PersonId { get; set; }

    public virtual Person Person { get; set; }
    public virtual ICollection<Value> Values{get; set; }
}

public class Value 
{
    [Key]
    public int ValueId { get; set; }
    public int LocationId { get; set; }

    public virtual Location Location { get; set; } 
}

Using fluent Api, I have setup the foreign key configuration from Location to Person and also from Value to Location.

        // Person to Location(s)
        modelBuilder.Entity<Person>()
            .HasMany(e => e.Locations)
            .WithRequired(e => e.Person)
            .WillCascadeOnDelete(true);

        // Location to Value(s)
        modelBuilder.Entity<Location>()
            .HasMany(e => e.Values)
            .WithRequired(e => e.Location)
            .WillCascadeOnDelete(false);

What I'm trying to do is create a new Person, add a Location and to that location add a value.

So naturally I'll first create a person

[pseudo code]
var newPerson = new Person();

then create the Location

[pseudo code] 
var newLocation = new Location { Person = newPerson };

and then finally the value

[pseudo code]
var newValue = new Value { Location = newLocation };

DbContext.Values.Add(newValue); DbContext.SaveChanges();

What's happening is that I'm able to successfully save the Location with the newly created person if I want but when I try to get another level down and add the value like above it's complaining about the foreign key constraint between Value and Location.

I have managed to get it to work by first adding the Location to the context and doing a save changes but this would mean that my code isn't transaction safe.

Upvotes: 3

Views: 829

Answers (2)

Harald Coppoolse
Harald Coppoolse

Reputation: 30454

You have modeled a proper one-to-many relation ship between Persons and Locations: a Person has zero or more Locations, and every Location belongs to exactly one Person. If the Person seizes to exist, then his Locations also seize to exist.

However, you have modeled the relation between Locations and Values differently. Apparently if a Location seizes to exist, the Values don't have to be deleted because of the WillCasscaseOnDelete(false). What would the value of LocationId mean if the Location is deleted?

You solution depends on what you intended to model:

  • If you really intended a one-to-many relation, so a Location has zero or more Values and every Value belongs to exactly one Location, you should WillCascadeOnDelete(true).
  • If you meant that a Value belongs to zero or one Location, then model a proper one to zero-or-one relation. In that case a Value can still exist without the Location
  • If you meant that a Value could belong to several Locations then model a one-to-many relation or many-to-many relation between Location and Value. In this situation you can delete a Location without its Value, because these Values might live without their Location.

Upvotes: 1

Afnan Makhdoom
Afnan Makhdoom

Reputation: 654

You're able to save the person because it does not include a foreign key, meaning it does not have a property for Locationid but in the case of values, it requires Locationid, that is the entire purpose of defining a relation.

When you define a relation between two tables, you simply can not avoid saving records in one of the table without the foreign key of the other table. Add the location, save the changes, in the next line, add your value and then save changes. This procedure does not make your application vulnerable in any way.

If you still don't want to do this, then instead of defining the relation with Entity Framework, make the Locationid unique in your values table and perform custom validations yourself but I wouldn't suggest that.

Upvotes: 1

Related Questions