DbContext _ _ holds the database connection and the entity classes
Provides LINQ-based data access
Implements identity tracking, change tracking, and API for CRUD operations
Entity classes
Each database table is typically mapped to a single entity class (C# class)
Associations (Relationship Management)
An association is a primary key / foreign key based relationship between two entity classes
Allows navigation from one entity to another, e.g. Student.Courses
Concurrency control
EntityFramework uses optimistic concurrency control (no locking by default)
Provides automatic concurrency conflict detection and means for conflicts resolution
The DbContext Class
The DbContext class is generated by the Visual Studio designer
DbContext provides:
Methods for accessing entities (object sets) and creating new entities ( Add() methods)
Ability to manipulate database data though entity classes (read, modify, delete, insert)
Easily navigate through the table relationships
Executing LINQ queries as native SQL queries
Create the DB schema in the database server
Using DbContext Class
First create instance of the DbContext : NorthwindEntities northwind = new NorthwindEntities();
In the constructor you can pass a database connection string and mapping source
DbContext properties
Connection – the SqlConnection to be used
CommandTimeout – timeout for database SQL commands execution
All entity classes (tables) are listed as properties
e.g. ObjectSet<Order>Orders{get;}
Reading Data with LINQ Query
Executing LINQ-to-Entities query over EF entity:
using (var context = new NorthwindEntities())
{
var customers =
from c in context.Customers
where c.City == "London"select c;
}
Customers property in the DbContext :
publicpartialclassNorthwindEntities : DbContext
{
public ObjectSet<Customer> Customers
{
get { … }
}
}
We can also use extension methods (fluent API) for constructing the query
using (var context = new NorthwindEntities())
{
var customerPhoness = context.Customers
.Select(c => c.Phone)
.Where(c => c.City == "London")
.ToList();
}
Find element by id
using (var context = new NorthwindEntities())
{
var customer = context.Customers.Find(2);
Console.WriteLine(customer.ContactTitle);
}
Logging the Native SQL Queries
To print the native database SQL commands executed on the server use the following:
var query = context.Countries;
Console.WriteLine(query.ToString());
This will print the SQL native query executed at the database server to select the Countries
Can be printed to file using StreamWriter _ _ class instead of Console _ _ class
Creating New Data
To create a new database row use the method Add(…) of the corresponding collection:
// Create new order object
Order order = new Order()
{
OrderDate = DateTime.Now, ShipName = "Titanic",
ShippedDate = new DateTime(1912, 4, 15),
ShipCity = "Bottom Of The Ocean"
};
// Mark the object for inserting
context.Orders.Add(order);
context.SaveChanges();
SaveChanges() method call is required to post the SQL commands to the database
Cascading Inserts
We can also add cascading entities to the database:
Country spain = new Country();
spain.Name = "Spain";
spain.Population = "46 030 10";
spain.Cities.Add(new City { Name = "Barcelona"} );
spain.Cities.Add(new City { Name = "Madrid"} );
countryEntities.Countries.Add(spain);
countryEntities.SaveChanges();
This way we don’t have to add each City individually
They will be added when the Country entity ( Spain ) is inserted to the database
Updating Existing Data
DbContext allows modifying entity properties and persisting them in the database
Just load an entity, modify it and call SaveChanges()
The DbContext _ _ automatically tracks all changes made on its entity objects
Order order = northwindEntities.Orders.First();
order.OrderDate = DateTime.Now;
context.SaveChanges();
Deleting Existing Data
Delete is done by Remove() on the specified entity collection
SaveChanges() method performs the delete action in the database
Order order = northwindEntities.Orders.First();
// Mark the entity for deleting on the next save
northwindEntities.Orders.Remove(order);
northwindEntities.SaveChanges();
Extending Entity Classes
When using “database first” or “model first” entity classes are separate .cs files that are generated by T4 tempalte XXXModel\.tt
And each time we update the EntitiesModel _ _ from the database all files are generated anew
If we add methods like ToString() , they will be overridden and lost
That is why all the entity classes are " partial "
We can extend them in another file with the same partial class
When using “code first” this is not a problem
Executing Native SQL Queries
Executing a native SQL query in Entity Framework directly in its database store: ctx.Database.SqlQuery<return-type>(native-SQL-query);
Example:
string query = "SELECT count(*) FROM dbo.Customers";
var queryResult = ctx.Database.SqlQuery<int>(query);
int customersCount = queryResult.FirstOrDefault();
Examples are shown in SQL Server but the same can be done for any other database
Native SQL queries can also be parameterized:
NorthwindEntities context = new NorthwindEntities();
string nativeSQLQuery =
"SELECT FirstName + ' ' + LastName " +
"FROM dbo.Employees " +
"WHERE Country = {0} AND City = {1}";
object[] parameters = { country, city };
var employees = context.Database.SqlQuery<string>(
nativeSQLQuery, parameters);
foreach (var emp in employees)
{
Console.WriteLine(emp);
}
Joining Tables in EF
In EF we can join tables in LINQ or by using extension methods on IEnumerable<T>
The same way like when joining collections
northwindEntities.Customers.
Join(northwindEntities.Suppliers,
(c=>c.Country), (s=>s.Country), (c,s)=>
new {Customer = c.CompanyName, Supplier =
s.CompanyName, Country = c.Country });
var custSuppl =
from customer in northwindEntities.Customers
join supplier in northwindEntities.Suppliers
on customer.Country equals supplier.Country
selectnew {
CustomerName = customer.CompanyName,
Supplier = supplier.CompanyName,
Country = customer.Country
};
Grouping Tables in EF
Grouping also can be done by LINQ
The same ways as with collections in LINQ
Grouping with LINQ:
var groupedCustomers =
from customer in northwindEntities.Customers
group customer by Customer.Country;
Grouping with extension methods:
var groupedCustomers =
northwindEntities.Customers.GroupBy(
customer => customer.Country);
Attaching and Detaching Objects
In Entity Framework, objects can be attached to or detached from an object context
Attached objects are tracked and managed by the DbContext
SaveChanges() persists all changes in DB
Detached objects are not referenced by the DbContext
Behave like a normal objects, like all others, which are not related to EF
Attaching Detached Objects
When a query is executed inside an DbContext , the returned objects are automatically attached to it
When a context is destroyed, all objects in it are automatically detached
E.g. in Web applications between the requests
You might later on attach to a new context objects that have been previously detached
Detaching Objects
When an object is detached?
When we obtain the object from an DbContext and then Dispose it
Manually: by calling Detach (…) method
Product GetProduct(int id)
{
using (NorthwindEntities northwindEntities =
new NorthwindEntities())
{
return northwindEntities.Products.First(
p => p.ProductID == id);
}
}
Attaching Objects
When we want to update a detached object we need to reattach it and the update it
Done by the Attach(…) method of the context
voidUpdatePrice(Product product, decimal newPrice)
{
using (NorthwindEntities northwindEntities =
new NorthwindEntities())
{
northwindEntities.Products.Attach(product);
product.UnitPrice = newPrice;
northwindEntities.SaveChanges();
}
}
Homework
Using the Visual Studio Entity Framework designer create a DbContext for the Northwind database
Create a DAO class with static methods which provide functionality for inserting, modifying and deleting customers. Write a testing class.
Write a method that finds all customers who have orders made in 1997 and shipped to Canada.
Implement previous by using native SQL query and executing it through the DbContext .
Write a method that finds all the sales by specified region and period (start / end dates).
Create a database called NorthwindTwin with the same structure as Northwind using the features from DbContext . Find for the API for schema generation in MSDN or in Google.
Try to open two different data contexts and perform concurrent changes on the same records. What will happen at SaveChanges() ? How to deal with it?
By inheriting the Employee entity class create a class which allows employees to access their corresponding territories as property of type EntitySet<T> .
Create a method that places a new order in the Northwind database. The order should contain several order items. Use transaction to ensure the data consistency.
Create a stored procedures in the Northwind database for finding the total incomes for given supplier name and period (start date, end date). Implement a C# method that calls the stored procedure and returns the retuned record set.
Create a database holding users and groups. Create a transactional EF based method that creates an user and puts it in a group “Admins”. In case the group “Admins” do not exist, create the group in the same transaction. If some of the operations fail (e.g. the username already exist), cancel the entire transaction.
* Use SQL Server Profiler to view all your queries from previous homework tasks