2

I really need someone to help me to fully understand how to do many-to-many relationship with Entity Framework 4 CTP 5, POCO. I need to understand 3 concepts:

  1. How to config my model to indicates some tables are many-to-many.
  2. How to properly do insert.
  3. How to properly do update.

Here are my current models:

public class MusicSheet
{
    [Key]
    public int ID { get; set; }
    public string Title { get; set; }
    public string Key { get; set; }

    public virtual ICollection<Author> Authors { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
}

public class Author
{
    [Key]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Bio { get; set; }

    public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}

public class Tag
{
    [Key]
    public int ID { get; set; }
    public string TagName { get; set; }

    public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}

As you can see, the MusicSheet can have many Authors or Tags, and an Author or Tag can have multiple MusicSheets.

Again, my questions are:

  1. What to do on the EntityTypeConfiguration to set the relationship between them as well as mapping to an table/object that associates with the many-to-many relationship.
  2. How to insert a new music sheets (where it might have multiple authors or multiple tags).
  3. How to update a music sheet. For example, I might set TagA, TagB to MusicSheet1, but later I need to change the tags to TagA and TagC. It seems like I need to first check to see if the tags already exists, if not, insert the new tag and then associate it with the music sheet (so that I doesn't re-insert TagA?). Or this is something already handled by the framework?

Thank you very much. I really hope to fully understand it rather than just doing it without fully understand what's going on. Especially on #3.

Saxman
  • 5,009
  • 11
  • 51
  • 72

2 Answers2

5
  1. In the EF4 CTP5 the relationship is done by default convention when you put public virtual ICollection in each of the classes of the many to many relationship, as you already have done, your context class should look like this:

    public class YourContextName : DbContext
    {
        public DbSet<MusicSheet> MusicSheets { get; set; }
        public DbSet<Tag> Tags { get; set; }
        public DbSet<Author> Authors { get; set; }
    }
    
  2. Very simple you just create a instance of the MusicSheet class and then add all the instances of you authors and tags to each of the collections of Authors and Tags in your MusicSheet, and then add your instance of MusicSheet to your context collection of MusicSheets and then call SaveChanges:

            MusicSheet musicSheet = new MusicSheet
                                    {
                                        Title = "Music Sheet 1",
                                        Key = "Key",
                                        Authors = new List<Author>
                                                      {
                                                          new Author
                                                              {
                                                                  Name = "Author 1",
                                                                  Bio = "Author 1 biographic text..."
                                                              },
                                                          new Author
                                                              {
                                                                  Name = "Author 2",
                                                                  Bio = "Author 2 biographic text..."
                                                              }
                                                      },
    
                                        Tags = new List<Tag>
                                                   {
                                                       new Tag {TagName = "TagA"},
                                                       new Tag {TagName = "TagC"}
                                                   }
                                    };
    
    
        var context = new YourContextName();
        context.MusicSheets.Add(musicSheet);
        context.SaveChanges();
    
  3. To update you have to load your MusicSheet and remove the tags you don't want and then add the ones you need to add, this is how:

        var context = new YourContextName();
        var myMusicSheet = context.MusicSheets.First();
    
        //The Tag you wnat to remove.
        var tagToRemove = myMusicSheet.Tags.First();
    
        var tagToAdd = new Tag {TagName = "TagX"};
    
        myMusicSheet.Tags.Remove(tagToRemove);
        myMusicSheet.Tags.Add(tagToAdd);
    
        context.Entry(myMusicSheet).State = EntityState.Modified;
        context.SaveChanges();
    

You can also find any author and/or tag that you know that exist and added to your MusicSheet and vice versa, but this is the foundation.

Remember this is for the EF4 CTP5 Code first...

Excuse me my English is not my main language, I hope this can help you, best regards from Dominican Republic.

PS: Don't forget to add references to EntityFramework and System.Data.Entity, is your responsibility to do anything else like unit test, validation, exception handling...etc

EDIT:

First you need to add a constructor to your models:

public class Tag
{
    [Key]
    public int ID { get; set; }
    public string TagName { get; set; }

    public Tag()
    {
       MusicSheets = new List<MusicSheet>();
    }        

    public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}

...Then you can do something like this:

var context = new YourContextName();
var newMusicSheet = new MusicSheet();
    newMusicSheet.Title = "Newly added Music Sheet";

//Your existing Tag.
var existingTag = contex.Tags.Find(3);        

existingTag.MusicSheets.Add(existingTag);

context.Entry(existingTag).State = EntityState.Modified;
context.SaveChanges();

You can do the same for all your models.

I hope this can help you!

Ramón García-Pérez
  • 1,880
  • 2
  • 20
  • 25
  • Thank you very much, I got #1 & #2 answered and understood :) On my question of updating: I need to find the existing tags/authors and remove them before adding new one? I can't just supply them with whatever tags/authors and the framework just go out there and update itself? Just making sure :) Thanks again. – Saxman Feb 14 '11 at 16:41
  • Yes, you can gain more flexibility in the insertion process by initializing your collections in the constructor.Cheers man! – Ramón García-Pérez Feb 14 '11 at 18:07
  • Hey BBHorus, I have another question on inserting. What if I insert a new music sheet with some of the tags that's already exists? How would I do that without creating duplicate tags? Thanks. – Saxman Feb 15 '11 at 18:24
  • I edit my answer to show how to add new music sheet to existing tag. PS: Am I the only one who is hitting Enter for a new line and instead is saving the comment, it happens to me several times. Regards – Ramón García-Pérez Feb 16 '11 at 16:00
  • Thanks BBHorus, I'll give it a try and see if that works :) Thanks again. – Saxman Feb 16 '11 at 19:53
  • I'm having some problem: first, this should be `existingTag.MusicSheets.Add(nweMusicSheet)`, correct? Second, I'm using unit of work and repository pattern via this post (http://stackoverflow.com/questions/4442828/entity-framework-4-ctp-4-ctp-5-generic-repository-pattern-and-unit-testable), which doesn't give me `....Entry(existingTag).State = EntityState.Modified;` option. I'm using the `IMusicRepository` and `MusicRepository`. Can you help? Thanks. – Saxman Feb 16 '11 at 20:15
  • Surprisingly, all I did was `_unitOfWork.Commit()` (where `_unitOfWork` is the UnitOfWork class) and it insert the sheet/tags/authors/artists, all correctly. Thanks! – Saxman Feb 16 '11 at 20:24
0

You do not really need an EntityTypeConfiguration to set the relationship between them. It should work as it is right now. With CTP5 all you have to do to establish a many-to-many relationship is to include ICollection in both entities.

Now about how to perform inserts and deletes, there are two ways I know of. The one I usually use is create an entity for the resultant table of the many-to-many relationship, then create an instance of this entity and feed it with all the data that is required, including instances of the other entities (the ones that have the many-to-many relationship). And finally I simply add it to the repository and commit the transaction (usually using a UnitOfWork class).

Quick example:

public class Item
{
    public int ID { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Bid> Bids { get; set; }
}

public class User 
{
    public int ID { get; set; }
    public string Username{ get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public virtual ICollection<Bid> Bids { get; set; }
}

public class Bid
{
    public int ID { get; set; }
    public float Amount { get; set; }
    public DateTime Date { get; set; }
    public virtual Item Item { get; set; }
    public virtual User User { get; set; }
}

Then I would simply create instances of the Bid entity.

public void PlaceBid(User user, Item item, int amount)
{
    if (ValidateBid(amount, user, item))
    {
        Bid bid = new Bid
        {
            Amount = amount,
            Date = DateTime.Now,
            User = user,
            Item = item
        };

        try
        {
            repository.Add(bid);
            unitOfWork.Commit();
        }
        catch (Exception ex)
        {
            //TODO: Log the exception
            throw;
        }
    }
}
Kassem
  • 8,116
  • 17
  • 75
  • 116