dougajmcdonald
dougajmcdonald

Reputation: 20057

EF One to One relationship creates new record each on each call to `SaveChanges()`

I have an EF model which enforces a user > address one to one relationship which trimmed down looks like this:

public class User 
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int AddressId { get; set; }
  [ForeignKey(nameof(AddressId))]
  public Address Address { get; set; } = new Address();
}

public class Address 
{
  public int Id { get; set; }
}

I'm using the a generic repository pattern but the same thing happens if I work directly on the EF context.

If I do this:

public void Create() 
{
  var user = new User();
  context.Users.Add(user);
  context.SaveChanges();
}

I get a user created in my DB with an attached address and both the AddressId and Address.Id property are set to a correct value.

However if I do this:

public void Update(int userId, String name) 
{  
  var user = context.Users.Single(x => x.Id == userId);
  user.Name = name;
  context.SaveChanges();
}

When I look at the user after the SaveChanges() the name updates as expected but I have an entirely new Address record created in the DB and EF has incremented the AddressId property for the user to the new Address.Id.

This seems like I've made a basic error either in the schema definition, or in how I'm expecting things to work.

Can anyone shed any light on this? appreciate any thoughts.

EDIT

If I make the AddressId on the User a nullable int and then tweak the create method like this:

public void Create() 
{
  var user = new User();
  var address = new Address()
  context.Addresses.Add(address);
  user.Address = address;
  context.Users.Add(user);
  context.SaveChanges();
}

Then things work as I'd expect.

Upvotes: 1

Views: 578

Answers (1)

Slava Utesinov
Slava Utesinov

Reputation: 13498

I think the problem at this row: public Address Address { get; set; } = new Address() - each time, when EF retrives User from database, it rewrites old Address to completely new one, that is why AddressId is changed and icremented. So, simply remove new Address(), EF will have done all work by itself:

public class User 
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int AddressId { get; set; }
    //[ForeignKey(nameof(AddressId))] is redundant
    public virtual Address Address { get; set; }// = new Address();
}

public class Address 
{
    public int Id { get; set; }

    public virtual ICollection<User> Users {get;set;}
    public virtual ICollection<Organization> Organizations {get;set;}
}

Usage with not nullable AddressId:

public void Create() 
{
    var address = new Address()
    context.Addresses.Add(address);
    context.SaveChanges();

    var user = new User{ AddressId = address.Id };
    context.Users.Add(user);
    context.SaveChanges();
}

Upvotes: 2

Related Questions