DotNetStories
In this post I will demonstrate with a hands-on example how
to use the Fluent API to map POCO classes (set
configurations) to SQL Server tables without using the set
of conventions Entity
Framework Code First expects.
I will also give a short introduction to ASP.Net MVC and its main components. I will talk briefly about Entity Framework Code First, Database First and Model First.
Also I will show you how to architecture properly your ASP.Net MVC 4.0 EF Code first application.
We should not have our domain classes, data access classes and views,controllers in the same project.
We should have them in 3 different projects.
That way our code is more extensible,
maintainable and easy to reuse in other non
web applications that might need to consume or use EF Code
first data access layer.We have a clear separation of
concerns.
We can configure our POCO classes with Data Annotations. By using Data Annotations we can configure our domain-entity classes so that they can take best advantage of the EF.We can decorate our entity classes with declarative attributes.
If you want to find out how you can use Data Annotations have a look at this post.
Fluent API supports all the configurations that Data Annotations support and more.Data Annotations support only a subset of the configurations that Fluent API supports.
You can configure your classes not in a declarative way (Data Annotations) but you can create a new class and configure the entity classes through pure C# code and lambda expressions. This is the Fluent API way.Code purists prefer this way since we keep our domain classes without any other declarations.
Before I can go on I will explain that the
EF Code First Model works with conventions.
Through Fluent API, we can configure entities and properties in various ways.We can configure things like
We can also configure things like Inheritance Hierarchies, map an entity to multiple tables and a table to multiple entities.
I will demonstrate most of these configurations.
Let me give you a short introduction to
ASP.Net MVC first. I suggest that you go through some
of the videos and other learning resources from the official
ASP.Net MVC
site.
I will say a few words on how I understand ASP.Net MVC and what its main benefits/design goals are.
Obviously the first paradigm on building web applications on the web is Web Forms.
Web forms was the first and only way to build web application when ASP.Net was introduced to the world, nine years ago.
It replaced the classic ASP model by providing us with a strongly typed code that replaced scripting.We had/have languages that are compiled.Web forms feels like a form that you programmed with VB 6.0 for the desktop.
The main idea was to abstract the WEB. By that I mean HTML is abstracted in a way. Click events replaced "Post" operations.Since that time, web standards have strengthened and client side programming is on the rise. Developers wanted to have more control on the HTML.Web forms , as I said before handles HTML in an abstract way and is not the best paradigm for allowing full control on the HTML rendering.
ASP.Net MVC provide us with a new way of writing ASP.Net applications.It does not replace web forms. It is just an alternative project type.It still runs on ASP.Net and supports caching,sesions and master pages. In ASP.Net MVC applications we have no viewstate or page lifecycle. For more information on understanding the MVC application execution process have a look at this link .It is a highly extensible and testable model.
In order to see what features of ASP.Net are compatible in both Models have a look here.
MVC pattern has been around for decades and it has been used across many technologies as a design pattern to use when building UI. It is based on an architecture model that embraces the so called "seperation of concern pattern".
There are three main building blocks in the MVC pattern. The View talks to the Model. The Model has the data that the View needs to display.The View does not have much logic in them at all.
The Controller orchestrates
everything.When we have an HTTP request coming in,
that request is routed to the Controller. It is up to
the Controller to talk to the
file system,database and build the model.The routing
mechanism in MVC is implemented through the System.Web.Routing assembly.
Routes are defined during application startup.Have a look at
the Global.asax file,when
building an MVC application.
The Controller will select which View to use to display the Model to the client.It is clear that we have now a model that fully supports "separation of concerns".The Controller is responsible for building the Model and selecting the View.
The Controller does not save any data or state. The Model is responsible for that.The Controller selects the View but has nothing to do with displaying data to the client.This is the View's job.
The Controller component is basically a class file that we can write VB.Net or C# code. We do not have Page_Load event handler routines, just simple methods which are easy to test.No code behind files are associated with the Controller classes.All these classes should be under the Controllers folder in your application.Controller type name must end with Controller (e.g ProductController).
In the Views folder we should place the files responsible for displaying content to the client.Create subfolders for every Controller. Shared folder contains views used by multiple controllers.
In this post I will use the Razor View engine rather than the WebForms View. Razor View engine is designed with MVC in mind and it is the way (as far as I am concerned) to work with ASP.Net MVC.
Before we start I will give again a short introduction to
Entity Framework. The stable version of Entity Framework as
we speak is EF 5.0.It is available through Nuget and
it is an open source project.
The .Net framework provides support for Object Relational Mapping through EF. So EF is a an ORM tool and it is now the main data access technology that microsoft works on. I use it quite extensively in my projects. Through EF we have many things out of the box provided for us. We have the automatic generation of SQL code.It maps relational data to strongly types objects.All the changes made to the objects in the memory are persisted in a transactional way back to the data store.
You can search in my blog, because I have posted many posts
regarding ASP.Net and EF.
There are different approaches (paradigms) available using
the Entity
Framework, namely Database First, Code First, Model First.
You can find in this post an example on how to use the Entity Framework to retrieve data from an SQL Server Database using the "Database/Schema First" approach.
In this approach we make all the changes at the database
level and then we update the model with those changes.
In this post you can see an example on how to use the "Model First" approach when working with ASP.Net and the Entity Framework.
This model was firstly introduced in EF version 4.0 and
we could start with a blank model and then create a database
from that model.When we made changes to the model , we could
recreate the database from the new model.
The Code First approach is the more code-centric than the other two. Basically we write POCO classes and then we persist to a database using something called DBContext.
In this application we will us the Code First approach when building our data-centric application
with EF.
Code First relies on DbContext. We create classes with properties and then these classes interact with the DbContext class.Then we can create a new database based upon our POCOS classes and have tables generated from those classes.We do not have an .edmx file in this approach.By using this approach we can write much easier unit tests.
DbContext is a new context class and is smaller,lightweight wrapper for the main context class which is ObjectContext (Schema First and Model First).
I am running VS Studio 2012 Ultimate edition but you can use Visual Studio Express 2012 for Web. You can install Visual Studio Express 2012 for Web if you download Web Platform Installer.You can download this tool from this link.
I will create an ASP.Net MVC application that has two
entities, Department and Courses. They have one to many
relationships between them.
1) I am launching VS 2012 and I will Visual C# as the
programming language. I will also select ASP.NET MVC 4 Web Application from the
available templates. Choose C# as the development
language and Internet Application. I will name my
application FluentAPIMVC. This will be the startup project.
2) Now that we have our MVC project structure we will create another project in our solution. This will be a Windows Library project. I will name it FluentAPIMVC.DomainClasses. In this project I will add only the definitions of the two domain classes Course and Department. I delete the class1.cs.
I create a new class, Department.cs. The code
follows
public class Department { public int DepartmentId { get; set; } public string Name { get; set; } public decimal Budget { get; set; } public DateTime StartDate { get; set; } public virtual ICollection<Course> Courses { get; set; } }
I will add another class, Course.cs
public class Course { public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } public int DepartmentID { get; set; } public virtual Department Department { get; set; } }
These are just two plain POCO classes that know nothing at
all about Entity Framework.
3) I will add another project in this solution. This will be a Windows Library project. I will name it FluentAPIMVC.DomainAccess.
In this class I will add classes that act at the data access layer that will interact with the ASP.Net MVC application and the SQL Server database that will be created.
I will add references to System.Data.Entity
I will add a reference to the FluentAPIMVC.DomainClasses project as well.
Then we need to install Entity Framework 5.0. We will do that through Nuget packages. Make sure you do that.
I will add a class DepartmentDBContext to the project.
The contents of this class are:
public class DepartmentDBContext:DbContext { public DbSet<Department> Departments { get; set; } public DbSet<Course> Courses { get; set; } }
This class inherits from DbContext.
Now that we have the entity classes created, we must let
the model know.I will have to use the DbSet<T> property.
I will add more code to this class later on.
4) We must take care of the connection string. It is very easy to create one in the web.config.It does not matter that we do not have a database yet.When we run the DbContext and query against it , it will use a connection string in the web.config and will create the database based on the classes.I will use the LocalDb
<add name="DepartmentDBContext" connectionString="Data Source=(LocalDb)\v11.0; AttachDbFilename=|DataDirectory|\DepartmentCourses.mdf; Integrated Security=True" providerName="System.Data.SqlClient" />
5) Now we can build our application to make sure everything works ok. Now I want to insert data , seed the database, to the tables.
I will add another class to the FluentAPIMVC.DomainAccess. I name it DepartmentCoursesInsert.
The contents of this class follow
public class DepartmentCoursesInsert: DropCreateDatabaseAlways<DepartmentDBContext> { protected override void Seed(DepartmentDBContext context) { var departments = new List<Department> { new Department { Name = "Physics",Budget=35500, StartDate=DateTime.Parse("12/12/1999"), Courses = new List<Course> { new Course {Title = "Nuclear Physics", Credits= 45}, new Course {Title = "Quantum Physics", Credits= 35} } }, new Department { Name = "Maths",Budget=45500, StartDate=DateTime.Parse("12/12/2009"), Courses = new List<Course> { new Course {Title = "Trigonometry", Credits= 35}, new Course {Title = "Analytic Geometry", Credits= 45} } }, }; departments.ForEach(dep => context.Departments.Add(dep)); base.Seed(context); } }
We need to add entries for the System.Data.Entity and
the FluentAPIMVC.DomainClasses namespace for this
class to compile.
In this class I inherit from the DropCreateDatabaseAlways<DepartmentDBContext>
class and I will override the default behavior of that class with my class.
I will ovverride the Seed method with some data.Then I create 2 instances of the Department entity and 4 entities of the Course entity.
Then through a simple lambda expression I add the data to the database using these last lines of code.
departments.ForEach(dep => context.Departments.Add(dep)); base.Seed(context);
This is just a way to populate initially the database with some data. You could leave this step out and populate the database through the views.
There is another way to populate the database and you should not use DropCreateDatabaseAlways in production databases.
We have Code First Migrations to insert data but I
will not show that here. You can read about
Code First Migration in this
post.
Before the addition of Code First Migrations (4.1,4.2 versions), Code First database initialisation meant that Code First would create the database if it does not exist (the default behaviour - CreateDatabaseIfNotExists).
Another pattern is DropCreateDatabaseAlways which means
that Code First will recreate
the database every time one runs the application.
The other pattern we could use is DropCreateDatabaseIfModelChanges which means that Entity Framework, will drop the database if it realises that model has changes since the last time it created the database.
That is of course fine for the development database but totally unacceptable and catastrophic when you have a production database. We cannot lose our data because of the way that Code First works.
Now we need to make one more change.in the Global.asax.cs file to add a line of code
Database.SetInitializer(new DepartmentCoursesInsert());
We add this line of code to the
Application_Start() method.
I will add references to System.Data.Entity
I will add a reference to the FluentAPIMVC.DomainAccess project as well.
6) Now I will create a new class in the in the FluentAPIMVC.DomainAccess project. I will name it CourseMap.
This is a configuration class that uses Fluent API to configure the type.It defines the table the entity maps to. It defines-matches the columns of the table with the entity properties. Also defines any relationships and things like Primary key e.t.c. Its contents follow.
public class CourseMap : EntityTypeConfiguration<Course> { public CourseMap() { // Primary Key this.HasKey(t => t.CourseID); // Properties this.Property(t => t.CourseID) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(t => t.Title) .IsRequired() .HasMaxLength(70); this.Property(t => t.Credits) .IsRequired(); // Table & Column Mappings this.ToTable("Course"); this.Property(t => t.CourseID).HasColumnName("ID"); this.Property(t => t.Title).HasColumnName("Title"); this.Property(t => t.Credits).HasColumnName("CreditsForCourse"); this.Property(t => t.DepartmentID).HasColumnName("DepID"); // Relationships this.HasRequired(t => t.Department) .WithMany(t => t.Courses) .HasForeignKey(d => d.DepartmentID); } }
In the code above I define the primary key. I define that the primary key as an identity. Then I set that the Title property is non nullable and has a max length of 70. The entity property Credits is required.
Then I define the Table and Column mappings. The entity maps to a table called Course.Then I define the names for the column names.So I can change the entity name properties to different table column names.
Finally I define the one to many relationship.
7) Now I will create a new class in the in the FluentAPIMVC.DomainAccess project. I will name it DepartmentMap.
This is a configuration class that uses Fluent API to configure the type.It defines the table the entity maps to. It defines-matches the columns of the table with the entity properties. Also defines any relationships and things like Primary key e.t.c. Its contents follow.
public class DepartmentMap : EntityTypeConfiguration<Department> { public DepartmentMap() { // Primary Key this.HasKey(t => t.DepartmentId); // Properties this.Property(t => t.DepartmentId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(t => t.Name) .IsRequired() .HasMaxLength(40); this.Property(t => t.Budget) .IsRequired() .HasColumnType("decimal") .HasPrecision(20, 4) ; this.Property(t => t.StartDate) .IsOptional(); // Table & Column Mappings this.ToTable("Department"); this.Property(t => t.DepartmentId).HasColumnOrder(1).HasColumnName("ID"); this.Property(t => t.Name).HasColumnOrder(2).HasColumnName("Dep Name"); this.Property(t => t.Budget).HasColumnOrder(4).HasColumnName("Budget"); this.Property(t=>t.StartDate).HasColumnOrder(3).HasColumnName("Start Date"); } }
In the code above I define the primary key. I define that the primary key as an identity. Then I set that the Name property is non nullable and has a max length of 40. The entity property Budget is required (not null in the respective column) and its datatype will be decimal with a precision of (20,4).
The StartDate entity property is not required (allow
null in the respective column in the table).
Then I define the Table and Column mappings. The entity maps
to a table called Department.Then I define the names
for the column names.So I can change the entity name
properties to different table column names.I also change the
order of the columns.
I am sure you have realised what Fluent API is and
how we can use it to define mappings. I have just
demonstrated some of the most common settings and
configurations used in Fluent API.
8) Now I need to add some more code in the DepartmentDBContext class.
I need to override the OnModelCreating method so I
can give the builder useful information before it goes on
and builds the model.
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new CourseMap()); modelBuilder.Configurations.Add(new DepartmentMap()); }
The complete code for this class follows
public class DepartmentDBContext:DbContext { public DbSet<Department> Departments { get; set; } public DbSet<Course> Courses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new CourseMap()); modelBuilder.Configurations.Add(new DepartmentMap()); } }
9) Now I need to make some changes to the
FluentAPIMVC project.
I will add a reference to the FluentAPIMVC.DomainClasses project.
I need to add a controller. I will name it
DepartmentController
Have a look at the picture below.
Now we need to make a small change to the _Layout.cshtml file. We will add this line of code to add another item in the menu.
<li>@Html.ActionLink("Departments", "Index", "Department")</li>
10) Build and run your application.
Navigate to the your localhost/port/department, in my case (http://localhost:57958/Department).
Have a look at App_Data folder. You will see the
DepartmentCourses.mdf localdb database.Τhis database
was created from EF Code First.
Have a look at the picture below to see the table's
definitions. If we go back to your configuration classes
where we did the initial mapping you will see that all
Fluent API configurations were honored.Have a look at the
column names,types and order. Check which types allow nulls
and which not.
Have a look at the picture below to see the
department page.You can see the seeded data. You can
edit existing departments,create new departments and delete
them.
Hope it helps!!!