Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework - Code First saving many to many relation

I have two classes:

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

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

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

    public virtual ICollection<Company> Companies { get; set; }
}

In my MVC application controller get new Company from post. I want to add current user to created Company in something like this.

User user = GetCurrentLoggedUser();
//company.Users = new ICollection<User>(); // Users is null :/
company.Users.Add(user);  // NullReferenceException
companyRepository.InsertOrUpdate(company);
companyRepository.Save();

How it should look like to work properly? I don't know it yet but after adding user to collection I expect problems with saving it to database. Any tips on how it should look like would be appreciated.

like image 522
bizon Avatar asked Feb 25 '23 17:02

bizon


1 Answers

Use this approach:

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

    private ICollection<User> _users;
    public ICollection<User> Users
    {
        get
        {
            return _users ?? (_users = new HashSet<User>());
        }
        set
        {
            _users = value;
        }
    }
}

HashSet is better then other collections if you also override Equals and GetHashCode in your entities. It will handle duplicities for you. Also lazy collection initialization is better. I don't remember it exactly, but I think I had some problems in one of my first EF test applications when I initialized the collection in the constructor and also used dynamic proxies for lazy loading and change tracking.

There are two types of entities: detached and attached. An attached entity is already tracked by the context. You usually get the attached entity from linq-to-entities query or by calling Create on DbSet. A detached entity is not tracked by context but once you call Attach or Add on the set to attach this entity all related entities will be attached / added as well. The only problem you have to deal with when working with detached entities is if related entity already exists in database and you only want to create new relation.

The main rule which you must understand is difference between Add and Attach method:

  • Add will attach all detached entities in graph as Added => all related entities will be inserted as new ones.
  • Attach will attach all detached entities in graph as Unchanged => you must manually say what has been modified.

You can manually set state of any attached entity by using:

context.Entry<TEntity>(entity).State = EntityState....;

When working with detached many-to-many you usually must use these techniques to build only relations instead of inserting duplicit entities to database.

By my own experience working with detached entity graphs is very hard especially after deleting relations and because of that I always load entity graphs from database and manually merge changes into attached graphs wich are able to fully track all changes for me.

Be aware that you can't mix entities from different contexts. If you want to attach entity from one context to another you must first explicitly detach entity from the first one. I hope you can do it by setting its state to Detached in the first context.

like image 147
Ladislav Mrnka Avatar answered Mar 08 '23 04:03

Ladislav Mrnka