Search code examples
entity-frameworkentity-framework-ctp5

Code First CTP5 Many To Many Binding


I have been using the Code First approach for the Entity Framework. I have an Event class, a Band class and a EventBands class which maps the many to many relationship. The Code First approach worked fine (When I didn't have the EventBands class) but then I decided I wanted the many to many table to store additional values. Now I get this error message:

System.Data.Edm.EdmEntityType: : EntityType 'EventBands' has no key defined. Define the key for this EntityType.

System.Data.Edm.EdmEntitySet: EntityType: The EntitySet EventBands is based on type EventBands that has no keys defined.

It obvious what the error message means. However the resolution isn't so obvious. I think I have to override the model binding method but I am not entirely sure how to map keys with this approach.

Any help would be appreciated, I have included the classes in question below.

Thanks in advance,

Jon

Event:

#region Properties
    private int eventId;
    public int EventId
    {
        get
        {
            return eventId;
        }
        set
        {
            eventId = value;
        }
    }

    private string name;
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }

    private string description;
    public string Description
    {
        get
        {
            return description;
        }
        set
        {
            description = value;
        }
    }

    private DateTime startDatetime;
    public DateTime StartDateTime
    {
        get
        {
            return startDatetime;
        }
        set
        {
            startDatetime = value;
        }
    }

    private DateTime endDatetime;
    public DateTime EndDateTime
    {
        get
        {
            return endDatetime;
        }
        set
        {
            endDatetime = value;
        }
    }

    private int venueUserId;
    public int VenueUserId
    {
        get { return venueUserId; }
        set { venueUserId = value; }
    }

    public virtual Venue Venue
    {
        get;
        set;
    }

    public virtual ICollection<EventReview> Reviews
    {
        get;
        set;
    }

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

    public virtual ICollection<Fan> Attendees
    {
        get;
        set;
    }
    #endregion

    #region Constructor
    public Event()
    {
        EventBands = new HashSet<EventBands>();
        Attendees = new HashSet<Fan>();
        StartDateTime = DateTime.Now;
        EndDateTime = DateTime.Now.AddDays(14);
    }
    #endregion

Band:

public class Band : PostableUser
{
    #region Properties
    private int genreGenreId;
    public int GenreGenreId
    {
        get { return genreGenreId; }
        set { genreGenreId = value; }
    }

    public virtual Genre Genre
    {
        get;
        set;
    }

    public virtual ICollection<Album> Albums
    {
        get;
        set;
    }

    public virtual ICollection<BandReview> Reviews
    {
        get;
        set;
    }

    public virtual ICollection<EventBands> EventBands
    {
        get;
        set;
    }
    #endregion

    #region Constructor
    public Band()
    {
        EventBands = new HashSet<EventBands>();
    }
    #endregion
}

EventBands

#region Properties
    private int eventEventId;
    public int EventEventId
    {
        get { return eventEventId; }
        set { eventEventId = value; }
    }

    public virtual Event Event
    {
        get;
        set;
    }

    private int bandUserId;
    public int BandUserId
    {
        get { return bandUserId; }
        set { bandUserId = value; }
    }

    public virtual Band Band
    {
        get;
        set;
    }

    private DateTime startDateTime;
    public DateTime StartDateTime
    {
        get { return startDateTime; }
        set { startDateTime = value; }
    }

    private DateTime endDateTime;
    public DateTime EndDateTime
    {
        get { return endDateTime; }
        set { endDateTime = value; }
    }
    #endregion

BandUserId is inherited from the User base class.


Solution

  • I got this working in the end by overriding the OnModelCreating method in my DataContext class.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new AlbumConfiguration());
            modelBuilder.Configurations.Add(new BandConfiguration());
            modelBuilder.Configurations.Add(new CityConfiguration());
            modelBuilder.Configurations.Add(new CommentConfiguration());
            modelBuilder.Configurations.Add(new CountryConfiguration());
            modelBuilder.Configurations.Add(new CountyConfiguration());
            modelBuilder.Configurations.Add(new EventBandsConfiguration());
            modelBuilder.Configurations.Add(new EventFansConfiguration());
            modelBuilder.Configurations.Add(new EventConfiguration());
            modelBuilder.Configurations.Add(new FanConfiguration());
            modelBuilder.Configurations.Add(new GenreConfiguration());
            modelBuilder.Configurations.Add(new PostConfiguration());
            modelBuilder.Configurations.Add(new RecordLabelConfiguration());
            modelBuilder.Configurations.Add(new ReviewConfiguration());
            modelBuilder.Configurations.Add(new TrackConfiguration());
            modelBuilder.Configurations.Add(new UserConfiguration());
            modelBuilder.Configurations.Add(new VenueConfiguration());
        }
    

    The Configuration classes contain the mappings of the primary, composite keys, foreign keys, and any other properties.

    Event Configuration:

    public class EventConfiguration : EntityTypeConfiguration<Event>
    {
        public EventConfiguration()
        {
            #region Primary Key
            this.HasKey(x => x.EventId);
            #endregion
    
            #region Foreign Keys
            this.HasRequired(x => x.Venue)
                .WithMany()
                .HasForeignKey(x => x.VenueId)
                .WillCascadeOnDelete(false);
            #endregion
    
            #region Properties
            this.Property(x => x.Description).IsRequired().HasColumnType("nvarchar");
            this.Property(x => x.EndDateTime).IsRequired();
            this.Property(x => x.Name).IsRequired().HasColumnType("nvarchar");
            this.Property(x => x.StartDateTime).IsRequired();
            #endregion
        }
    }
    

    Event Bands Configuration:

    public class EventBandsConfiguration : EntityTypeConfiguration<EventBands>
    {
        public EventBandsConfiguration()
        {
            #region Primary Key
            this.HasKey(x => new { x.BandId, x.EventId });
            #endregion
    
            #region Foreign Keys
            this.HasRequired(x => x.Band)
                .WithMany()
                .HasForeignKey(x => x.BandId)
                .WillCascadeOnDelete(false);
            this.HasRequired(x => x.Event)
                .WithMany()
                .HasForeignKey(x => x.EventId)
                .WillCascadeOnDelete(false);
            #endregion
    
            #region Properties
            this.Property(x => x.StartDateTime).IsRequired();
            this.Property(x => x.EndDateTime).IsRequired();
            #endregion
        }
    }
    

    User Configuration:

    public UserConfiguration()
        {
            #region Primary Key
            this.HasKey(x => x.UserId);
            #endregion
    
            #region Foreign Keys
            this.HasRequired(x => x.City)
                .WithMany()
                .HasForeignKey(x => x.CityId)
                .WillCascadeOnDelete(false);
            #endregion
    
            #region Properties
            this.Property(x => x.UserName);
            this.Property(x => x.LoweredUserName);
            this.Property(x => x.ApplicationName);
            this.Property(x => x.Email);
            this.Property(x => x.Comment);
            this.Property(x => x.Password);
            this.Property(x => x.PasswordQuestion);
            this.Property(x => x.PasswordAnswer);
            this.Property(x => x.IsApproved);
            this.Property(x => x.LastActivityDate);
            this.Property(x => x.LastLoginDate);
            this.Property(x => x.LastPasswordChangedDate);
            this.Property(x => x.CreationDate);
            this.Property(x => x.IsOnline);
            this.Property(x => x.IsLockedOut);
            this.Property(x => x.LastLockedOutDate);
            this.Property(x => x.FailedPasswordAttemptCount);
            this.Property(x => x.FailedPasswordAttemptWindowStart);
            this.Property(x => x.FailedPasswordAnswerAttemptCount);
            this.Property(x => x.FailedPasswordAnswerAttemptWindowStart);
            this.Property(x => x.MobileAlias);
            this.Property(x => x.IsAnonymous);
            this.Property(x => x.Description);
            this.Property(x => x.Website);
            #endregion
    
            #region Inheritance Mapping
            this.Map<Fan>(x => x.Requires("UserType").HasValue("Fan"))
                .Map<Band>(x => x.Requires("UserType").HasValue("Band"))
                .Map<Venue>(x => x.Requires("UserType").HasValue("Venue"));
            #endregion
        }
    }
    

    Band Configuration

    public class BandConfiguration : EntityTypeConfiguration<Band>
    {
        public BandConfiguration()
        {
            #region Foreign Keys
            this.HasRequired(x => x.Genre)
                .WithMany()
                .HasForeignKey(x => x.GenreId)
                .WillCascadeOnDelete(false);
            #endregion
        }
    }