Unit 3
Unit 3
The above figure represents how an entity framework interacts with the domain class and
database. It provides a connection between the business entity and data tables in the database. It
saves data stored in the properties of business entities and also retrieves data from the database
and converts it to business entities objects automatically. Entity Framework will execute the
relevant query in the database and then materialize results into instances of your domain objects
for you to work within your app.
What is Entity Framework?
Entity Framework was first released in 2008, Microsoft's primary means of interacting
between .NET applications and relational databases. Entity Framework is an Object Relational
Mapper (ORM) which is a type of tool that simplifies mapping between objects in your software
to the tables and columns of a relational database.
Entity Framework (EF) is an open source ORM framework for ADO.NET which is a part
of .NET Framework.
An ORM takes care of creating database connections and executing commands, as well as
taking query results and automatically materializing those results as your application
objects.
An ORM also helps to keep track of changes to those objects, and when instructed, it will
also persist those changes back to the database for you.
Conceptual Model
When defining the class and features of entity framework first and then entity framework convert
it into the conceptual model first and it creates database and objects in the database from the
conceptual model this method is called Code First. Now your object directly works with the
database to retrieve or make changes.
Features of Entity Framework
It is platform independent.
It uses LinQ queries to manipulate the data in the database instead of SQL queries.
It keep the track of values that have been changed of the properties of entities.
It also save changes which are done insert, delete or update operations.
It also handle concurrency so the data override by a user and will reflect when another use
fetches it.
It also handles transaction management automatically and also provides the customize options
for transaction management.
It provides caching which means it stores the result of the frequently used queries.
It also follow certain conventions for programming so it by default configure the EF Model.
It also allows to configure the EF Model by a fluent API to override the default convention.
If you made any changes in database schema then you can reflect those changes in EF model
by writing migration command in CLI(Command Line Interface).
It also support stored procedure.
It also support parameterized queries.
ORM
What is an ORM?
ORM stands for Object-Relational Mapping. It is a programming technique used to map data
between an object-oriented programming language and a relational database management system
(RDBMS).
The goal of ORM is to simplify the development of data-driven applications by providing a high-
level, object-oriented API that can be used to interact with the database. ORM frameworks provide
a set of classes and methods that map database tables and relationships to objects and collections of
objects in the programming language.
ORM sets the mapping between the set of objects which are written in the preferred programming
language like JavaScript and relational database like SQL. It hides and encapsulates the SQL
queries into objects and instead of SQL queries we can use directly the objects to implement the
SQL query.
In an ORM framework, a database table is represented by a class, and each row in the table is
represented by an instance of the class. The ORM framework handles the mapping of data between
the class instances and the database tables. This means that developers can work with objects in
their programming language of choice, rather than writing SQL queries and working with result
sets.
ORM frameworks also provide features such as caching, lazy loading, and change tracking, which
can improve performance and simplify application development.
Some popular ORM frameworks include Entity Framework Core, Hibernate (for Java), and Django
ORM (for Python).
ORM (Object-Relational Mapping) is required for several reasons:
Object-Oriented Programming: Most modern programming languages, such as C#, Java,
Python, and Ruby, are object-oriented. ORM frameworks allow developers to work with
objects in their programming language of choice, which simplifies application
development.
Database Abstraction: ORM frameworks abstract the details of the underlying database,
including table structures, relationships, and data types. This means that developers can
work with a higher-level API instead of writing SQL queries and managing result sets.
Security: ORM frameworks can provide security features such as parameterized queries
and data validation, which can help prevent SQL injection attacks and other security
vulnerabilities.
Performance: ORM frameworks can improve performance by caching data, reducing the
number of database queries required, and optimizing database access.
Maintenance: ORM frameworks can simplify maintenance by allowing developers to
work with a single set of object-oriented classes that map to database tables, rather than
having to manage both object-oriented classes and SQL queries separately.
In summary, ORM frameworks provide a higher-level, object-oriented API that simplifies
database access, improves security, and can improve application performance and
maintenance.
While ORM (Object-Relational Mapping) can provide many benefits, there are also
some potential drawbacks to consider:
Learning Curve: ORM frameworks can have a steep learning curve, especially for
developers who are not familiar with the specific framework. Developers need to
understand the framework’s conventions and configuration options to use it effectively.
Performance Overhead: ORM frameworks can introduce additional performance
overhead compared to direct SQL queries. The ORM framework must translate object-
oriented operations into SQL statements, which can add additional overhead and potentially
impact performance.
Limited Control: ORM frameworks can limit the control that developers have over the
SQL statements that are executed against the database. This can limit the ability to optimize
queries for performance or to take advantage of database-specific features.
Complexity: ORM frameworks can add complexity to the application, especially if the
database schema is complex or if the application uses advanced database features. In some
cases, it may be simpler and more efficient to write custom SQL queries instead of using an
ORM framework.
Compatibility: ORM frameworks may not be compatible with all databases or database
features. Developers may need to use a different ORM framework or write custom SQL
queries to work with specific databases or features.
In summary, while ORM frameworks can provide many benefits, they may also have some
drawbacks related to learning curve, performance overhead, limited control, complexity,
and compatibility. It is important to carefully evaluate these factors when deciding whether
to use an ORM framework in an application.
Popular ORM In DOT NET
There are several popular ORM (Object-Relational Mapping) frameworks in the .NET
ecosystem, including:
Entity Framework Core: This is the most popular ORM framework for .NET and is
included in the .NET Core and .NET Framework runtime. It provides a high-level, object-
oriented API for working with databases and supports multiple database providers,
including SQL Server, MySQL, and PostgreSQL.
NHibernate: This is a mature and popular ORM framework that has been around since
2005. It is based on the Java Hibernate framework and provides similar features for .NET
developers. NHibernate supports multiple database providers and is highly customizable.
Dapper: This is a lightweight ORM framework that provides high performance and
supports raw SQL queries as well as object mapping. It is popular for applications that
require high performance and control over the SQL queries that are executed.
In summary, the most popular ORM framework in the .NET ecosystem is Entity
Framework Core, followed by NHibernate, Dapper. Each of these frameworks has its own
strengths and weaknesses, and the choice of framework will depend on the specific
requirements of the application.
When to use EF Core and Dapper
EF Core and Dapper are both popular ORM (Object-Relational Mapping) frameworks in
the .NET ecosystem, but they have different strengths and weaknesses, and the choice of
framework will depend on the specific requirements of the application.
EF Core is a full-featured ORM framework that provides a high-level, object-oriented API
for working with databases. It supports multiple database providers, including SQL Server,
MySQL, PostgreSQL, and SQLite, and provides features such as change tracking, lazy
loading, and automatic database schema migration. EF Core is ideal for applications that
require a high-level, object-oriented API and need to work with a variety of database
providers.
Dapper, on the other hand, is a lightweight ORM framework that provides high
performance and supports raw SQL queries as well as object mapping. It is popular for
applications that require high performance and control over the SQL queries that are
executed. Dapper does not provide features such as change tracking or automatic schema
migration, so it is best suited for applications that require low-level control over database
access.
In general, if an application requires a high-level, object-oriented API and needs to work
with a variety of database providers, EF Core is a good choice. If an application requires
high performance and low-level control over database access, Dapper is a good choice. It is
also possible to use both EF Core and Dapper in the same application, depending on the
specific requirements of different parts of the application.
TABLE MAPPING
1. Table splitting
2. Entity splitting
3. Table-specific facet configuration
EF Core offers a lot of flexibility when it comes to mapping entity types to tables in a database.
This becomes even more useful when you need to use a database that wasn't created by EF.
The below techniques are described in terms of tables, but the same result can be achieved when
mapping to views as well.
TABLE SPLITTING
EF Core allows to map two or more entities to a single row. This is called table splitting or table
sharing.
Configuration
To use table splitting the entity types need to be mapped to the same table, have the primary keys
mapped to the same columns and at least one relationship configured between the primary key of
one entity type and another in the same table.
A common scenario for table splitting is using only a subset of the columns in the table for greater
performance or encapsulation.
In this example Order represents a subset of DetailedOrder.
public class Order
{
public int Id { get; set; }
public OrderStatus? Status { get; set; }
public DetailedOrder DetailedOrder { get; set; }
}
public class DetailedOrder
{
public int Id { get; set; }
public OrderStatus? Status { get; set; }
public string BillingAddress { get; set; }
public string ShippingAddress { get; set; }
public byte[] Version { get; set; }
}
In addition to the required configuration we call Property(o =>
o.Status).HasColumnName("Status") to map DetailedOrder.Status to the same column
as Order.Status.
C#Copy
modelBuilder.Entity<DetailedOrder>(
dob =>
{
dob.ToTable("Orders");
dob.Property(o => o.Status).HasColumnName("Status");
});
modelBuilder.Entity<Order>(
ob =>
{
ob.ToTable("Orders");
ob.Property(o => o.Status).HasColumnName("Status");
ob.HasOne(o => o.DetailedOrder).WithOne()
.HasForeignKey<DetailedOrder>(o => o.Id);
ob.Navigation(o => o.DetailedOrder).IsRequired();
});
context.Add(
new Order
{
Status = OrderStatus.Pending,
DetailedOrder = new DetailedOrder
{
Status = OrderStatus.Pending,
ShippingAddress = "221 B Baker St, London",
BillingAddress = "11 Wall Street, New York"
}
});
context.SaveChanges();
}
modelBuilder.Entity<DetailedOrder>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
Inheritance
It's recommended to read the dedicated page on inheritance before continuing with this section.
The dependent types using table splitting can have an inheritance hierarchy, but there are some
limitations:
The dependent entity type cannot use TPC mapping as the derived types wouldn't be able to
map to the same table.
The dependent entity type can use TPT mapping, but only the root entity type can use table
splitting.
If the principal entity type uses TPC, then only the entity types that don't have any
descendants can use table splitting. Otherwise the dependent columns would need to be
duplicated on the tables corresponding to the derived types, complicating all interactions.
ENTITY SPLITTING
EF Core allows to map an entity to rows in two or more tables. This is called entity splitting.
Configuration
For example, consider a database with three tables that hold customer data:
A Customers table for customer information
A PhoneNumbers table for the customer's phone number
An Addresses table for the customer's address
Here are definitions for these tables in SQL Server:
SQLCopy
CREATE TABLE [Customers] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY ([Id]));
modelBuilder.Entity<Cat>()
.ToTable(
"Cats",
tableBuilder => tableBuilder.Property(cat => cat.Id).HasColumnName("CatId"));
modelBuilder.Entity<Dog>()
.ToTable(
"Dogs",
tableBuilder => tableBuilder.Property(dog => dog.Id).HasColumnName("DogId"));
With the TPC inheritance mapping, the Breed property can also be mapped to different column
names in different tables. For example, consider the following TPC tables:
SQLCopy
CREATE TABLE [Cats] (
[CatId] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[CatBreed] nvarchar(max) NOT NULL,
[EducationalLevel] nvarchar(max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([CatId]));
modelBuilder.Entity<Cat>()
.ToTable("Cats",builder => { builder.Property(cat => cat.Id).HasColumnName("CatId");
builder.Property(cat => cat.Breed).HasColumnName("CatBreed");
});
modelBuilder.Entity<Dog>()
.ToTable("Dogs",builder => {builder.Property(dog => dog.Id).HasColumnName("DogId");
builder.Property(dog => dog.Breed).HasColumnName("DogBreed");
});
Step 3 − Select ADO.NET Entity Data Model from middle pane and enter name
DatabaseFirstModel in the Name field.
Step 4 − Click Add button which will launch the Entity Data Model Wizard dialog.
Step 5 − Select EF Designer from database and click Next button.
Step 8 − Select all the tables Views and stored procedure you want to include and click Finish.
You will see that Entity model and POCO classes are generated from the database.
Let us now retrieve all the students from the database by writing the following code in program.cs
file.
using System;
using System.Linq;
namespace DatabaseFirstDemo {
class Program {
static void Main(string[] args) {
using (var db = new UniContextEntities()) {
var query = from b in db.Students
orderby b.FirstMidName select b;
Console.WriteLine("All All student in the database:");
foreach (var item in query) {
Console.WriteLine(item.FirstMidName +" "+ item.LastName);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
}
When the above program is executed, you will receive the following output −
All student in the database:
Ali Khan
Arturo finand
Bill Gates
Carson Alexander
Gytis Barzdukas
Laura Norman
Meredith Alonso
Nino Olivetto
Peggy Justice
Yan Li
Press any key to exit...
When the above program is executed, you will see all the students’ name which were previously
entered in the database.
ENTITY FRAMEWORK - CODE FIRST APPROACH
The Entity Framework provides three approaches to create an entity model and each one has their
own pros and cons. *Code First * Database First * Model First
Some developers prefer to work with the Designer in Code while others would rather just work
with their code. For those developers, Entity Framework has a modeling workflow referred to as
Code First.
Code First modeling workflow targets a database that doesn’t exist and Code First will
create it.
It can also be used if you have an empty database and then Code First will add new tables
too.
Code First allows you to define your model using C# or VB.Net classes.
Additional configuration can optionally be performed using attributes on your classes and
properties or by using a fluent API.
// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
// Primary key
public int CourseID { get; set; }
[Key]
public int CompanyId { get; set; }
}
Timestamp Attribute
The Timestamp attribute specifies the byte array (byte []) property/column that has a concurrency
mode of "Fixed" in the model and it should be a Timestamp column in the stored model
(database).
Example
[Timestamp]
public byte[] TimeStamp { get; set; }
ConcurrencyCheck Attribute
The ConcurrencyCheck Attribute is used to specify a property/column that has a concurrency
mode of “fixed” in the EDM model. This attribute can be used with scalar properties only. A fixed
concurrency mode means that this property is a part of the concurrency check during save
operations.
Example
[ConcurrencyCheck]
public string Name { get; set; }
ForeignKey Attribute
This attribute specifies the foreign key for the Navigation property.
Example
[Table("Employee", Schema = "dbo")]
public class Employee
{
[Column("ID", Order = 1)]
public int EmployeeId { get; set; }
[ForeignKey("DepartmentId")]
public int DepartmentId { get; set; }
[ForeignKey("DepartmentId")]
public DepartmentMaster Department { get; set; }
}
InverseProperty Attribute
A relationship in the Entity Framework always has two ends, a navigation property on each side
and an Entity Framework that maps them together automatically by convention. If there are
multiple relationships between the two entities, Entity Framework cannot handle the relationships.
This is because Entity Framework doesn't know which navigation property map with which
properties on another side. For example, an Employee is bound with two departments one is the
primary and the other is the secondary. In this scenario, Entity Framework does not know which
navigation properties on the opposite side should be returned.
Using an InverseProperty attribute, we can specify which navigation property should be returned.
Example
[Table("Department", Schema = "dbo")]
public class DepartmentMaster
{
[Key]
public int DepartmentId { get; set; }
[Required]
public string Code { get; set; }
[MinLength(5)]
[MaxLength(100)]
[Index("IX_Name_DepartmentMaster", IsClustered = false, Order = 2)]
public string Name { get; set; }
ComplexType Attribute
The Complex types are the non-scalar properties of an entity that enable scalar properties to be
organized within the entities. A Complex type may have scalar properties or other complex type
properties. Complex types do not have a key and Identity so Entity Framework cannot manage
these objects apart from the parent entity. A Complex type entity can be also used for a Stored
Procedure result.
Example
[ComplexType]
public class UserInfo
{
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
}
[Table("Department", Schema = "dbo")]
public class DepartmentMaster
{
[Key]
public int DepartmentId { get; set; }
// Other properties...
[NotMapped]
public class InternalClass
{
public int Id { get; set; }
public string Name { get; set; }
}
Required Attribute
The Required attribute tells the Entity Framework that this property must have a value and this
attribute will force the Entity Framework to ensure that this has data in it. This attribute will also
participate in database creation (by marking this column as “not nullable”).
Example
[Required]
public string Code { get; set; }
C#
MinLength Attribute
This attribute is used to validate a property, whether the property has a minimum length of string.
Example
[MinLength(5)]
public string Name { get; set; }
MaxLength Attribute
The maxlength attribute allows us to specify additional property validations to set the maximum
length of the string. This attribute will also participate in database creation (by setting the length
of the property).
Example
[MinLength(5)]
[MaxLength(100)]
public string Name { get; set; }
StringLength Attribute
StringLength is used to specify the maximum length of the string. This attribute is applied only to
string-type properties. We can also specify a minimum length of characters that are allowed in the
data field. This attribute will also participate in database creation (by setting the length of the
property).
Example
[StringLength(100, MinimumLength = 5)]
public string Name { get; set; }
DATABASE INITIALIZER
The Code-First approach allows you to define model classes as per the Domain requirements.
Hence, you have complete control over the classes being written or implemented.
In the code-first approach the user who is working will only concentrate on creating classes,
models, and writing code, the rest of the work like creating a database, creating tables, assigning
keys, etc, will be looked over by the Entity framework.
Thus here the developer will not get the overload of backend works.
The Code-First approach has there own principle or strategy. These strategies are the main
backbone of the code-first approach. These strategies are also called database Initializers.
Before going deep into the code, firstly, I would like to give a brief introduction about what
these approaches are.
Here, I have explained the Database initializers and how they work in creating and updating
records in the model.
To add initializers to our dbcontext we need the following things in dbcontext class.
For using all these we need a namespace that is using System.Data.Entity;
So here I am explaining each and every database initializer and how we can use them in our
project.
1. CreateDatabaseIfNotExists:
This is the default initializer class used by the Code-First approach, When the application
starts, this initializer checks for the required model database. If it is not found it will create
the database. Suppose we have created a new project and added a model with different
property. Now we will create a controller, that include all CRUD operation views and a
dbcontext.
Suppose, I have these 3 properties in my model. For this create a new project as in the
following screenshot.
Here as per our model, we can see the two fields in the view.
Now when we add a record it will get saved in our local database and shown as follows.
Now when we will check server explorer a database will automatically get created and inside
that, a table of employee created automatically and the record will be added there.
Now when we will check employee table we will get the record.
2. DropCreateDatabaseWhenModelChanges:
As the name suggests this database initializer will drop the table each time you change the
model. The main demerit of this database initializer is that the previous data you saved in the
table will delete. Here I am explaining how it will work.
Here I have added one more field to the model.
Now here I have compared the two models.
Now, I will build the solution and overwrite the controller. And initialise
the "DropCreateDatabaseWhenModelChanges".to the datacontext constructor.
Now, I will override my controller for updating the view for new property and one record.
When I click to add this data it will be shown as follows.
But here we are unable to see the first record which we have uploaded. This is because when
we run this application the "DropCreateDatabaseWhenModelChanges" initializer first
drops the table and then creates a new table. When we check the local table the only record
which we have inserted will show.
3. DropCreateDatabaseAlways:
This strategy of code first approach creates the database every time you run the application.
If the database already exists and there is no modification done in the model, it will drop the
database and create a new one. In this approach, you will also lose the data.
Now here I am changing the database initializer to DropCreateDatabaseAlways as in the
following screenshot,
Now here also we can see that the previous record also deleted.
The above three databases initializing approach fails when you add new model classes or
property to the existing model. To overcome the demerits of Entity-framework code first approach
a new concept called “Entity-framework code first Migration” came into picture.
See the following red color box that shows the added references for EntityFramework and
database:
8. Add a new class named EmployeeContext.cs to our Entity Framework Code First Context
and add the following code. Code for EmployeeContext class file:
9. using System;
10. using System.Collections.Generic;
11. using System.Data.Entity;
12. using System.Linq;
13. using System.Text;
14. using System.Threading.Tasks;
15.
16. namespace CodeFirstMigration
17. {
18. public class EmployeeContext : DbContext
19. {
20. public DbSet<Employee> Employees { get; set; }
21. }
22.
23. public class Employee
24. {
25. public int EmployeeID { get; set; }
26. public string EmpName { get; set; }
27. }
}
28. Now we have a model. It's time to do some operation for data access. Add some code to the
Program.cs file as in the following: Code for Program class file:
29. using System;
30. using System.Collections.Generic;
31. using System.Linq;
32. using System.Text;
33. using System.Threading.Tasks;
34. namespace CodeFirstMigration
35. {
36. class Program
37. {
38. static void Main(string[] args)
39. {
40. using (var context = new EmployeeContext())
41. {
42. //Insert values in database
43. context.Employees.Add(new Employee { EmployeeID = 1, EmpName = "krishna" });
44. context.Employees.Add(new Employee { EmployeeID = 2, EmpName = "radha" });
45. context.SaveChanges();
46. //Get all values from database
47. foreach (var emp in context.Employees)
48. {
49. Console.WriteLine("Employee ID : " + emp.EmployeeID);
50. Console.WriteLine("Employee Name : " + emp.EmpName);
51. }
52. }
53. Console.WriteLine("Press Any key to exit....");
54. Console.ReadKey();
55. }
56. }
}
57. Save the changes and Run. The output is as in the following:
58. See in the SQL Server and SQL Server Object Explorer your
model CodeFirstMigration.EmployeeContext.
Until now we just created the model and added records to the database. The actual concept of
Code First Migration is as follows.
1. Make some more changes to our model. Add a new property Role to the Employees table.
public string Role { get; set; }
2. Run the application. You will not get the error “InvalidOperationException” like the
following:
6. This command added two more classes to your project in the Migrations folder.
o 201411061912454_InitialCreate.cs This migration was generated because Code First
already created a database for us before we enabled migrations.
o Configuration.cs It allows you to configure how Migrations behave for your context.
7. Update the database with the new changes we made in the model.
8. Run Add-Migration MigrationForRoleInEmp in a Package Manager console.
Now it adds one more class to the Migrations folder in MigrationForRoleInEmp.cs. Let's
use Update-Database to apply this migration to the database.
Code First Migrations will compare the migrations in our Migrations folder with the ones that
have been applied to the database. It will see that the MigrationForRoleInEmp migration
needs to be applied and run it. Finally, CodeFirstMigration.EmployeeContext database is
now updated to include the Role column in the Employees table. See the following image:
Entity Framework supports the following three methods to load related data.
Eager Loading
Lazy Loading
Explicit Loading
Eager Loading
In Eager Loading, all relevant data for an entity is loaded at the time of the query of the entity in
the context object. Eager Loading can be done by using the "Include" method. To perform Eager
Loading, Lazy Loading must be disabled. Lazy Loading is enabled by default in Entity
Framework.
Example
Suppose I have the three tables DepartmentMaster, EmployeeMaster, and EmployeeDetails. The
relation among these three tables is shown in the following diagram.
Example Code
using (var context = new Entities())
{
Console.WriteLine("Eager Loading Example....");
var dep = context.DepartmentMasters
.Include(dm => dm.EmployeeMasters)
.FirstOrDefault(f => f.DepartmentId == 1);
foreach (var emp in dep.EmployeeMasters)
{
Console.WriteLine(emp.Code);
}
Console.ReadKey();
}
Output
Eager Loading at multiple levels
We can also eagerly load multiple levels of related entities. In the following example, it loads the
related entity Department, Employee Master, and Detail.
Sample Code
using (var context = new Entities())
{
Console.WriteLine("Eager Loading Example....");
var dep = context.DepartmentMasters
.Include(dm => dm.EmployeeMasters.Select(em => em.EmployeeDetail))
.FirstOrDefault(f => f.DepartmentId == 1);
foreach (var emp in dep.EmployeeMasters)
{
Console.WriteLine($"Code: {emp.Code} Email Address:
{emp.EmployeeDetail.PersonalEmail}");
}
Console.ReadKey();
}
Output
Lazy Loading
Lazy Loading means delayed loading of the related entities. In Lazy Loading, a related entity or
collection of entities is loaded from the database for the first time that a property is referred to or
the entity is accessed.
Sample code
using (var context = new Entities())
{
context.Configuration.LazyLoadingEnabled = true; // If using DbContext
Console.WriteLine("Lazy Loading Example....");
var dep = context.DepartmentMasters
.Include(dm => dm.EmployeeMasters.Select(em => em.EmployeeDetail))
.FirstOrDefault(f => f.DepartmentId == 1);
foreach (var emp in dep.EmployeeMasters)
{
Console.WriteLine($"Code: {emp.Code} Email Address:
{emp.EmployeeDetail.PersonalEmail}");
}
Console.ReadKey();
}
Output
In the code above, initially, one query is fired to fetch Department Master data. When the
Employee Masters property of the Department is accessed, the Entity Framework loads the related
data automatically. In short, whenever the navigation property is accessed, the Entity Framework
loads the related entity from the database. In the example above if one department has two
employees then the for loop retrieves the related employee as well as the employee details table
from the database.
Explicit Loading
If Lazy Loading is disabled then it is still possible to fetch the related entities without the use of
Eager Loading. It is possible with Explicit Loading. To explicitly retrieve the related entities from
the database we can use the "Load" method. In the following example, If Lazy Loading is disabled
then when I check whether the employee has related detail data it returns null.
Output
using (var context = new Entities())
{
context.Configuration.LazyLoadingEnabled = false; // If using DbContext
var emp = context.EmployeeMasters.FirstOrDefault(p => p.EmployeeId == 1);
if (emp.EmployeeDetail == null)
{
Console.WriteLine("No Employee Detail Data or Employee Detail is not loaded");
}
Console.ReadKey();
}
Now I want to load the related detail data explicitly. There is a one-to-one relationship between
EmployeeMaster and the EmployeeDetail entity. The following will explicitly load one-to-one
related entities
using (var context = new Entities())
{
var emp = context.EmployeeMasters.FirstOrDefault(p => p.EmployeeId == 1);
if (emp.EmployeeDetail == null)
{
Console.WriteLine("No Employee Detail Data or Employee Detail is not loaded");
Console.WriteLine("Loading Employee Detail Data....");
if (!context.Entry(emp).Reference(e => e.EmployeeDetail).IsLoaded)
{
context.Entry(emp).Reference(e => e.EmployeeDetail).Load();
Console.WriteLine("Employee Detail Data load Complete....");
Console.WriteLine("Employee Email: " + emp.EmployeeDetail.PersonalEmail);
}
}
Console.ReadKey();
}
Output