Entity Framework
Entity Framework
Entity Framework
As per the above figure, EF creates data access classes for your existing
database, so that you can use these classes to interact with the database
instead of ADO.Net directly.
EF can also create the database from your domain classes, thus you can
focus on your domain-driven design.
EF provides you a model designer where you can design your DB model and
then EF creates database and classes based on your DB model.
What is O/RM?
ORM is a tool for storing data from domain objects to relational database like
MS SQL Server, in an automated way, without much programming. O/RM
includes three main parts: Domain class objects, Relational database objects
and Mapping information on how domain objects map to relational database
objects (tables, views & storedprocedures). ORM allows us to keep our
database design separate from our domain class design. This makes the
application maintainable and extendable. It also automates standard CRUD
operation (Create, Read, Update & Delete) so that the developer doesn't
need to write it manually.
A typical ORM tool generates classes for the database interaction for your
application as shown below.
There are many ORM frameworks for .net in the market such as
DataObjects.Net, NHibernate, OpenAccess, SubSonic etc. Entity Framework
is an open source ORM framework from Microsoft.
EDM (Entity Data Model): EDM consists of three main parts - Conceptual
model, Mapping and Storage model.
Conceptual Model: The conceptual model contains the model classes and
their relationships. This will be independent from your database table design.
Storage Model: Storage model is the database design model which includes
tables, views, stored procedures, and their relationships and keys.
Mapping: Mapping consists of information about how the conceptual model
is mapped to the storage model.
For the basic tutorials, we will use EF 6.0, the latest version of entity
framework as of this writing.
Install the following tools to work with entity framework:
This will open Manage NuGet packages dialogue box. Now, select Online in
left bar and search for EntityFramework as shown below.
This will search for all the packages related to Entity Framework. Select
EntityFramework and click on Install.
Click on the I Accept button in License Acceptance dialogue box. This will
start the installation.
appropriate
version
of
You can see in the above diagram that the sample SchoolDB database
includes tables with the following relationships, for demo purpose.
Let's create first simple Entity Data Model for sample School database in the
next section.
Here, we are going to create an Entity Data Model (EDM) for SchoolDB
database and understand the basic building blocks.
Entity Data Model is a model that describes entities and the relationships
between them. Let's create first simple EDM for SchoolDB database using
Visual Studio 2012 and Entity Framework 6.
1. Open Visual Studio 2012 and create a console project.
2. Now, add Entity Data Model by right clicking on the project in the solution
explorer -> Add -> click New Item and select ADO.NET Entity Data Model
from popup, Give the new item the name 'School' and click Add button.
3. Entity Data Model Wizard in VS2012 opens with four options to select
from: EF Designer from database for database first approach, Empty EF
Designer
model for
model
first
approach, Empty
Code
First
model and Code First from database for Code-First approach. We will
focus on the database-first approach in the basic tutorials so select EF
Designer from database option and clickNext.
5. In this step, you need to choose the version of Entity Framework. We will
use Entity Framework 6.0 in the basic tutorials so select Entity Framework
6.0 and click Next.
Note: If you have already installed the latest version of Entity Framework
using NuGet manager as shown in the Setup Environment section then this
step of the wizard will no longer appear since you have already installed
Entity Framework.
6. This step will display all the Tables, Views and Stored Procedures (SP) in
the database. Select the Tables, Views and SPs you want, keep the default
checkboxes selected and click Finish. You can change Model Namespace if
you want.
Note:
EDM also adds a connection string in the config file as shown below.
<?xml version="1.0"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit
http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,
EntityFramework, Version=6.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
</configSections>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5"/>
</startup>
<entityFramework>
<defaultConnectionFactory
type="System.Data.Entity.Infrastructure.SqlConnectionFactory,
EntityFramework"/>
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices,
EntityFramework.SqlServer"/>
</providers>
</entityFramework>
<connectionStrings>
<add name="SchoolDBEntities"
connectionString="metadata=res://*/SchoolDB.csdl|
res://*/SchoolDB.ssdl|
res://*/SchoolDB.msl;provider=System.Data.SqlClient;provider
connection string="data source=.\sqlexpress;initial
catalog=SchoolDB;integrated
security=True;multipleactiveresultsets=True;application
name=EntityFramework""
providerName="System.Data.EntityClient"/>
</connectionStrings>
</configuration>
In this way, you can create a simple EDM from your existing database.
Now, let's examine all the building blocks of generated EDM (School.edmx)
as shown in the above figure.
Entity-Table Mapping:
Each entity in EDM is mapped with the database table. You can check the
entity-table mapping by right clicking on any entity in the EDM designer ->
select Table Mapping. Also, if you change any property name of the entity
from designer then the table mapping would reflect that change
automatically.
Visual Studio cannot display the model in Design view and in XML format at
the same time, so you will see a message asking whether its OK to close the
Design view of the model. Click Yes. This will open the XML format view. You
can see the following XML view by toggling all outlining as shown below.
You can see SSDL content, CSDL content and C-S mapping content here. If
you expand SSDL and CSDL, each one has some common XML node under
each schema node. You don't need to edit the xml data because this can be
accomplished easier in the Model Browser.
Model Browser:
We have created our first Entity Data Model for School database in the
previous section. The visual designer of EDM does not display all the objects
it creats. It only display entities which are mapped to the database tables
and views.
Model Browser gives you all the information about all the objects and
functions EDM has created. Open Model Browser by right clicking on the EDM
Designer and select Model Browser from the context menu.
Model browser contains all the information about the EDM, its conceptual
model, storage model and mapping information as shown below.
As you can see in the above figure, model browser contains the following
objects:
Diagrams: Model browser contains visual diagrams of EDM. We have seen a
default visual diagram created by EDM. You can also create multiple
diagrams for one EDM if your application has a large number of entities.
Entity Types: Entity types lists all the class types which are mapped to the
database tables.
Complex Types: Complex types are the classes which are generated by
EDM to contain the result of stored procedures, table-valued function etc.
These complex types are customized classes for different purposes.
Enum Types: Enum types lists all the entities which are used as Enum in
entity framework.
Associations: Associations lists all foreign key relationship between the
entity types.
Function Imports: Function imports lists all the functions which will be
mapped to stored procedures, table-valued functions, etc. Stored procedures
and table-valued functions will be used as functions and not an entity in EF.
.Store: Store represents database schema (SSDL).
DBContext:
As you have seen in the previous Create Entity Data Model section, EDM
generates the SchoolDBEntities class, which was
derived from
the System.Data.Entity.DbContext class, as shown below. The class that
derives DbContext is called context class in entity framework.
DbContext is the primary class that is responsible for interacting with data as
object. DbContext is responsible for the following activities:
EntitySet: DbContext contains entity set (DbSet<TEntity>) for all the entities which is
mapped to DB tables.
Querying: DbContext converts LINQ-to-Entities queries to SQL query and send it to the
database.
Change Tracking: It keeps track of changes that occurred in the entities after it has been
querying from the database.
Persisting Data: It also performs the Insert, Update and Delete operations to the
database, based on what the entity states.
Caching: DbContext does first level caching by default. It stores the entities which have
been retrieved during the life time of a context class.
Manage Relationship: DbContext also manages relationship using CSDL, MSL and
SSDL in DB-First or Model-First approach or using fluent API in Code-First approach.
Object Materialization: DbContext converts raw table data into entity objects.
namespace EFTutorials
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
public partial class SchoolDBEntities : DbContext
{
public SchoolDBEntities()
: base("name=SchoolDBEntities")
{
}
return
((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("sp_DeleteS
tudent", studentIdParameter);
}
public virtual ObjectResult<Nullable<decimal>>
sp_InsertStudentInfo(Nullable<int> standardId, string studentName)
{
var standardIdParameter = standardId.HasValue ?
new ObjectParameter("StandardId", standardId) :
new ("StandardId", typeof(int));
var studentNameParameter = studentName != null ?
new ObjectParameter("StudentName", studentName) :
new ObjectParameter("StudentName", typeof(string));
return
((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Nullable<d
ecimal>>("sp_InsertStudentInfo", standardIdParameter,
studentNameParameter);
}
public virtual int sp_UpdateStudent(Nullable<int> studentId,
Nullable<int> standardId, string studentName)
{
var studentIdParameter = studentId.HasValue ?
new ObjectParameter("StudentId", studentId) :
new ObjectParameter("StudentId", typeof(int));
var standardIdParameter = standardId.HasValue ?
new ObjectParameter("StandardId", standardId) :
new ObjectParameter("StandardId", typeof(int));
var studentNameParameter = studentName != null ?
new ObjectParameter("StudentName", studentName) :
Instantiating DbContext:
You can use DbContext by instantiating context class and use for CRUD
operation as shown below.
Note: By default dynamic proxy is enabled for every entity. However, you
can disable dynamic proxy by setting the ProxyCreationEnabled option to
false in context class.
context.Configuration.ProxyCreationEnabled = false;
EDM generates POCO entities which satisfy the above requirements for a
dynamic proxy by default.
At
runtime,
type
of
Student
System.Data.Entity.DynamicProxies.Student as below:
will
be
Entity can have two types of properties, Scalar and Navigation properties.
Scalar properties:
Scalar properties are properties whose actual values are contained in the
entity. For example, Student entity has scalar properties like StudentId and
StudentName. These correspond with the Student table columns.
Navigation properties:
Navigation properties are pointers to other related entities. The Student has
Standard property as a navigation property that will enable the application to
navigate from a Student to related Standard entity.
Entity Relationships:
Here, you will learn how entity framework manages the relationships
between entities.
Entity framework supports three types of relationships, same as database: 1)
One to One 2) One to Many, and 3) Many to Many.
We have created an Entity Data Model for the SchoolDB database in
the Create Entity Data Modelsection. The following figure shows the visual
designer for that EDM with all the entities and relationships among them.
One-to-One Relationship:
As you can see in the above figure, Student and StudentAddress have a Oneto-One relationship (zero or one). A student can have only one or zero
address. Entity framework adds Student navigation property into
StudentAddress entity and StudentAddress navigation entity into Student
entity. Also, StudentAddress entity has StudentId property as PrimaryKey
which makes it a One-to-One relationship.
The following code snippet shows Student and StudentAddress entity classes.
{
this.Courses = new HashSet<Course>();
}
public int StudentID { get; set; }
public string StudentName { get; set; }
public Nullable<int> StandardId { get; set; }
public byte[] RowVersion { get; set; }
public virtual Standard Standard { get; set; }
public virtual StudentAddress StudentAddress { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public partial class StudentAddress
{
public int StudentID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public virtual Student Student { get; set; }
}
As you can see in the above code, Student entity class includes
StudentAddress navigation property and StudentAddress includes Student
navigation property with foreign key property StudentId. This way EF handles
one-to-one relationship between entities.
One-to-Many Relationship:
The Standard and Teacher entities have a One-to-Many relationship marked
by multiplicity where 1 is for One and * is for many. This means that
Standard can have many Teachers whereas Teacher can associate with only
one Standard.
To represent this, The Standard entity has the collection navigation
property Teachers (please notice that it's plural), which indicates that one
Standard can have a collection of Teachers (many teachers). And Teacher
entity has a Standard navigation property (Not a Collection) which indicates
that Teacher is associated with one Standard. Also, it contains StandardId
foreign key (StandardId is a PK in Standard entity). This makes it One-toMany relationship.
The following code snippet shows Standard and Teacher entity class created
by EDM.
}
public int TeacherId { get; set; }
public string TeacherName { get; set; }
public Nullable<int> StandardId { get; set; }
public Nullable<int> TeacherType { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual Standard Standard { get; set; }
}
As you can see in the above code snippet, Standard entity class has Teachers
property of type ICollection, so that it can contain multiple Teacher objects.
(It initializes Teachers property with HashSet<Teacher> in the constructor, so
that you can add Teacher objects into collection without worrying about
initializing it.)
Also, Teacher entity class includes Standard property with StandardId for
foreign key property. Entity framework includes this foreign key property
because we checked Include foreign key columns in the model in the
EDM wizard while creating EDM in the Create Entity Data Model section.
Many-to-Many Relationship:
Student and Course have Many-to-Many relationships marked by *
multiplicity. It means one Student can enrol for many Courses and also, one
Course can be be taught to many Students.
The database design includes StudentCourse joining table which includes the
primary key of both the tables (Student and Course table). Entity Framework
represents many-to-many relationships by not having entityset for the
joining table in CSDL, instead it manages this through mapping.
As you can see in the above figure, Student entity includes Courses property
and Course entity includes Students property to represent many-to-many
relationship between them.
The following code snippet shows Student and Course entity classes.
Entity Graph:
When an entity has a relationship with other entities, then the full object
hierarchy is called an 'entity graph'. For example the following is a Student
entity graph, that includes hierarchy of Student entity with Standard,
StudentAddress & Course entities.
Entity Lifecycle:
Before we work on CRUD operation (Create, Read, Update, Delete), it's
important to understand the entity lifecycle and how it is being managed by
the EntityFramework.
During an entity's lifetime, each entity has an entity state based on the
operation performed on it via the context (DbContext). The entity state is an
enum of type System.Data.Entity.EntityState that includes the following
values:
1. Added
2. Deleted
3. Modified
4. Unchanged
5. Detached
The Context not only holds the reference to all the objects retrieved from the
database but also it holds the entity states and maintains modifications made
to the properties of the entity. This feature is known as Change Tracking.
The change in entity state from the Unchanged to the Modified state is the
only state that's automatically handled by the context. All other changes
must be made explicitly using proper methods of DbContext and DbSet.
The following figure illustrates how the operation performed on entity
changes its' states which, in turn, affects database operation.
As you can see in the above figure, new entity in context has Added entity
state. So the context will execute insert command to the database. In the
same way, when you retrieve an existing entity using L2E queries, it will have
Unchanged state, this is because you have just retrieved an entity and hasn't
performed any operation on it yet. When you modify values of existing entity,
it changes its state to Modified which in turn will execute update command
on SaveChanges. Deleted entity from context will have Deleted state which in
turn will execute delete command to the database.
So, in this way, operations performed on entities changes states. Context
builds and executes database commands based on the state of an entity.
Code First:
In the Code First approach, you avoid working with visual model designer
(EDMX) completely. You write your POCO classes first and then create
database from these POCO classes.
Developers who follow the path of Domain-Driven Design (DDD) principles,
prefer to begin by coding their domain classes first and then generating the
database required to persist their data.
In the Add Entity dialogue, enter name of the entity. Also you can change the
default settings for Entity set and key property. We will keep the default
settings as it is. Click OK to generate an entity.
You can also add properties in the generated entity by right clicking on entity
- > Add New -> Scalar property.
In the same way, you can add other entities and associations using toolbox.
This will open Generate Database Wizard. You can select existing database
or create a new connection by clicking on New Connection.. Select
database server and enter the name of the database to create and then click
OK. It will ask you for the confirmation for creating a new database.
Click Yes to create a database.
This will add ModelName.edmx.sql file in the project. You can execute DDL
scripts in Visual Studio by opening .sql file -> right click -> Execute.
Now, you can generate context class and entities by right clicking on the
designer and selecting Add Code Generation Item..
So, in this way, you can design your DB model and then generate a database
and classes based on the model. This is called the Model-First approach.
Entity Data Model can be updated whenever database schema changes. Also,
database-first approach supports stored procedure, view, etc.
These basic tutorials cover the Database-first approach.
As per the above figure, if you already have an existing application with
domain classes, then you can use the code-first approach because you can
create a database from your existing classes in this approach. If you have an
existing database, then you can create an EDM from an existing database in
the database-first approach. If you do not have an existing database or
domain classes, and you prefer to design your DB model on the visual
designer, then go for Model-first approach.
LINQ to Entities:
Language-Integrated Query (LINQ) is a powerful query language introduced
in Visual Studio 2008. You can use LINQ in C# or Visual Basic to query
different data sources. LINQ-to-Entities operates on entity framework entities
to access the data from the underlying database. You can use LINQ method
syntax or query syntax when querying with EDM. Visit LINQ Tutorials to learn
LINQ step-by-step.
LINQ Method syntax:
Entity SQL:
Entity SQL is another way to create a query. It is processed by the Entity
Frameworks Object Services directly. It returns ObjectQuery instead of
IQueryable.
You need ObjectContext to create a query using Entity SQL.
The following code snippet shows the same query result as L2E query above.
You can also use EntityConnection and EntityCommand to execute Entity SQL
as shown below:
Native SQL:
You can execute native SQL queries for a relational database as shown
below:
First/FirstOrDefault:
If you want to get a single student object, when there are many students,
whose name is "Student1" in the database, then use First or FirstOrDefault,
as shown below:
The difference between First and FirstOrDefault is that First() will throw an
exception if there is no result data for the supplied criteria whereas
FirstOrDefault() returns default value (null) if there is no result data.
Single/SingleOrDefault:
You can also use Single or SingleOrDefault to get a single student object as
shown below:
ToList:
If you want to list all the students whose name is 'Student1' (provided there
are many students has same name) then use ToList():
{
var studentList = (from s in ctx.Students
where s.StudentName == "Student1"
select s).ToList<Student>();
}
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Student1' = [Extent1].[StudentName]
go
GroupBy:
If you want to group students by standardId, then use groupby:
SELECT
[Project2].[C1] AS [C1],
[Project2].[StandardId] AS [StandardId],
[Project2].[C2] AS [C2],
[Project2].[StudentID] AS [StudentID],
[Project2].[StudentName] AS [StudentName],
[Project2].[StandardId1] AS [StandardId1]
FROM ( SELECT
[Distinct1].[StandardId] AS [StandardId],
1 AS [C1],
[Extent2].[StudentID] AS [StudentID],
[Extent2].[StudentName] AS [StudentName],
[Extent2].[StandardId] AS [StandardId1],
CASE WHEN ([Extent2].[StudentID] IS NULL) THEN CAST(NULL AS int)
ELSE 1 END AS [C2]
FROM (SELECT DISTINCT
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1] ) AS [Distinct1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON ([Distinct1].
[StandardId] = [Extent2].[StandardId]) OR (([Distinct1].[StandardId] IS
NULL) AND ([Extent2].[StandardId] IS NULL))
) AS [Project2]
ORDER BY [Project2].[StandardId] ASC, [Project2].[C2] ASC
go
OrderBy:
If you want to get the list of students sorted by StudentName, then use
OrderBy:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
ORDER BY [Extent1].[StudentName] ASC
go
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent2].[City] AS [City]
FROM [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[StudentAddress] AS [Extent2] ON [Extent1].
[StudentID] = [Extent2].[StudentID]
WHERE 1 = [Extent1].[StandardId]
go
The projectionResult in the above query will be the anonymous type, because
there is no class/entity which has these properties. So, the compiler will
mark it as anonymous.
Nested queries:
You can also execute nested LINQ to entity queries as shown below:
The nested query shown above will result in an anonymous list with a
StudentName and Course object.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Join1].[CourseId1] AS [CourseId],
[Join1].[CourseName] AS [CourseName],
[Join1].[Location] AS [Location],
[Join1].[TeacherId] AS [TeacherId]
FROM [dbo].[Student] AS [Extent1]
INNER JOIN (SELECT [Extent2].[StudentId] AS [StudentId], [Extent3].
[CourseId] AS [CourseId1], [Extent3].[CourseName] AS [CourseName],
[Extent3].[Location] AS [Location], [Extent3].[TeacherId] AS [TeacherId]
FROM [dbo].[StudentCourse] AS [Extent2]
INNER JOIN [dbo].[Course] AS [Extent3] ON [Extent3].[CourseId] =
[Extent2].[CourseId] ) AS [Join1] ON [Extent1].[StudentID] = [Join1].
[StudentId]
WHERE 1 = [Extent1].[StandardId]
go
In this way, you can do a projection of the result, in the way that you would
like the data to be.
DBSet Class
DBSet class represents an entity set that is used for create, read, update,
and delete operations. A generic version of DBSet (DbSet<TEntity>) can be
used when the type of entity is not known at build time.
You can get the reference of DBSet by using DBContext, eg.
dbcontext.Students, dbcontext.Standards, or any other entity set. DbContext
class includes DbSet as shown below:
Some of the important methods of DBSet class are shown in the table
below::
Method Name
Return Type
Description
Add
Added entity
type
Adds the given entity to the context the Added state. When
changes are being saved, the entities in the Added states a
inserted into the database. After the changes are saved, th
object state changes to Unchanged.
Example:
dbcontext.Students.Add(studentEntity)
AsNoTracking<En DBQuery<Enti Returns a new query where the entities returned will not be
tity>
ty>
cached in the DbContext. (Inherited from DbQuery.)
Example:
var studentList =
dbcontext.Students.AsNoTracking<Student>().ToList<Stude
;
Attach(Entity)
Entity which
Attaches the given entity to the context in the Unchanged s
was passed as
parameter
Example:
dbcontext.Students.Attach(studentEntity);
Create
Entity
Find(int)
Entity type
Method Name
Return Type
Description
Example:
Student studEntity = dbcontext.Students.Find(1);
Include
DBQuery
Example:
var studentList =
dbcontext.Students.Include("StudentAddress").ToList<Stud
;
var studentList = dbcontext.Students.Include(s =>
s.StudentAddress).ToList<Student>();
Remove
Removed
entity
SqlQuery
DBSqlQuery
Creates a raw SQL query that will return entities in this set.
default, the entities returned are tracked by the context; th
be changed by calling AsNoTracking on theDbSqlQuery<TE
returned from this method.
Example:
var studentEntity = dbcontext.Students.SqlQuery("select *
student where studentid = 1").FirstOrDefault<Student>();
DBEntityEntry Class
DBEntityEntry is an important class, which is useful in retrieving various
information about an entity. You can get an instance of DBEntityEntry of a
particular entity by using Entry method of DBContext. For example:
DBEntityEntry studentEntry = dbcontext.Entry(StudentEntity);
DBEntityEntry enables you to access entity state, current, and original values
of all the property of a given entity. The following example code shows how
to retrieve important information of a particular entity.
Console.WriteLine("
}
}
Output:
Entity Name: Student
Entity State: Modified
********Property Values********
Property Name: StudentID
Original Value: 1
Current Value: 1
Property Name: StudentName
Original Value: First Student Name
Current Value: Edited name
Property Name: StandardId
Original Value:
Current Value:
Description
Collection
DBCollectionEntry
Example:
var studentDBEntityEntry = dbContext.Entry(studentEn
var collectionProperty =
studentDBEntityEntry.Collection<course>(s => s.Cours
ComplexProper
ty
DBComplexProperty
Entry
GetDatabaseVa DBPropertyValues
lues
Property
DBPropertyEntry
Reference
DBReferenceEntry
Gets an object that represents the reference (i.e. noncollection) navigation property from this entity to anoth
entity.
Example:
var studentDBEntityEntry = dbContext.Entry(studentEn
var referenceProperty = studentDBEntityEntry.Referenc
=> s.Standard);
Reload
void
Output:
Find Student
Context tracking changes of 1 entity.
--------------------------------------Find Standard
Context tracking changes of 2 entities.
Editing Standard
--------------------------------------Remove Student
---------------------------------------
As you can see in the above sample code snippet and output, context keeps
track of entities whenever we retrieve, add, modify or delete any entity.
Please notice that context is alive during any of the operations on entities.
Context will not keep track if you do any operation on entities out of its
scope.
As per the above scenario, Context1 is used for read operation and then
Context1 is destroyed. Once entities change, the application submits entities
using Context2 - a different context object.
Disconnected scenario is complex because the new context doesn't know
anything about the modified entity so you will have to instruct the context of
what has changed in the entity. In the figure below, the application retrieves
an entity graph using Context 1 and then the application performs some CUD
(Create, Update, Delete) operations on it and finally, it saves the entity graph
using Context 2. Context 2 doesn't know what operation has been performed
on the entity graph in this scenario.
context.SaveChanges();
}
Disconnected Entities:
Before we see how to perform CRUD operation on disconnected entity graph,
let's see how to associate disconnected entity graph with the new context
instance.
There are two things we need to do when we get a disconnected entity graph
or even a single disconnected entity. First, we need to attach entities with the
new context instance and make context aware about these entities. Second,
set appropriate EntityStates to these entities manually because the new
context instance doesn't know anything about the operations performed on
the disconnected entities, so the new context cannot apply the appropriate
EntityState.
The following figure illustrates this process.
DbSet.Add():
DbSet.Add() method attaches the entire entity graph to the new context and
automatically applies Added entity state to all the entities.
Consider the following example code.
DbSet.Attach():
DbSet.Attach method attaches a whole entity graph to the new context with
Unchanged entity state.
Consider the following example code.
Console.WriteLine("StudentAddress EntityState:
{0}",addressEntry.State);
}
Output:
Student EntityState: Unchanged
StudentAddress EntityState: Unchanged
As per the above code, we can attach disconnected entity graph using
DbSet.Attach method. This will attach the entire entity graph to the new
context with Unchanged entity state to all entities.
Thus, Attach method will only attach entity graph to the context, so we need
to find the appropriate entity state for each entity and apply it manually.
DbContext.Entry():
Entry method of DbContext returns DbEntityEntry instance for a specified
entity. DbEntityEntry can be used to change the state of an entity.
DbContext.Entry(disconnectedEntity).state = EntityState.Added/Modified/Deleted/Unchanged
This method attaches a whole entity graph to the context with specified state
to the parent entity and set the state of other entities, as shown in the
following table.
Parent Entity State
Added
Added
Modified
Unchanged
Deleted
using System;
using System.Collections.Generic;
public partial class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentID { get; set; }
public string StudentName { get; set; }
public Nullable<int> StandardId { get; set; }
public byte[] RowVersion { get; set; }
public virtual Standard Standard { get; set; }
public virtual StudentAddress StudentAddress { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
public partial class SchoolDBEntities : DbContext
{
public SchoolDBEntities()
: base("name=SchoolDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder
modelBuilder)
{
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Standard> Standards { get; set; }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<StudentAddress> StudentAddresses { get; set; }
public virtual DbSet<Teacher> Teachers { get; set; }
}
class Program
{
As you can see in the above code snippet, first, we have created a new
Student entity object and set StudentName to 'Bill'. Second, we have created
a new DBContext object and added newStudent into Students EntitySet.
Third, we called SaveChanges method of DBContext which will execute the
following insert query to the database.
class Program
{
static void Main(string[] args)
{
// create new Student entity object in disconnected scenario (out of
the scope of DbContext)
var newStudent = new Student();
//set student name
newStudent.StudentName = "Bill";
//create DBContext object
using (var dbCtx = new SchoolDBEntities())
{
//Add newStudent entity into DbEntityEntry and mark EntityState
to Added
dbCtx.Entry(newStudent).State =
System.Data.Entity.EntityState.Added;
// call SaveChanges method to save new Student into database
dbCtx.SaveChanges();
}
}
}
So, in this way, you can add a new single entity in the disconnected scenario.
In the next chapter, you will learn how to update an existing single entity in
the disconnected scenario.
using System;
using System.Collections.Generic;
public partial class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentID { get; set; }
public string StudentName { get; set; }
public Nullable<int> StandardId { get; set; }
public byte[] RowVersion { get; set; }
public virtual Standard Standard { get; set; }
public virtual StudentAddress StudentAddress { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
public partial class SchoolDBEntities : DbContext
{
public SchoolDBEntities()
: base("name=SchoolDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder
modelBuilder)
{
}
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Standard> Standards { get; set; }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<StudentAddress> StudentAddresses { get; set; }
public virtual DbSet<Teacher> Teachers { get; set; }
}
Student stud;
//1. Get student from DB
using (var ctx = new SchoolDBEntities())
{
As you see in the above code snippet, we are doing the following steps:
1. Get the existing student from DB.
2. Change the student name out of Context scope (disconnected mode)
3. Pass the modified entity into the Entry method to get its DBEntityEntry
object and then mark its state as Modified
4. Call SaveChanges() method to update student information into the
database.
SaveChanges will send the following update query to the database:
In this way, we can easily update a single entity using DBContext in the
disconnected mode.
DbContext.Entry method returns an instance of DBEntityEntry for a specified
Entity. An instance of DbEntityEntry class providea access to information
about a given entity and its state. You can change the state of an entity to
Added, Updated, or Deleted.
In the next chapter you will learn how to delete a single entity in the
disconnected mode.
Student studentToDelete;
//1. Get student from DB
using (var ctx = new SchoolDBEntities())
{
studentToDelete = ctx.Students.Where(s => s.StudentName ==
"Student1").FirstOrDefault<Student>();
}
//Create new context for disconnected scenario
using (var newContext = new SchoolDBEntities())
{
newContext.Entry(studentToDelete).State =
System.Data.Entity.EntityState.Deleted;
newContext.SaveChanges();
}
The code shown above results in the following delete query which deletes the
row from Teacher table.
delete [dbo].[Student]
where ([StudentId] = @0)',N'@0 int',@0=1
Thus, there is no difference when you add a single entity or entity graph in
disconnected or connected scenario. Make sure that all the entities are new.
Learn how to update entity graph in disconnected scenario in the next
section.
We need to identify the states of each entity in the entity graph, before
calling the SaveChages() method of context. There are different patterns to
identify entity state, which we need to consider in designing data layer with
Entity Framework.
As you can see in the figure shown above, the client fetches the Standard
and Teacher entity graph and does some operations on it and then sends it to
Context 2 to save the changes.
In the disconnected scenario, context2 doesn't know the state of each entity.
It has to determine the state of the Standard entity by using StandardId and
the state of the Teacher entity by using TeacherId property. If StandardId
and TeacherID value is zero, that means it is a new entity and if it is not zero
then it is a modified entity.
In the above figure, Standard, Teacher 1, and Teacher 2 have a non-zero Id
property value so they are marked as Modified and Teacher 3 Id is zero so it
is marked as Added.
The code snippet shown below uses Id property to mark an entity state:
{
context.Configuration.ProxyCreationEnabled = false;
disconnectedStandard = context.Standards.Where(s => s.StandardId
== 58).Include(s => s.Teachers).FirstOrDefault<Standard>();
}
//Update Standard in disconnected mode
disconnectedStandard.StandardName = "Edited Standard Name";
//Update teachers collection by editing first teacher and adding new
teacher
disconnectedStandard.Teachers.ElementAt(0).TeacherName = "Edited
Teacher Name";
disconnectedStandard.Teachers.Add(new Teacher() { TeacherName =
"New Teacher", StandardId = disconnectedStandard.StandardId });
using (var newContext = new SchoolDBEntities())
{
//mark standard based on StandardId
newContext.Entry(disconnectedStandard).State =
disconnectedStandard.StandardId == 0 ? EntityState.Added :
EntityState.Modified;
//mark teacher based on StandardId
foreach (Teacher tchr in disconnectedStandard.Teachers)
newContext.Entry(tchr).State = tchr.TeacherId == 0 ?
EntityState.Added : EntityState.Modified;
newContext.SaveChanges();
}
Advantages:
Good performance.
Disadvantages:
Every entity type needs to have an Id property. It cannot determine states of an entity that
do not have Id property.
Cannot identify an Unchanged entity. It is set to Modified state even if the entity is not
changed at all. So, there is an unnecessary database update statement for an
unchanged entity.
interface IEntityObjectState
{
EntityObjectState ObjectState { get; set; }
}
public enum EntityObjectState
{
Added,
Modified,
Deleted,
Unchanged
}
newContext.Entry(tchr).State =
System.Data.Entity.EntityState.Unchanged;
}
//save changes
newContext.SaveChanges();
}
Advantages:
Disadvantage:
Need to set the appropriate states of each entity in disconnected mode. So there is a
need to be extra careful in disconnected mode.
EF will now include a RowVersion column in the where clause, whenever you
do an update operation and if the rowversion value is different than in the
where clause then it will throwDbUpdateConcurrencyExection.
The following code shows that User1 and User2 get the same student and
update StudentName at the same time:
User1 saves his changes before User2. So when user2 tries to save the
changes, he will get concurrency exection:
{
Console.WriteLine("Optimistic Concurrency exception occured");
}
}
//User 2 saves changes after User 1.
//User 2 will get concurrency exection
//because CreateOrModifiedDate is different in the database
using (var context = new SchoolDBEntities())
{
try
{
context.Entry(student1WithUser2).State = EntityState.Modified;
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine("Optimistic Concurrency exception occured");
}
}
Concurrency in Code-First:
You can create a timestamp property in code-first by using [Timestamp]
attribute. Make sure that the property type is byte[] because timestamp is
binary in C#.
[Timestamp]
public byte[] RowVersion { get; set; }
EF includes a property in the where clause, during the update operation, if
the property is marked with the Timestamp attribute.
You can resolve concurrency exceptions many ways. Visit msdn for detailed
information on how to resolve optimistic concurrency.
You will see the function in the context class for GetCoursesByStudentId as
shown below:
Now, GetCoursesByStudentId can be called and the result shown below will
be returned:
You will learn how to use stored procedures for CUD operation in the next
chapter.
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Update [SchoolDB].[dbo].[Student]
set StudentName = @StudentName,StandardId = @StandardId
where StudentID = @StudentId;
END
sp_DeleteStudent
First of all, add these stored procedures into EDM and make sure that
the Import selected stored procedures and function into the entity
model checkbox is unchecked as we will map these procedures with
Student entity directly.
Now, Model Browser will add procedures into Storage model but not in
Function Imports
In the EDM designer, right click on Student entity and select Stored
Procedure Mapping to open Mapping details:
In the Mapping Details, you will see <Select Insert Function>, <Select
Update Function>, and <Select Delete Function>. Select the appropriate
stored procedure for each one, e.g. Select sp_InsertStudentInfo for Insert
function, as shown below:
Now, we need to validate it before executing to ensure that there will not be
a run time error. To accomplish this, right click on Student entity in the
designer and click Validate and make sure that there are no warnings or
errors:
Now you can add, update, and delete student as shown below:
exec [dbo].[sp_InsertStudentInfo]
@StandardId=NULL,@StudentName='New Student using SP'
go
exec [dbo].[sp_UpdateStudent]
@StudentId=47,@StandardId=NULL,@StudentName='Edited student
using SP'
go
exec [dbo].[sp_DeleteStudent] @StudentId=47
go
Note: Once context calls SaveChanges after adding a new student, it will
assign new StudentID to StudentID property of the Student entity because
sp_InsertStudentInfo returns StudentId. This is necessary in order to use
that entity object for further operation.
Int16
Int32
Int64
Byte
SByte
You can create and use an Enum type in your entity data model in three
ways:
1. Convert an existing property of entity to Enum from EDM designer
2. Add a new Enum from EDM designer
3. Use an existing Enum type from different namespace
For the purposes of this demo, we have included the TeacherType integer
column in the Teacher table of SchoolDB. TeacherType 1 is for permanent
teachers, 2 is for contractor teachers, and 3 is for guest teachers.
1. Convert an existing property to Enum:
Next, we will see how to convert a TeacherType to an Enum.
First, right click on the TeacherType property of a Teacher entity and click
'Convert to Enum' in the context menu.
It will open the 'Add Enum Type' dialog box where you can enter the 'Enum
Type Name' and, select 'Underlying Type' and Enum member names. For
example:
After converting it to Enum, you can see TeacherType as Enum Type in the
Model Browser, as shown below:
Also, you can see that the type of the TeacherType property is converted to
TeacherType Enum:
Now, you can use TeacherType Enum in CRUD operation using DBContext.
For example:
tchr.TeacherType = TeacherType.Permanent;
ctx.Teachers.Add(tchr);
ctx.SaveChanges();
}
After creating an Enum Type you can change the type of the TeacherType
property to the newly created TeacherType Enum from the property window.
3. If you already have Enum type created in your code, then you can
use that as a data type of any entity property.
To use an existing Enum type, right click on designer Add New Enum
Type. Enter the Enum Type Name in the dialog box. Do not enter the member
as you already have that in your code.
Now, select 'Reference external type' checkbox and enter the namespace of
your existing enum and click OK. This will add the Enum type in the Model
browser. Then, you can assign this Enum type to any property of an entity
from the property window.
Note: Select 'Set Flags attribute' if you want to use bitwise operators with
your Enum.
Now, create an entity data model (.edmx) after having geography column in
the database as shown in previous chapter section. After creating EDM, you
of
Course
entity
is
You can now use the Location property in CRUD operation using DBContext
as shown in the following example.
Visit MSDN for more information on geography data type and geometry data
type of MS SQL Server 2008.
USE [SchoolDB]
GO
/****** Object: UserDefinedFunction [dbo].[GetCourseListByStudentID]
*/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[GetCourseListByStudentID]
(
-- Add the parameters for the function here
@studentID int
)
RETURNS TABLE
AS
RETURN
(
Now, update your EDM and add this TVF into your EDM. Right click on the
designer select Update Model from the Database..
Expand Stored Procedures and Functions node expand schema node (dbo
schema in our case) select 'GetCourseListByStudentID' and click Finish.
Make sure that the checkbox for 'Import selected procedures and functions
into the entity model' is checked (this will import the function automatically).
After you imported the function, you can verify it: Open Model Browser
expand Function
Imports
right
click
on
imported function
'GetCourseListByStudentID' click Edit:
You can see that EDM has automatically created the complex type
GetCourseListByStudentID_Result as a return collection type.
You can also select an existing entity as a return type if TVF returns the same
columns as entity:
Local Data
The Local property of DBSet provides simple access to the entities that are
currently being tracked by the context, and have not been marked as
Deleted. Local keeps track of entities whose entity state is added, modified
and unchanged. For example:
{
Console.WriteLine("Found {0}: {1} with state {2}",
student.StudentID, student.StudentName,
ctx.Entry(student).State);
}
}
Output:
In Local :
Found 0: New Student1 with state Added
Found 2: Student2 with state Unchanged
Found 3: Student3 with state Unchanged
In DbSet query:
Found 1: Student1 with state Deleted
Found 2: Student2 with state Unchanged
Found 3: Student3 with state Unchanged
Eager Loading:
Eager loading is the process whereby a query for one type of entity also
loads related entities as part of the query. Eager loading is achieved using
the Include method of IQueryable.
The code snippet shown below demonstrates eager loading, where Standard
entity will also be loaded with Student entity using the Include() method:
LINQ Query Syntax:
using System;
using System.Data.Entity;
class Program
{
static void Main(string[] args)
{
using (var ctx = new SchoolDBEntities())
{
stud = ctx.Students.Include(s => s.Standard)
.Where(s => s.StudentName == "Student1")
.FirstOrDefault<Student>();
}
}
}
The code shown above will result in the following SQL query:
[Extent2].[StandardId] AS [StandardId],
[Extent2].[StandardName] AS [StandardName],
[Extent2].[Description] AS [Description]
FROM [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[Standard] AS [Extent2] ON [Extent1].[StandardId]
= [Extent2].[StandardId]
WHERE 'Student1' = [Extent1].[StudentName]
[Project2].[StudentName] AS [StudentName],
[Project2].[StandardId] AS [StandardId],
[Project2].[StandardName] AS [StandardName],
[Project2].[Description] AS [Description],
[Project2].[C1] AS [C1],
[Project2].[TeacherId] AS [TeacherId],
[Project2].[TeacherName] AS [TeacherName],
[Project2].[StandardId1] AS [StandardId1]
FROM ( SELECT
[Limit1].[StudentID] AS [StudentID],
[Limit1].[StudentName] AS [StudentName],
[Limit1].[StandardId1] AS [StandardId],
[Limit1].[StandardName] AS [StandardName],
[Limit1].[Description] AS [Description],
[Project1].[TeacherId] AS [TeacherId],
[Project1].[TeacherName] AS [TeacherName],
[Project1].[StandardId] AS [StandardId1],
CASE WHEN ([Project1].[TeacherId] IS NULL) THEN CAST(NULL AS int)
ELSE 1 END AS [C1]
FROM (SELECT TOP (1) [Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName], [Extent1].[StandardId] AS
[StandardId2], [Extent2].[StandardId] AS [StandardId1], [Extent2].
[StandardName] AS [StandardName], [Extent2].[Description] AS
[Description]
FROM [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[Standard] AS [Extent2] ON [Extent1].
[StandardId] = [Extent2].[StandardId]
WHERE 'updated student' = [Extent1].[StudentName] ) AS [Limit1]
LEFT OUTER JOIN (SELECT
[Extent3].[TeacherId] AS [TeacherId],
[Extent3].[TeacherName] AS [TeacherName],
[Extent3].[StandardId] AS [StandardId]
FROM [dbo].[Teacher] AS [Extent3]
Learn how Entity Framework supports lazy loading of entities, in the next
section.
Lazy Loading:
One of the important functions of Entity Framework is lazy loading. Lazy
loading means delaying the loading of related data, until you specifically
request for it. For example, Student class contains StudentAddress as a
complex property. So, the context first loads all the students from the
database, then it will load the address of a particular student when we access
StudentAddress property as shown below.
The code shown above will result in two SQL queries. First, it will fetch all
students:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
The, it will send the following query when we get the reference of
StudentAddress:
However, you can also turn off lazy loading for a particular property or an
entire context. To turn off lazy loading for a particular property, do not make
it virtual. To turn off lazy loading for all entities in the context, set its
configuration property to false:
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.Objects;
using System.Linq;
public partial class SchoolDBEntities : DbContext
{
public SchoolDBEntities(): base("name=SchoolDBEntities")
{
this.Configuration.LazyLoadingEnabled = false;
}
}
If you run the code shown above, you can see that it first loads student but
not standard, as shown below:
The code shown above will execute two different database queries. The first
query gets Student and the second query gets Standard.
Load collection:
Use the Collection() method instead of Reference() method to load collection
navigation property. The following example loads the courses of student.
If you change the column name in query, then it will throw an exception
because it must match column names:
Validate Entity
You can write custom server side validation for any entity. To accomplish this,
override ValidateEntity method of DBContext as shown below.
As you can see in the above code, we are validating the Student entity. If
StudentName is blank, then we are adding DBValidationError into
DBEntityValidationResult. So whenever you call DBContext.SaveChanges
method and you try to save Student entity without StudentName, then it will
throw DbEntityValidationException. For example:
try
{
using (var ctx = new SchoolDBEntities())
{
ctx.Students.Add(new Student() { StudentName = "" });
ctx.Standards.Add(new Standard() { StandardName = "" });
ctx.SaveChanges();
}
}
catch (DbEntityValidationException dbEx)
{
foreach (DbEntityValidationResult entityErr in
dbEx.EntityValidationErrors)
{
foreach (DbValidationError error in entityErr.ValidationErrors)
{
Console.WriteLine("Error Property Name {0} : Error Message:
{1}",
error.PropertyName, error.ErrorMessage);
}
}
}
You can rename the diagram, for example: StudentDiagram to include all
student related entities:
Now, you can drag and drop Student and StudentAddress entities to this new
diagram:
It will create a new diagram and move Teacher and Course entities to a new
diagram as shown below:
So, in this way you can create a new diagram from an existing diagram.
You can also include the related entities of a particular entity. For example,
right click on Student entity select 'Include Related'. Standard and Course
entities will also be included, because Student has their reference property:
Additionally, you can also move properties up or down by right clicking on the
property select Move Property select UP/Down etc. as shown below:
Remove from Diagram will only remove the entity from the diagram whereas
'Delete from Model' will delete the entity from the EDM. So you won't be able
to use that entity at all.
To change the color of multiple entities at once, select multiple entities and
change Fill Color from the property window:
Additionally, you can also change the property format of entities to either
Display name or Display name and type. Right click on designer select
'Scalar Property format' select 'Display Name and Type':
This way, you can change the color and display of entities in the EDM
designer.