Beginner Entity Framework Code First-Part2
Published on February 15, 2013
This is a continous post of Beginner Entity Framework Code First. You can find part1 here. In part1, we learned how to create database using entity frame work and how to do one-to-many association between post,comment tables. In Today’s post I will explain how to configure entities using Data annotations and Fluent API.
Configuring Database Table In general when we create database tables in sqlserver management studio we can easily set condition like Not Null,Max length ,etc.. In Entity framework also we can put these condition on data table very easily. We simply call it as Configuring Entities. In Entity Framework we can do Configuration in two ways. 1)Using Data Annotation. 2)Using Fluent API.
Configuring with Data Annotations This is the very simplest method for configuring class and properties. We can apply these configuration very easily to the classed and properties. In order to work with these configuration we need to add using System.ComponentModel.DataAnnotations namespace to your source code. For example if we want to put Not null attribute on PostTitle Property in Post class. Then change your code as
public class Post
{
public int PostId { get; set; }
[Required]
public string PostTitle { get; set; }
public string PostBody { get; set; }
public virtual ICollection<comment> Comments { get; set; }
}
Now run the project(Before run this delete existing database other wise you will get model change exception we will see how to solve it later). You may get following error. If you see the properties window for PostTitle Column it will look like below. Null able became False so you can’t leave that field null. Configuring with the Fluent API Configuring with Data Annotation is very simple but just imagine a class with 20 properties and each propertie has two to three annotations. How unclear code it is. Most of the developer(including me) don’t like this kind of code. That’s where Fluent API come in to picture. We can configure all these properties in OnModelCreating method of DbContext Class. Now to demonstrate how this work we will use same above example, but this time we will use Fluent API to Configure it. Now change the BlogEntites class as below.
public class BlogEntites : DbContext
{
public DbSet<post> Posts { get; set; }
public DbSet<comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<post>()
.Property(p => p.PostTitle)
.IsRequired();
}
}
Here OnModelCreateing is a method which will execute when model is creating for first time. You can remove [Required] annotation we added to PostTitle property inside Post Class. Now if we re run the code the same impact will happen to the PostTitle property which had happen when we use [Required] data annotation. Fixing Model Context Change Error Once the data base as create for first time, After that if we do any change to the model we will get the below error. In this post when we add requird annotation to the PostTitle Property we delete and re created the Blog.mdf manually so that we don’t get the above error(We can use EF5 migrations but it is out of context for this post). Entity Framework provides the same kind of functionality for drop create database. Entity Frame work provides two types of drop creates. 1)DropCreateDatabaseAlways 2)DropCreateDatabaseIfModelChanges 3)CreateDatabaseIfNotExists Most of the time we use DropCreateDatabaseIfModelchanges in your code. Now create asp.net special file _Global.asax_Add the following code in that file.
protected void Application_Start(object sender, EventArgs e)
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BlogEntites>());
}
Now If you do any changes to the model you don’t need to worry about Model Changed error. But one disadvantage is we will loose all the data. Data Seeding For example you have a Country Table with Country name, Country code. You want that data all the time in your data base. If you drop and recreate the database you will look that data. In order to solve that kind of problem you can seed the data when ever database created. Let’s see how to do this. Add one more class to BlogEntites.cs file. As below.
public class BlogDbInitilizer : DropCreateDatabaseIfModelChanges<BlogEntites>
{
protected override void Seed(BlogEntites context)
{
var seedpost = new Post
{
PostTitle = "Seed Post Title 1",
PostBody = "Seed post Body 1",
Comments = new List<Comment>
{
new Comment{CommentAuthor="aravind",CommentBody="my first comment on seed post 1"},
new Comment{CommentAuthor="dimpu",CommentBody="my second Commment on seed post1"}
}
};
context.Posts.Add(seedpost);
context.SaveChanges();
}
}
and Chnage Global.asax as below.
protected void Application_Start(object sender, EventArgs e)
{
Database.SetInitializer(new BlogDbInitilizer());
}
If you run the code and see in side the table data. You can see the seed data inside the database table data. To see the data right click on posts table in server explorer and select show table data. Overview After all above steps your solution should look below. BlogEntites.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace EFDBCreation
{
public class Post
{
public int PostId { get; set; }
public string PostTitle { get; set; }
public string PostBody { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
public string CommentAuthor { get; set; }
public string CommentBody { get; set; }
public virtual Post Post { get; set; }
}
public class BlogEntites : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.Property(p => p.PostTitle)
.IsRequired().HasMaxLength(120);
}
}
public class BlogDbInitilizer : DropCreateDatabaseIfModelChanges<BlogEntites>
{
protected override void Seed(BlogEntites context)
{
var seedpost = new Post
{
PostTitle = "Seed Post Title 1",
PostBody = "Seed post Body 1",
Comments = new List<Comment>
{
new Comment{CommentAuthor="aravind",CommentBody="my first comment on seed post 1"},
new Comment{CommentAuthor="dimpu",CommentBody="my second Commment on seed post1"}
}
};
context.Posts.Add(seedpost);
context.SaveChanges();
}
}
}
default.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace EFDBCreation
{
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var post1 = new Post
{
PostTitle = "Post Title 1",
PostBody = "post Body 1",
Comments = new List<Comment>
{
new Comment{CommentAuthor="aravind",CommentBody="my first comment on post 1"},
new Comment{CommentAuthor="dimpu",CommentBody="my second Commment on post1"}
}
};
using (BlogEntites be = new BlogEntites())
{
be.Posts.Add(post1);//post1 added to memory
be.SaveChanges();// post1 added to DB
}
}
}
}
Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Data.Entity;
namespace EFDBCreation
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Database.SetInitializer(new BlogDbInitilizer());
}
}
}
web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="BlogEntites" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=Blog;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Blog.mdf" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
</configuration>