简体   繁体   中英

Many to Many Relationship - Entity Framework is creating new objects

I'm using the Entity Framework (6.1.3) with the code first approach. I have a model class movie and a model class Tag that look like this:

public class Movie
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Tag> Tags { get; set; }
}

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

In the DbContext I'm overwriting the OnModelCreating method to setup the one-to-many relationship like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Movie>()
                .HasMany(a => a.Tags)
                .WithMany()
                .Map(x =>
                {
                    x.MapLeftKey("Movie_Id");
                    x.MapRightKey("Tag_Id");
                    x.ToTable("MovieTags");
                });

    base.OnModelCreating(modelBuilder);
}

If I try to insert a Movie into the database with some tags I have the problem that unwanted Tags are created.

For example I already have the two Tags "Foo" (Id:1) and "Bar" (Id:2) in the Database. Now I create a new Movie object and insert the existing Tag "Foo" (loaded from the Database) and the new Tag "Foobar" to the Collection of the object. After I add this movie to the DbContext and call the Method "SaveChanges" there are two new Tags in my Database.

Why is the Entity Framework inserting an exisiting Tag to my Database? What do I have to change that only the missing Tag is inserted?

Edit-1: Here is the Controller code where I add the tags and save it

List<Tag> tagList = new List<Tag>();
// ViewModel.Tags either contains the Tag-Id or the Tag-Name (if it is new)
foreach (string tag in viewModel.Tags)
{
    if (IsDigitsOnly(tag))
    {
        // Load the existing Tag from the DbContext and add to List
        tagList.Add(TagService.Get(int.Parse(tag)));
    }
    else
    {
        // Create new Tag
        tagList.Add(new Tag() { Name = tag });
    }
}

Movie movie = new Movie();
movie.Name = viewModel.Name;
movie.Tags = tagList;

MovieService.Insert(movie);

Here is the MovieService code with the insert

public void Insert(Movie movie)
{
    Context.Movie.Add(movie);
    Context.SaveChanges();
}

Edit-2: I found the problem! It was a problem with how I set up my project. I used Ninject for DI and I didn't bind the DbContext to the request (InRequestScope). So everytime one of my services was called it used a different DbContext and because of that it didn't know of already loaded Tags.

Could you supply the code where you're adding the Tag?

In the meanwhile, I assume it's something like

Movie.Tags.Add(new Tag { Name = "foo" });

You'll probably want to add an existing Tag as follows:

Movie.Tags.Add(ctx.Tags.First(T => T.Name == "foo"));

... or the equivalent code that will get the Tag from your context.

Also, you might want to make the Tags ICollection virtual so the proxy can override it. That can be a hard-to-find source of bugs too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM