In the previous post I created my database from the ADO.NET Data Entity Model. You can find the post here:
Entity framework creating your ADO.NET entity data model first and creating your database from your model
It is also possible to create your database from code first, meaning creating the database from your defined classes. In that case you don’t need to model your entities anymore in your ADO.NET Entity Data Model, but you just create the classes and properties you want to work with.
To be able to work with Entity Framework Code First you need to have Entity Framework 4.1 extension installed, as this update contains the features to work with Code First. You can download Entity Framework 4.1 here:
1. Entity Framework Code First with System.Data.Entity.DbContext
We will create a Console Application called “EntityFramework.CodeFirst“:
Add a reference to System.Data.Entity and a reference to EntityFramework (this is under extensions instead of assemblies).
We will create 2 classes which we will be working with in our solution:
To work with Entity Framework Code First, we need to create a class that derives from System.Data.Entity.DbContext. This class will behave the same as the ObjectContext we are used to work with in Entity Framework, except it will not be working directly on an ADO.NET entity data model in this case.
If we now write some code to create a new blog and save it with the context in the console application:
If you run the client console application and check your ./SQLEXPRESS when it has run, you will find that the database has been created by default:
The database is called “EntityFramework.CodeFirst.Context”, which matches the full namespace + the name of the DbContext object. This means if you would be using 2 classes in your solution deriving from the DbContext, you would be working on 2 different databases.
Our Blogs table looks like this and has the 1 new record added:
The schema of our Post table:
You can see the Entity framework automatically understood the Id is the primary key, since Id is a known keyword for primary key’s. It also automatically put Blog_Id field in there, to make the relation between the Blog and the Posts. The relation in our objects is through the Posts list on the Blog class.
Some of the fields are an nvarchar(MAX), while we would like to restrict them to other lengths. We want to manually say what our primary key is, in case our primary key would not be called “Id”. We also want to define ourselves which fields are nullable and which are not. In this case the entity framework code first allows Title and Content field to be nullable, while for us this is required.
2. Defining schema restrictions and mappings with DataAnnotations and ModelBuilder
We let the schema structure be decided automatically by the Entity Framework Code first, but you can also put annotations on our classes to define how the fields should look like in the database.
Add a reference to System.ComponentModel.DataAnnotations to your project.
Change the Post class like this:
We annotated the Id property as Key, and we mapped the other properties as Required (= not nullable). We also set an annotation of MaxLength on our Title and Content property and added an ErrorMessage in case you try to save a Title that is longer the 50 characters, then this Errormessage will be returned. Note we also added 1 new property called BusinessLogicMethod, which would be a simple method that does some calculation based on some class fields. This property hasn’t got any data, it just does some operation. This property does not match a value, so it does not have to be mapped to our database table. We can avoid the property being mapped by added the NotMapped annotation to it. You can find more possible annotations in the System.ComponentModel.DataAnnotations for other purposes, like column mapping, associations and so forth.
If you delete the database from the previous execution and run the client console application again, the database is gonna get created again and the Posts table will look like this now:
You can see the Title and Content are not nullable anymore and a maxlength has been set to the fields, equal to the maxlength we defined in our annotations.
There is also an alternative to using the Annotations on the classes, if you for example would not have access to the classes. Let’s assume the data classes are provided to use by another class library, to which we do not have access. So adding annotations to the class is not possible.
Make your Post class look like this again:
In your Context class, which derives from the DbContext, you can override the method OnModelCreating, which passes a DbModelBuilder class into the method. Inside of the OnModelCreating method you can use the modelBuilder to set the constraints and configuration like you would have done by adding annotations to the classes directly.
We get the Post Entity and set a key on the Id property. We also get the Title and Content property and set a max length on them and also set them as required properties. Finally we ignore the BusinessLogicMethod by using the modelbuilder.Entity<entity>.Ignore method.
If you remove the current database and execute this again, you will end up with the same Posts table schema as you did with the annotations.
3. Dropping and recreating the database automatically during development
If you run the console application multiple times, you will get exceptions if you changed your entities and your database structure does not match the entities. You have to drop the database and run your code again so the database gets created again, since it didn’t exist yet and matches your entities again.
You can also set this to be done automatically:
You can set some initialisation code with Database.SetInitializer method and saying the database should be dropped and created automatically if the model changes, or whether it should always be dropped and recreated. Please note the dropping and recreating of your database means all the data in the database will be lost. The default initializer is to create the database if it does not exist. If the database already exists and the database schema does not match the schema of your entities anymore, an exception will be thrown.
You can also write your own database initializer by for example overriding the DropCreateDatabaseIfModelChanges on which you can override the Seed method, which allows you to insert test data every time the database is being recreated, which can be useful during development.
The validation whether the entities schema still matches the database schema happens at start up of the application. The validation happens through the EdmMetaData table in your database:
At start up of the application, the DbContext will build an in-memory entity data model of the entities that are exposes through the DbSet in the context. It will create a hash of the model and compare it to the hash that is saved in the EdmMetaData table. If the hash is not equal it does mean the database schema does not match the entity data model and an exception will be thrown, depending on the database initialization that has been set.
If the database has to be created for the first time, this validation obviously does not occur and the EdmMetaData with the hashtag is created. If you decide to work with Code First on a yet existing database that matches your entities, an EdmMetaData table will be created on the first start up that matches the database schema of the application and in future the validation will happen against that hash.
However there will be times that you want to disable this database initialization behavior. If you have a production database, you want to be sure the database can not be dropped and recreated at any time because of the data lose. If you want to disable to database initialisation, you can do it like this:
4. Making use of the ADO.NET DbContext Generator T4 template
When adding the Entity Framework 4.1 update, you will also have a new T4 template added, aka the ADO.NET DbContect Generator template. It allows you to use the DbContext and it’s new features easily with a existing database and using an ADO.NET Entity Data Model.
I have the AdventureWorks database on my local SQL server and I added an ADO.NET Entity Data Model to it with the Product and ProductReview class:
Using the ADO.NET Entity Framework, the classes are being written in the designer of the model and derive from EntityObject by default. However you can also use a T4 template to generate the model classes like normal POCO classes, like the Blog and Post class we used before. All the mumbojambo won’t be present, it will look like a simple class. Right click the model background and click Add Code Generation Item and select the ADO.NET DbContext Generator.
After running the generator you should have something like this:
If you would look to the designer of your model, you will see your designer is empty. If you look at the properties of the Model, you will see the Code Generation Strategy has changed from Default to None:
You can see our Product and ProductReview classes are generated as seperate classes in our template. If we look at how the ProductReview class looks like:
As you can see, the productReview class looks like a simple POCO class. The generated DbContext class will look very similar to the one we created manually:
You see the DbContext creation points to the AdventureWorksEntities connectionstring, which is defined in our app.config when we created the ADO.NET Entity Data Model from our existing AdventureWorks database. You also see that OnModelCreating would throw an exception in case it would be invoked, since we are not using a real Code First scenario anymore.
The client code to test your new context could look like this:
Which would work just fine if you execute it:
You could easily rename a property in your model, which would just change the mapping on your model.
In future I will drill a bit deeper into System.Data.Entity.DbContext because this seems a like a huge improvement.
Any suggestions, remarks or improvements are always welcome.
If you found this information useful, make sure to support me by leaving a comment.
Cheers and have fun,