Entity Framework Code First

flutheronioneyedΛογισμικό & κατασκευή λογ/κού

13 Δεκ 2013 (πριν από 3 χρόνια και 8 μήνες)

86 εμφανίσεις

Entity Framework Code First

After finding out what a client needs
in
an application, one of the first things I would do is design the database. Then as
the project progressed and I was writing code, I would find that I needed to change the database. In
addition to making
the db changes, I would have to change the code in various places that accessed the database. I always thought it would
be great if some of this disconnect between the code an
d the database could be reduced and I could just write code
wi
thout having to open SSMS and change the database schema.

I recently started on a project that used the Entity Framework using the ‘Code First’ approach. In this approach, I didn’t
have to worry about where or how the data was being stored. When I made cha
nges to the code, changes were
propagated to the database.

While some parts of it were very easy and happened automatically, there are some areas that can be troublesome for
newbies. This presentation will be an overview on using ‘Entity Framework Code Fir
st’ and will show you
how you can
define your model objects by writing some pocos. It will also cover
some of the areas to watch out for and lessons I have
learned…sometimes the hard way. In addition to the basics, if time allows, we will
show how the Enti
ty Framework
works seamlessly with MVC and
go into
some more advanced topics.


Creating Code First Demo Project

Create new application library


Add Entity Framework Nuget package



Add an employee class

and make it public


Add a company class

and make
it public


Create a context class derived from dbcontext

and make it public
. You will need to add System.Data.Entity namespace
to class



Add the DbSets for company and employee
.

Use EF package in project by selecting manage nugget packages for solutio
n







Build CodeFirstDemo1

Add reference to the Code First project.

Set the project as the startup project.

Initialize the context and add a record


What will happen when I run the app now? I haven’t done anything to configure a database.

It
successfully runs, so let’s go see what happened and where the data went. In localdb, you can see that a database was
created and the 2 tables added.

It ended up here in localdb because I didn’t set up anything in the config or
programmed against the fluen
tdb api.

Notice that it created the PK from the columns named Id. You could also have used the convention of tablenameid and it
would have used that for the PK.




Notice the configuration history table that was created and look at the record.


Configuring DB Initialization

We can configure both where to look for and create the database as well as the initialization strategy to be used.

S
pecify where you want the database

Specify the name of a database in the context constructor and if it doesn’
t exist in the localdb, it will be created. Edit the
constructor of the Context class to have a new database name.







By default, it will use the localdb if you haven’t specified otherwise in a connections string. If you didn’t want it to crea
te
a dat
abase on localdb, you can create a connection string in app.config specifying where you want it to go and then in
the Context, specify the connection string that should be used when the initializer runs.

To see this this, you can add the following to your
app.config for the console app.


<
connectionStrings
>


<
add

name
=
"
MainConnectionString
"

connectionString
=
"
Data Source=samsung
-
i7
\
sqlExpress2012;Initial
Catalog=TestInitializingWithNamedConnectionsString;Integrated Security=true
"

providerName
=
"
System.Data.SqlClient
"
/>


</
connectionStrings
>

Then in Context.cs, add ‘MainConnectionString’ as a parameter to the base constructor as shown below.


After running the app, you can see that it created a database under my SQLExpress2012 instance


Adding
Command Injection

Before we set an initialization strategy, let’s create a way to see what the EF API is doing under the covers for us. We
need a way to intercept the commands being sent to the database and log them to the console for us to watch.

First, w
e need to add a logging framework. Since I like nLog as a free open source logging provider, I am going to add it
to the project using Nuget.


Add nlog

info to

configuration fle.

Add the interceptor code.

Add the call to start the interceptor to the
sub m
ain and add a breakpoint on that call. Then press F5.


Step through the code to see when something happens at the db level.

When you get to the point sahown in the image below, you should have a number of things logged to the console.


Below is some of
what is displayed on the console.


As you can see, it is checking for the tables being present and is also reading the _migrationHistory table.

Now we’ll change the name of the database
in the initializer
and see what happens.


When it didn’t find the da
tabase, you can see that it went ahead and created it along with the tables.

Initialization Strategies

The first time the program runs, you want it to create the database for you. What do you want it to do on the second
and subsequent times? You have 4 opt
ions that you can specify to determine this behavior. They are:

1.

CreateDatabaseIfNotExists


this is the default. If you change the model with this initializer set, you will get an
exception

2.

DropCreateDatabaseIfModelChanges

3.

DropCreateDatabaseAlways

4.

Custom
DB Initializer


you specify whatever logic you want to use.

Let’s change the model and verify what happens.


As expected, we get an exception.


Set a DBInitializer in the
Context.cs
class to
CreateDatabaseIfNotExists

as shown below.


Now when we run
it,
the database is updated. If you put a break point and look at it, you would see it drop and then
create the database.

I’ll leave it to you to verify that if we set the initializer to
DropCreateDatabaseAlways

that it will drop and recreate the
database
every time, even if the model doesn’t change.

Seeding Your Data

If you choose one of the options that recreates your database, you may want your database to be seeded with some
data prior to running. To do that, you would add the data that you want inser
ted into your database into the Context.cs
class.


Change the
Context.cs to use the DemoI
nitializer


Run it and watch to see that the database gets dropped and recreated and the data gets added.

Choosing an Initialization Strategy

When you are early in a

project and making a lot of changes to your model, you may want to choose one of the options
that includes dropping and recreating the database. Eventually, you will get to a point in the project where you will want
to control if and when the database get
s dropped and recreated.

When that is the case you will want to set the Context to use the CreateDatabaseIfNotExists initializer. In order to see
what happens, change the model and watch it blow up when run.


The error message is telling you that you
need to
do migrations to get the model changes made to the database.

Adding a Migration and Updating the Database

Open the Nuget Package Manager Console

Enter “add
-
migration
” and type the name that you want for the migration. I usually use a long name des
criptive of what
the migration is for. For example, in the above change to the model, I added a DateOfBirth property to the employee
class. I would add a migration by typing something like “add
-
migration AddDateOfBirthToEmployee”.


In this case,
migrations have not yet been enabled for this project, so we need to do as it says and enable migrations.
When we do that, we get another error as shown below.


This confused me the first time I saw it, because I had been doing migrations on a project and

then it stopped working.
Somehow the default project changed from the one I was doing the migrations on. So if you change the default project
to CodeFirstDemoConsole, it will work.


This error tells us that since we want to use explicit migrations tha
t we need to delete the previously created automatic
migrations. So delete the initialcreate migration and rerun the add
-
migration.

Now run an add
-
migration and an update
-
database to get everything back in synch.


The migration created a file that has som
e instructions for both upgrading and downgrading a database. It is cool the
way that you can get your database to any
version of the app that you want. This only takes care of the situation where
you don’t have data. When you have data involved, you will
need to write your own code to transform the data
appropriately.

Now the app should run
.

Rolling Back

At the PM console type in
update
-
database
-
TargetMigration:0

.

We are going to attempt to roll back to the
original database before code migrations were
enabled. As you can see below, this failed because we added data to the
database and it doesn’t want you accidentally blowing away your data.


In this case in order to force the update despite the data loss, we need to add a

Force
parameter.

What will ha
ppen if we run the app now?

We’ll get an exception because the DB does not match our code. In this case, there is an empty db.


If you get frustrated about going around in circles with error messages, you can always delete the db and let it create it
for
you.
Once you have rolled back, you will want to delete any pending migrations. They will automatically get removed
from the _migration history table, but you will need to remove the migration files. Note that you can’t remove the
migration files before
the rollback because the migration file must run to undo the changes it had made to the database.

Following is the new migration file that gets created when I run a new add
-
migration from the PM prompt.


Run an update
-
database to get the database updated.

Looking at the db we see the expected schema and a record in the _
MigrationH
istory



Configure Relationships

(Goto PPT)

One to zero or one

Create a new model class named EmployeeAddress and create a 1 to 0 or 1 relationship
. Add the entity to the
Context.



In the Employee class, add a property for the EmployeeAddress

At PM enter “add
-
migration” and you get the following result. Since we do not have a
property named Id or
EntityNameId, it has no way to determine what should be the primary key.


We can help entity framework out by specifying that the EmployeeId is both the primary key and the foreign key. We
will get more into data annotations in a minute.


At the PM run add
-
migration again and then

when that completes type “update
-
database
. It s
hould complete
successfully.

Look at the database


Configure One to Many Relationship

We’ll create a one to many relationship between a company and their employees. To do this, we’ll add a property to the
Company class hold a list of employees and on the
constructor we will need to initialize that list.



Add
-
migration, update
-
database and then look at db.


Configuring a Many
-
to
-
Many Relationship

Suppose you also wanted to be able to discern when your employees work at multiple companies. In that case,
we will
need a many to many relationship.

Edit the Employee.cs so that
it can hold a collection of Companies and that the list gets initialized in constructor.

In prep, when going from a one
-
to
-
many to a many
-
to
-
many, EF got confused, so I rolled back to a
n earlier point using
update
-
d
ata
b
ase


TargetMigration:, deleted the 2 migrations in between, and then did an add
-
migration and then an
update
-
d
ata
b
ase,

and look at db.





Data Annotations

(Goto PPT)

Validation Attributes:




Annotation
Attribute

Description

Required

The Required annotation will force EF (and MVC) to ensure that property has data in it.

MinLength

MinLength annotation validates property whether it has minimum length of array or string.

MaxLength

MaxLength annotation maximum
length of property which in
-
tern set maximum length of
column in the database

StringLength

Specifies the minimum and maximum length of characters that are allowed in a data field.



Database Schema related Attributes:



Annotation
Attribute

Description

Table

Specify name of the DB table which will be mapped with the class

Column

Specify column name and datatype which will be mapped with the property

Key

Mark property as EntityKey which will be mapped to PK of related table.

ComplexType

Mark the class
as complex type in EF.

Timestamp

Mark the property as a non
-
nullable timestamp column in the database.

ForeignKey

Specify Foreign key property for Navigation property

NotMapped

Specify that property will not be mapped with database

ConcurrencyCheck

ConcurrencyCheck annotation allows you to flag one or more properties to be used for
concurrency checking in the database when a user edits or deletes an entity.

DatabaseGenerated

DatabaseGenerated attribute specifies that property will be mapped to
Computed column of
the database table. So the property will be read
-
only property. It can also be used to map the
property to identity column (auto incremental column).

InverseProperty

InverseProperty is useful when you have multiple relationship between
two classes.


Add a few attributes, add
-
migration and update
-
database


Look at database and note that name is now required and it is set as nvarchar(200)