1

Basically I have a multiselect list box in MVC and when the user changes selection it will come back and it should update the model. the below code works but I am just wondering how can I put it in a single foreach loop or is there a better way of updating the selection? Note: There is a many to many relationship between artist and artist type.

foreach (var artistTtype in this._db.ArtistTypes.ToList().Where(artistTtype => artist.ArtistTypes.Contains(artistTtype)))
{
    artist.ArtistTypes.Remove(artistTtype);
}

foreach (var artistTtype in this._db.ArtistTypes.ToList().Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)))
{
    artist.ArtistTypes.Add(artistTtype);
}
Filburt
  • 17,626
  • 12
  • 64
  • 115
akd
  • 6,538
  • 16
  • 70
  • 112
  • Take a look at this: [How should I remove all elements in a DbSet?](http://stackoverflow.com/questions/10448684/how-should-i-remove-all-elements-in-a-dbset). Maybe it's easier to execute the SQL in order to remove the `ArtistType` collection elements (by specifying the `ArtisId` in the `where` clause). – Alex Filipovici Nov 19 '13 at 09:59

2 Answers2

2

This for adding (just use AddRange):

artist.ArtistTypes.AddRange(this._db.ArtistTypes
         .Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)));

This for removing (use ForEach):

 this._db.ArtistTypes
         .Where(artistTtype => artist.ArtistTypes.Contains(artistTtype)).ToList()
              .ForEach(x=>artist.ArtistTypes.Remove(x));

EDIT:

you can always set

artist.ArtistTypes = this._db.ArtistTypes
         .Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)).ToList();

this will set ArtistTypes to what you want, you don't need to delete then add.

Kamil Budziewski
  • 22,699
  • 14
  • 85
  • 105
  • so imagine before user edit the multi selection number 1 and 4 was selected. when the user goes and only select number 2 and 3 the code should go and remove number 1 and 4 and add 2 and 3 instead. is there a better way of handling this or I still need to right 2 line of code one for adding one for removing ? – akd Nov 19 '13 at 09:58
  • it gives cannot convert System.Iqueryable<> to Generic.Collection<> ? – akd Nov 19 '13 at 10:29
  • A quick question if you can answer.. How would I delete an artist for example? DO I need to go and clear all the artist type first or it does it automatically when I delete an artist with this._db.Entry(artist).State = EntityState.Deleted; ? – akd Nov 19 '13 at 11:04
  • @akdurmus I'm not a EF expert :) I supose that `_db.Artists.Delete(artist)` will do, can you do this? – Kamil Budziewski Nov 19 '13 at 11:06
0

I see two "fixes":

1) You don't need to care about what's inside the list, since you're going to update the list of selections you can start from scratch, so the removal part becomes

artist.ArtistTypes.Clear();

2) Now you fill the list again. ToList() should not be needed since you're performing a .Where() to get the data, and you can leverage Linq's lazy mechanisms so you'll only read the data you actually use. You can also split the lines for increased readability (it doesn't matter: until you do the foreach() the db will not be actually hit.

//note that the .ToList() is gone
var query = this._db.ArtistTypes.Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID);
foreach (var artistTtype in query))
{
    artist.ArtistTypes.Add(artistTtype);
}

2b) (UNTESTED, off the top of my head) Another way of implementing the comparison you do is through a custom IEqualityComparer, switching to .Intersect() method. This is way more solid since if your keys change in the model you only have to change the comparer.

// I'm making up "ArtistType", fix according to your actual code
class ArtistTypeEqualityComparer : IEqualityComparer<ArtistType>
{
    public bool Equals(ArtistType x, ArtistType y)
    {
        if (ArtistType.ReferenceEquals(x, null)) return false;
        if (ArtistType.ReferenceEquals(y, null)) return false;
        if (ArtistType.ReferenceEquals(x, y)) return true;

        return x.ArtistTypeId.Equals(y.ArtistTypeId);
    }
    public int GetHashCode(ArtistType obj)
    {
        return obj.ArtistTypeId.GetHashCode();
    }
}

// And then the "add" part simplifies

artist.ArtistTypes.AddRange(this._db.ArtistTypes.Intersect(vm.SelectedIds.Select(x => new ArtistType{ ArtistTypeId = x }));
Alex
  • 23,004
  • 4
  • 39
  • 73