Description of relationships using the Code First Fluent API

Original author: Julie Lerman
  • Transfer
  • Tutorial
Trying to understand ASP.NET MVC 4 manuals in more detail, I came across such concepts as the Fluent API, Code First, annotations, and many others. There wasn’t much information on the Fluent API. Especially in Russian. We look.

The Code First approach in the Entity Framework allows you to use your own domain classes to represent the model that EF uses to build queries, track changes, and update. Code First uses a programming pattern called configuration convention. This means that Code First believes that your classes follow the conventions of the schema that EF uses for the conceptual model. In this case, EF will be able to use the necessary parts to perform its functions. However, if your classes do not use the conventions correctly, you can add the necessary configuration manually so that EF can correctly understand them.

Using the Code Firs approach, you can define these configurations in two ways. The first is to use simple attributes called DataAnnotations. The second is to use the Fluent API, which allows you to describe configurations imperatively in code.

This article focuses on customization using the Fluent API. Code Firs conventions are very useful for describing relationships between classes based on properties that point to descendants or individual classes. If your classes do not have foreign keys, Code Firs can create them themselves. But there are times when the class description does not provide enough information about the relationship, so that Code Firs could correctly understand everything and correctly add the "missing" parts.

Consider the model



Let's start with two simple classes, “Blog” and “Post,” where Blog has a one-to-many relationship with Post.

public class Blog
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string BloggerName { get; set; }
        public virtual ICollection Posts { get; set; }
    }
    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime DateCreated { get; set; }
        public string Content { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }


Understanding the one-to-many relationship convention



One common way to define a one-to-many relationship in a class is to create a child collection in one class and a foreign key, along with the navigation property in the child class. In the above example, Blog has a Posts property, which is a collection of ICollection of type Post. And Post, in turn, has a foreign key, BlogID, also known as the Blog navigation property, which points back to its Blog ancestor.

This data is enough for Code First to be able to create the following tables in the database on the basis of the convention:

image

Note that Code First made the BlogId field a foreign key (the restriction between the primary key / foreign key of Posts.BlogId and Blogs.Id is determined) and it is non-nullable. Code First drew these conclusions based on the convention described in the class.

Using HasRequired when a foreign key property is not specified



What happens if you do not set the BlogId property in the Post class, but set the Blog navigation property. Code First will still create the necessary relationship, because it knows that the Blog property points back to the Blog entity. He will create the Posts.Blog_Id foreign key, as shown in Figure 2, and associate it with Blog.Id.

image

However, there is one important detail. Blog_Id is nullable. Then it becomes possible to add Posts that are not related to Blog. So Code First, having studied the convention of the class, I understood it, but this is not quite what the developer sought. We will use the Fluent API to correct that inaccuracy.

The Fluent API configuration is used in Code First when building a model from a class. You can embed configurations by overriding the OnModelCreating method of the DbContext class, as shown below:

public class BlogContext : DbContext 
    {
        public DbSet Blogs { get; set; }
        public DbSet Posts { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
         //configure model with fluent API
        }


DbModelBuilder gives you the ability to intercept settings. We inform the model builder that we want to change one of the entities. For this we use generics, that we will change the essence of Post. After gaining access to it, it becomes possible to use the HasRequired method, which is part of the relationship, to indicate that the navigation property is required - in this case, the Blog property.

modelBuilder.Entity().HasRequired(p => p.Blog);


The result is a double effect. The first is that the Blog_Id field will become not null again. And also EF will perform validation on demand or before saving to the database, so that it would be sure that all the specified conditions are met.

Customizing Custom Foreign Key Names



Even if you specified a foreign key, its name does not always coincide with Code First conventions. By convention, the key name must match the name of the class or "classname_Id" or "classnameId". That's why Code First was able to work properly with the original class property, BlogId.

But what happens if the name is not by convention? For example, “FK” + parent_class_name + “Id”.

public int FKBlogId { get; set; }


Code First does not know that FKBlogId should act as a foreign key. It will create a standard column for FKBlogId and also Blog_Id, which will become the foreign key, since it is necessary for linking the Blog property.

image

Also, working further with Blog and Post, FKBlogId will never be perceived by the program as a foreign key pointing back to the blog.

Using the Fluent API, you can solve this problem too - take FKBlogId as a foreign key in relation to Blog.

When setting up, you must specify both ends of the relationship — a property on Blog indicating a multiple relationship (Posts) and a Post property pointing back to its parent (Blog).

First, you need to add the WithMany method, which allows you to specify which Blog property contains a multiple relationship.

modelBuilder.Entity().HasRequired(p => p.Blog)
                .WithMany(b => b.Posts)


Then you can add the HasForeignKey method, which indicates which Post property is the foreign key and points back to the blog. Full code:

modelBuilder.Entity().HasRequired(p => p.Blog)
                .WithMany(b => b.Posts)
                .HasForeignKey(p => p.FKBlogId);


Now Code First can collect all the necessary data for the correct construction of the database schema and relationships, which the developer suggests.

image

Defining a Spreadsheet Schema and Many-to-Many Relationships



If necessary, you can easily define many-to-many relationships using properties pointing to each other. For example, if you add the Tag class to a model to track Tags in Posts, you will need a many-to-many relationship between them.

New Tag Class:

public class Tag
    {
        public int TagId{ get; set; }
        public string Name { get; set; }
        public ICollection Posts { get; set; }
    }


New property to add to the Post class:

public ICollection Tags { get; set; }


Code first assumes that the name of the spanning table will consist of a combination of the names of two classes, and the table will contain foreign keys whose names consist of a combination of the class name and the key name. In this case, we are working with Post.Id and Tag.TagId. If you allow Code first to create a spanning table yourself, it will look like this:

image

Also, if you allow Code first to create a database yourself, then as a rule no problems arise. But if you are mapping to an existing database, then there may be problems with the names of tables, columns. Use the Fluent API to specify table and column names.

Consider an example when you need to specify three names. The table name must be PostJoinTag, and the column names TagId and PostId.

Let's start with the Entity mapping method. What to describe first Post or Tag does not matter. Choose Post. Denote both ends of the relationship. And just as the one-to-many relationship was indicated in the previous example, we will do the same using the HasMany and WithMany methods. We point out that the Post entity has multiple relationships with the Tags property, and the Tag entity has multiple relationships with the Posts property:

modelBuilder.Entity()
                .HasMany(p => p.Tags)
                .WithMany(t => t.Posts)
                .Map(mc =>
                   {
                       mc.ToTable("PostJoinTag");
                       mc.MapLeftKey("PostId");
                       mc.MapRightKey("TagId");
                   });


image

You need to pay attention when determining the left and right mapping keys (MapLeftKey, MapRightKey). The left one is the first class key that you pointed to, that is, Post, and the right one is the second class. If you swap them, then the data will not be saved correctly, which will lead to known negative consequences.

conclusions



You’ve become familiar with some of the features of describing relationships using the Code First fluent API. There are other mappings that can be used separately or in combination. Some may seem redundant or confusing, such as IsRequired and HasRequired or WithMany and HasMany. However, you should have seen the tasks for which each of them is responsible, as well as the benefits of using them.

You can read more about the Entity Framework on MSDN or on the Entity Framework team blog .

Also popular now: