2

I am trying to use code-first and Data Annotations with EF 6.0 with the following, very simple, model of two classes: basically employees who can be organizers and attendees of an event:

public class Employee
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    public virtual ICollection<Event> event { get; set; }
}

public class Event
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    [ForeignKey("organizer")]
    public Guid organizerId { get; set; }
    public virtual Employee organizer { get; set; }

    public virtual ICollection<Employee> atendees { get; set; }
}

When I let EF generate the database from these classes it surprisingly doesn't generate the many-to-many relationship between Employee and Event (so, no EmployeeEvent many-to-many table), rather it generates an event-id foreign key in Employees. (I have no clue why.)

Of course, I can directly create the proper db mapping by using the fluent-APIs, but I was wondering if there is a way to achieve this with data annotations.

Laszlo
  • 23
  • 3
  • 2
    `event` is a reserved keyword. That's probably not the problem, but you shouldn't (be able to) call a property that. But this collection, is it for all the events that employee is an organizer of or is an attendee of? Maybe you need to play around with the [InverseProperty](http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx) attribute a little. – Corak Mar 22 '16 at 15:43
  • Most probably it maps `Event.organizer` to `Employee.event` as many-to-one. – Ivan Stoev Mar 22 '16 at 16:03
  • Please [edit] your question so that your title actually covers your question. – CodeCaster Mar 22 '16 at 17:12
  • Thank you, Corak, for the InverseProperty idea - it solves the problem. Ivan - yes, you are correct EF just considered the organizer's relationship w/o the InverseProperty annotation. Thank for the suggestion CodeCaster - I changed the title. – Laszlo Mar 23 '16 at 01:47

3 Answers3

2

I think the Employee organizer is confusing to EF, that's why Ef thinks you have a one-to-many relationship; if that's the case then you can solve it by using InverseProperty as @Corak said

public class Employee
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    [InverseProperty("atendees")]
    public virtual ICollection<Event> event { get; set; }
}

public class Event
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    [ForeignKey("organizer")]
    public Guid organizerId { get; set; }
    public virtual Employee organizer { get; set; }

    [InverseProperty("event")]
    public virtual ICollection<Employee> atendees { get; set; }
}

That should be enough to solve the problem without Fluent API

Also for more information on InverseProperty you can take a look at this answer

Community
  • 1
  • 1
Enrique Zavaleta
  • 2,098
  • 3
  • 21
  • 29
  • Thanks, Enrique - this is what I was looking for. Works like a charm. Of course, I still have the circular cascade delete issue as Ivan mentioned, but it is a different matter. – Laszlo Mar 23 '16 at 01:40
1

The model you are describing might seem simple to you, but is not so trivial from the database design standpoint, that's why EF cannot correctly derive the "obvious" configuration.

First, you have actually 2 relationships between Employee and Event:

(1) one-to-many employee -> event describing events that employee is organizing
(2) many-to-many employee -> event describing events that employee is attending

So the model should be like this:

public class Employee
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    public virtual ICollection<Event> organizingEvents { get; set; }
    public virtual ICollection<Event> attendingEvents { get; set; }
}

public class Event
{
    [Key]
    public Guid Id { get; set; }
    public string name { get; set; }

    [ForeignKey("organizer")]
    public Guid organizerId { get; set; }
    public virtual Employee organizer { get; set; }

    public virtual ICollection<Employee> attendees { get; set; }
}

The additional problem is that such model creates a possibility of a circular cascade delete issue. Speaking about limitations, cascade options cannot be specified with data annotations, so it really needs a fluent configuration. And once it needs it anyway, why bother with data annotations, let configure the relations fully with fluent API:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasMany(e => e.organizingEvents)
        .WithRequired(e => e.organizer)
        .HasForeignKey(e => e.organizerId)
        .WillCascadeOnDelete(false);

    modelBuilder.Entity<Employee>()
        .HasMany(e => e.attendingEvents)
        .WithMany(e => e.attendees);
}
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Thanks, Ivan, yes, you are correct, the circular cascade issue is there anyway, so using fluent is the long term (and scalable) route. – Laszlo Mar 23 '16 at 01:42
0

As @Ivan Stoev's answer you may use fluent API as well.If you really want to achieve this by data annotation, you can have one class for middle table and you can map with one to many relationships.Try the following:

 public class Employee
    {
        [Key]
        public Guid Id { get; set; }
        public string name { get; set; }
        public virtual ICollection<middle_table> Middle_table{ get; set; }
        public virtual ICollection<Event> event { get; set; }
    }

    public class Event
    {
        [Key]
        public Guid Id { get; set; }
        public string name { get; set; }
        [ForeignKey("organizer")]
        public Guid organizerId { get; set; }
        public virtual Employee organizer { get; set; }
        public virtual ICollection<middle_table> Middle_table{ get; set; }
    }
    public class your_middle_class
    {
    public Guid Id { get; set; }
    public virtual Employee organizer { get; set; }
     public virtual Event Event{ get; set; }
    }