📜 ⬆️ ⬇️

Describing relationships using the Code First Fluent API

Trying to understand the ASP.NET MVC 4 manuals in more detail, I ran into concepts like Fluent API, Code First, annotations, and many others. By Fluent API was not so much information. 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 updates. Code First uses a programming pattern called configuration agreement. This means that Code First considers your classes to follow the schema convention that EF uses for the conceptual model. In this case, the EF will be able to use the necessary details to perform its functions. However, if your classes do not use the correct conventions, you can add the necessary configuration manually, so that EF can understand them correctly.

Using the Code Firs approach, you can define these configurations in two ways. The first is to use simple attributes called annotations (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 pointing to descendants or individual classes. If your classes do not have foreign keys, Code Firs can create them yourself. But there are cases when the class description does not provide enough information regarding the relationship, so that Code Firs could understand everything correctly and add the “not enough” parts correctly.

Consider the model



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

public class Blog { public int Id { get; set; } public string Title { get; set; } public string BloggerName { get; set; } public virtual ICollection<Post> 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 the Posts property, which is the ICollection collection of the Post type. And Post, in turn, has a foreign key, BlogID, also known as the blog property (navigation property), which points back to its ancestor Blog.

This data is sufficient for the Code First, based on the convention, to create the following tables in the database:

image

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

Use HasRequired when no foreign key property is specified.



What happens if you do not set the BlogId property in the Post class, but set the navigation property of the Blog. Code First will still create the necessary link, since it knows that the Blog property points back to the Blog entity. It will create the external key Posts.Blog_Id, as shown in Figure 2, and link it to Blog.Id.

image

However, there is one important detail. Blog_Id is nullable. Then it is possible to add Posts that are not related to Blog. So Code First, having studied the class convention, understood it, but this is not exactly what the developer wanted. Apply 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 deploy the configuration by overriding the OnModelCreating method of the DbContext class, as shown below:

 public class BlogContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> 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, which 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 mandatory — in this case, the Blog property.

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


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

Setting up custom foreign key names



Even if you specify a foreign key, it does not always coincide with Code First conventions. By convention, the key name must be the same as the name of the class or “name of the Id class” or “name of the Id class”. That's why Code First was able to work normally with the original class property, BlogId.

But what happens if the name is not according to the convention? For example, “FK” + parent_name_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 another Blog_Id, which will become the foreign key, as it is necessary for connecting 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 - perceive FKBlogId as a foreign key in the relationship to the Blog.

When setting up, you must specify both ends of the relationship — a property in the Blog, pointing to the plural relation (Posts) and the Post property, pointing back to the parent (Blog).

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

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


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

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


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

image

Definition of schema spanning tables and many-to-many relationships



If necessary, you can easily define many-to-many relationships using properties that point to each other. For example, if you add a 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<Post> Posts { get; set; } } 


New property to add to class Post:

 public ICollection<Tag> Tags { get; set; } 


Code first assumes that the name of the link table will consist of a combination of the names of the 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 the link table yourself, it will look like this:

image

Also, if you allow Code first to create the database yourself, then there are usually no problems. But if mapping to an already existing database is in progress, then problems with the names of tables, columns can arise. Use the Fluent API to specify the names of tables and columns.

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

Let's start with the mapping method Entity. 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 specified in the previous example, we will do the same using the HasMany and WithMany methods. We point out that the Post entity has multiple links with the Tags property, and the Tag entity has multiple links with the Posts property:

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


image

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

findings



You have familiarized yourself with some of the possibilities for describing relationships using the Code First fluent API. There are other mappings that can be used alone or in combination. Some of them 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 advantages of using them.

More details about the Entity Framework can be read on MSDN or on the blog of the Entity Framework team.

Source: https://habr.com/ru/post/174709/


All Articles