简体   繁体   中英

Adding new records to an Entity Framework many-many with existing records

I've got two classes as follows:

public class Movie
{
    ...
    public virtual ICollection<Actor> Actors { get; set; }
}

public class Actor
{
    ...
    public virtual ICollection<Movie> Movies { get; set; }
}

And Entity Frameworks creates a table in between to record the relationship between them.

Now, my question is how can I add a new record with existing Movies and Records? There are two parts to this question:

  1. With the following method, how can I add the actors to the movie without replacing all the existing relationships for the movie:

     public void AddRelationship(int movieId, int[] actorIds) { var movie = new Movie { Id = movieId }; context.Movies.Attach(movie); foreach(var actorId in actorIds) { movie.Actors.add(new Actor{ Id = actorId }); } context.SaveChanges(); } 

This creates a new Actor which is not what I want.

  1. With the following method, how can I replace all the actors for a movie with the given list:

     public void ReplaceRelationship(int movieId, int[] actorIds) { } 

A way with the second method is to delete all the existing ones and readd them, but I'm trying to keep the amount of Db trips down.

Also when adding I don't want to add duplicates, will I have to get all the relationships out and compare in my code?

1.

When you're doing this, you're actually creating a new actor.

movie.Actors.add(new Actor{ Id = actorId });

What you should be doing is first attaching the existing one, and then add it.

var actor = new Actor{ Id = actorId };
context.Actors.Attach(actor);
movie.Actors.Add(actor);

Or in full example:

public void AddRelationship(int movieId, int[] actorIds)
{
    var movie = _context.Movies.FirstOrDefault(x => x.Id == movieId);

    // You might need to do include actors like this:
    //_context.Movies.Include(x => x.Actors).FirstOrDefault(x => x.Id = movieId);

    if(movie == null)
    {
        // Now what?
        throw new Exception("Invalid movieId");
    }

    foreach(var actorId in actorIds)
    {
        var actor = new Actor
        {
            Id = actorId
        };
        _context.Actors.Attach(actor);
        movie.Actors.Add(actor); // EF will detect if it already exists or not.
    }
    _context.SaveChanges();
}

2.

public void ReplaceRelationship(int movieId, int[] actorIds)
{
    var movie = _context.Movies.FirstOrDefault(x => x.Id = movieId);

    // You might need to do include actors like this:
    //_context.Movies.Include(x => x.Actors).FirstOrDefault(x => x.Id = movieId);

    if(movie == null)
    {
        // Now what?
        throw new Exception("Invalid movieId");
    }

    // Get a list of the already existing actors, so we know which to remove.
    var existingActorIds = movie.Actors.Select(x => x.Id).ToList();

    // Add new actors.
    foreach (var actorId in actorIds.Where(x => !existingActorIds .Contains(x.Id)))
    {
        var newActor = _context.Actors.Find(actorId );

       // You might be able to use this instead.
       // var newActor = new Actor { Id = actorId };
       // _context.Actors.Attach(newActor);

        movie.Actors.Add(newActor );
    }

    var idsToRemove =
        existingActorIds.Where(x => !actorIds.Contains(x));
    // Remove the old ones
    foreach (var actorId in idsToRemove)
    {
        var actorEntity = movie.Actors.FirstOrDefault(x => x.Id== actorId );
        // Again, you should be able to use Attach like above.
        // I use FirstOrDefault() since I actually need the full entity later.
        movie.Actors.Remove(actorEntity);
    }
    _context.SaveChanges();
}

A way with the second method is to delete all the existing ones and readd them, but I'm trying to keep the amount of Db trips down.

Yeah, I totally get you. Unfortunately I haven't found a better solution than to actually call Remove() on each one.

Also when adding I don't want to add duplicates, will I have to get all the relationships out and compare in my code?

You can check if the item exists first. But in my cases EF has managed this for me. My mapping-table has two PK (one for MovieId and one for ActorId , which doesn't allow duplicates.

    this.HasMany(t => t.Actors)
        .WithMany(t => t.Movies)
        .Map(m =>
        {
            m.ToTable("ActorMovies");
            m.MapLeftKey("ActorId");
            m.MapRightKey("MovieId");
        });

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