Monday, February 4, 2013

Entity Framework Fluent API Mappings

Entity Framework Code First allows you to take control of your database schema. You can do this two ways, decorating your POCO's with attributes or using the fluent API. This post will show how to define relationships using the fluent API.

One to Many With Foreign Key:
public class Chats
{
   // Chats has one to many with ChatMessage
   public virtual ICollection<ChatMessage> ChatMessages { get; set; }

   // Chats has one to many with EventGroup
   public virtual ICollection<EventGroup> EventGroups { get; set; }

   // Chats is on the many side of one to many with Users
   // Set up navigation by providing Id and the POCO to reference back
   public virtual int UserId { get; set; }
   public virtual Users User { get; set; }

   // Chats is on the many side of one to many with ChatType
   // Set up navigation by providing Id and the POCO to reference back
   public virtual int ChatTypeId { get; set; }
   public virtual ChatType ChatTypeEntity { get; set; }
}

public ChatsConfig()
{
   // Chats is on the many side of one to many with Users
   HasRequired(c => c.User)
      .WithMany(u => u.Chats)
      .HasForeignKey(c => c.UserId);

   // Chats is on the many side of one to many with ChatType
   HasRequired(c => c.ChatTypeEntity)
      .WithMany(cte => cte.Chats)
      .HasForeignKey(c => c.ChatTypeId);
}

The Chats POCO has two ICollection properties and two navigation properties back to the referenced POCO's. We configure the one side which is represented by the navigation property and the corresponding Id. In this case, UserId and User, ChatTypeId and ChatTypeEntity.

 
 


Another One to Many With Foreign Key:

public class Artist
{
   public virtual int AritstId { get; set; }
   public virtual string AritstName { get; set; }
   public virtual string Country { get; set; }

   public virtual Genre Genre { get; set; }
   public virtual ICollection<Title> Titles { get; set; }
}

public ArtistConfig()
{
   // Artist has many Titles, required
   HasMany(a => a.Titles).WithRequired(t => t.Artist);
}

This approach is different from above. This time we are mapping the side that contains the ICollection<T>, as we can see the ICollection<Title> Titles property in the Artist class. Another difference is that we are using WithRequired here. This simply means that both ends of the relationship must exist in order for it to be valid. This does have a drawback. If you really want to delete an item from the collection, this set up will not work. You would have to delete the entire relationship. To get around that, you could make a composite key which will allow deleting collection items.

 


Composite Primary Key:
public class AboutUsImage
{
   public virtual long DealerId { get; set; }
   public virtual short ImageOrder { get; set; }
}

public AboutUsImageConfig()
{
   HasKey(aui => new { aui.DealerId, aui.ImageOrder } );
}

This one is pretty straightforward. You just take the two columns you need for the composite key and put them together in an anonymous object.

 


Composite Primary Key With Foreign Key:
public class Permission
{
   public virtual long UserId { get; set; }
   public virtual string PermissionType { get; set; }
   public virtual string AccessLevel { get; set; }

   public virtual User User { get; set; }
}

public PermissionConfig()
{
   // composite key, UserId and PermissionType
   // a User has many Permissions so UserId is also FK
   HasKey(p => new { p.UserId, p.PermissionType } )
      .HasRequired(p => p.User).WithMany(p => p.Permissions);

The composite key is the same as above. This foreign key case is a bit odd. Note that the foreign key is not explicitly defined here. EF inferred it. If you want to explicitly define, it would look like this:
   HasKey(p => new { p.UserId, p.PermissionType })
      .HasRequired(p => p.User).Withmany(p => p.Permissions)
      .HasForeignKey(p => p.UserId);
 
 

One to One:
public class User
{
   public virtual int UserId { get; set; }
   public virtual string Name { get; set; }
   pubilc virtual Address Address { get; set; }
}

public class Address
{
   public virtual int AddressId { get; set; }
   public virtual string Street { get; set; }
   public virtual string City { get; set; }
   public virtual string Zip { get; set; }
}

public UserConfig()
{
   HasOptional(u => u.Address).WithRequired();
}

To specify the one to one, you use HasOptional and WithRequired. There is no need to use any Id properties. If you do not see the relationship itself, create a diagram in SQL Server Management Studio.

 


Many to Many Relationship:
public class UserProfile
{
   public int UserId { get; set; }
   public string UserName { get; set; }
   public virtual ICollection Roles { get; set; }
}

public class Role
{
   public int RoleId { get; set; }
   public string RoleName { get; set; }
   public virtual ICollection UserProfiles { get; set; }
}

Right away we can see a relationship between UserProfile and Role. They each contain an ICollection of each other, of course, this is an indication of a many to many relationship. It doesn't matter which entity we choose, either will get the job done. We'll use UserProfile. Define the properties as normal for the POCO. But when dealing with the ICollection, this is where we set up the many to many:

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;

public class UserProfileConfig : EntityTypeConfiguration
{
   public UserProfileConfig()
   {
      HasKey(up => up.UserId);
      Property(up => up.UserId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      Property(up => up.UserName).HasColumnName("UserName").HasColumnType("nvarchar").HasMaxLength(128).IsOptional();

      HasMany(up => up.Roles)
         .WithMany(r => r.UserProfiles)
         .Map(usersinroles =>
         {
            usersinroles.MapLeftKey("UserId");
            usersinroles.MapRightKey("RoleId");
            usersinroles.ToTable("webpages_UsersInRoles");
         });

      ToTable("UserProfile");
   }
}