1

I have a table restaurant. A restaurant can have multiple specialties. And specialties are for example chinees, spahnish, greeks, etc.

Restaurant table/class:

[Table("Restaurant")]
public class Restaurant
{
    [Key]
    [Column(Order = 0)]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    [MaxLength(40)]
    [Column(Order = 2)]
    public string Name { get; set; } 

    [MaxLength(1000)]
    [Column(Order = 6)]
    public string Comments { get; set; }

    public virtual ICollection<Specialties> Specialties { get; set; }
}

And this is my specialties table/class:

 public enum Specialty
    {
        Chinees = 0,
        Spanish = 1,
        Etc.. = 2,
    }

    [Table("Specialties")]
    public class Specialties
    {
        [Key]
        [Column(Order = 0)]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        [Column(Order = 1)]
        public Specialty Specialty { get; set; } 

        //[Required]
        [MaxLength(20)]
        [Column(Order = 2)]
        public string Name { get; set; } 

        public virtual ICollection<Restaurant> Restaurant { get; set; }
    }

The specialties table is pre-filled with data.

This is my table/class for the many to many relationship:

[Table("RestaurantSpecialties")]
    public class RestaurantSpecialties
    {
        [Key]
        public int RestaurantId { get; set; }

        [Key]
        public int SpecialtyId { get; set; }

        [ForeignKey("RestaurantId")]
        public virtual Restaurant Restaurant{ get; set; }

        [ForeignKey("SpecialtyId")]
        public virtual Specialties Specialty { get; set; }
    }

With Fluent Api I tried to define the model like this:

// Primary keys
modelBuilder.Entity<Restaurant>().HasKey(q => q.Id);
modelBuilder.Entity<Specialties>().HasKey(q => q.Id);

// Relationship
modelBuilder.Entity<Restaurant>()
    .HasMany(q => q.Specialties)
    .WithMany(q => q.Restaurant)
    .Map(q =>
    {
        q.ToTable("RestaurantSpecialty");
        q.MapLeftKey("RestaurantId");
        q.MapRightKey("SpecialtyId");
    });

And in my Seed function i'm seeding data like this:

context.Specialties.AddOrUpdate(
    p => p.Specialty,
    new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
    new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
    );

ICollection<Specialties> lstSpecialties = new List<Specialties>();
lstSpecialties.Add(context.Specialties.FirstOrDefault(x => x.Name == "Chinees"));

context.Restaurants.AddOrUpdate(
  p => p.Name,
  new Restaurant{ Name = "Ctr test1", Specialties = lstSpecialties, Comments = "sfasd" },
  new Restaurant{ Name = "Ctr test2", Specialties = lstSpecialties, Comments = "asd" },
  );

But my table RestaurantSpecialties doesn't contain anything. I was expecting to see Id's of both tables.

What am I doing wrong?

[EDIT]

The enum has been renamed from 'Specialty' to 'RestaurantSpecialty' like Gert suggested to do (just the rename part, not the actual name).

The plural class 'Specialties' has also been renamed to 'Specialty'. Also because Gert suggested it.

The join table has also been removed from the model. The Fluent Api part in my original post has been updated accordingly.

Catering(s) has been renamed to Restaurant(s) which i forgot to do so in the beginning. So to avoind confusions i've done this too in my original code post.

context.Specialties.AddOrUpdate(
   new Specialty { CateringSpecialty = RestaurantSpecialty.Chinese, Name = "Chinese" },
   new Specialty { CateringSpecialty = CateringSpecialty.Greeks, Name = "Greeks" },
);


var aSpecialty = context.Specialties.FirstOrDefault(x => x.Name == "Chinese");
var restaurant1 = context.Restaurants.Include(c => c.Specialties).FirstOrDefault(x => x.Name == "Ctr test1");

 if (restaurant1 == null)
 {
    restaurant1 = new Restaurant
    {
         Name = "Ctr test4",
         Specialties = new List<Specialty> { aSpecialty },
         Comments = "testtttttttt"
    };
    context.Restaurants.Add(restaurant1);
 }
 else
 {
   if (!restaurant1.Specialties.Any(s => s.Id == aSpecialty.Id))
        restaurant1.Specialties.Add(aSpecialty);

       restaurant1.Name = "Ctr test4";
       restaurant1.Comments = "testtttt";
 }
Yustme
  • 6,125
  • 22
  • 75
  • 104
  • First off, `RestaurantSpecialties` should not be part of the class model if you map a many to many association. – Gert Arnold Jun 18 '13 at 09:55
  • Hi, i'm not sure what mapping i should use. either a many to many or twice one to many. what would you suggest? – Yustme Jun 18 '13 at 10:03
  • That's up to you. It's a pure junction table so you can do it either way. It depends on whether you want to use foreign key associations (with association class) or independent associations (without). Side note: don't use a plural word for a class name (`Specialties`). Rename both the class and the enum. – Gert Arnold Jun 18 '13 at 10:11
  • Okay, i've done it this way. And my restaurantspecialties table doesn't get filled with id's. any idea what i might be doing wrong? – Yustme Jun 18 '13 at 11:02
  • According to this post it is not possible to create many-to-many, you have to create two one-to-many relationships http://stackoverflow.com/questions/7050404/create-code-first-many-to-many-with-additional-fields-in-association-table – Alex S Jun 18 '13 at 13:58
  • That was one of my questions, how to make the twice one to many relationships. I've tried several ways, including that post you linked here which i found like a few hours ago. Nothing what i try seems to fill the restaurantspecialties table. – Yustme Jun 18 '13 at 14:22

1 Answers1

1

Try to add/update manually. I suspect that the AddOrUpdate method does not support updating a full object graph of related entities. Probably it supports adding related entities together with its parent entity if the parent entity doesn't exist yet. But if the "Chinees" specialty was already in the database without related entities the relationships possibly don't get updated.

A "manual update" would look like this:

context.Specialties.AddOrUpdate(
    p => p.Specialty,
    new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
    new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
    );

var chineesSpecialty = context.Specialties
    .FirstOrDefault(x => x.Name == "Chinees");

var catering1 = context.Caterings.Include(c => c.Specialties)
    .FirstOrDefault(x => x.Name == "Ctr test1");

if (catering1 == null)
{
    catering1 = new Catering
    {
        Name = "Ctr test1",
        Specialties = new List<Specialties> { chineesSpecialty },
        Comments = "sfasd"
    };
    context.Caterings.Add(catering1);
}
else
{
    if (!catering1.Specialties.Any(s => s.Id == chineesSpecialty.Id))
        catering1.Specialties.Add(chineesSpecialty);
    catering1.Comments = "sfasd";
}

// and the same for "Ctr test2"

This is also mentioned in this answer but it was during the early preview releases of Code-First Migrations, so I am not sure if this limitation still exists. But it would explain why you don't get entries in the relationship table.

Your mapping itself looks correct to me. (But as already recommended by Gert Arnold in the comments I would really remove the RestaurantSpecialties entity which looks redundant and only complicates your model.)

Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420
  • Hi, i just tried your approach and the join table didnt get filled either. I'm updating my first post with the code you provided. – Yustme Jun 19 '13 at 07:51
  • The post has been updated. To give you more info, i don't get any errors, but the join table that is created by the modelbuilder, doesn't get filled when adding a specialty to a restaurant. – Yustme Jun 19 '13 at 08:04
  • 1
    @Yustme: Can you add a `context.SaveChanges` **before** the `context.Specialties.FirstOrDefault...` line and try again. If you migrate from a database where the chinese specialty isn't yet in the DB `FirstOrDefault` would return `null` because adding the chinese specialty hasn't been committed yet. – Slauma Jun 19 '13 at 16:30
  • Crazy, i had the saveChanges line like twice in it. Think i removed it, but that did the trick! Thanks! – Yustme Jun 19 '13 at 17:41