CoursMVAAccederAuxDonnees 4EntityFramework6, Part1
CoursMVAAccederAuxDonnees 4EntityFramework6, Part1
CoursMVAAccederAuxDonnees 4EntityFramework6, Part1
Nous allons aborder un nouveau concept et un Framework de haut niveau appelé Entity Framework
6, permettant d’encapsuler ce que nous avons fait dans les 2 premiers cours.
Les prérequis
Ce cours s’adresse à toutes les personnes désirant comprendre les mécanismes mis en jeu lors de
l’accès aux données.
Une connaissance de C# est conseillé pour bien appréhender tous les concepts.
Matériel
Pour suivre ce tutorial, vous aurez besoin de :
1. Visual Studio 2013 / Express : Le cours est basé sur Visual Studio 2013 Ultimate, mais vous
pouvez utiliser Visual Studio Express : TODO
2. SQL Server 2014 / Express / LocalDB : La base de données sera hébergée sur un server SQL.
Vous pouvez utiliser la version SQL Server qui vous convient, SQL Server 2005 à 2014 ou SQL
Server Express / LocalDB pour les versions gratuites.
3. Base de données Northwind : Cette base de données contient quelques données exemples.
Vous pouvez la télécharger ici : http://northwind.codeplex.com
4. Solutions du tutorial : L’ensemble des sources de ce cours sont disponibles ici en annexe de
ce module.
5. MySQL : Serveur MySQL local ainsi que le connecteur .NET :
http://dev.mysql.com/downloads/installer/5.6.html
6. Editeur MySQL : http://www.heidisql.com/
Entity Framework 6
Entity Framework est un mappeur objet relationnel qui permet aux développeurs d’utiliser des
données relationnelles à l’aide d’objets .NET
Le diminutif d’Entity Framework est EF. Pour plus de simplicité nous utiliserons cette abréviation
pour la suite.
EF permet de s’abstraire de tout (ou presque) le code que l’on doit généralement écrire quand on
veut accéder à une source de données.
EF se veut agnostique à la source de données, comme nous l’avons fait dans le cours N°2, le même
code sert que l’on choisisse un fournisseur d’accès SQL ou Oracle ou encore MySQL.
EF est installé par défaut dans Visual Studio et vous donne accès à un designer, comme vous pouvez
le voir dans la copie d’écran suivante :
Lorsque l’on travaille avec Entity Framework, il est important de savoir que l’on a plusieurs choix /
solutions suivant le projet sur lequel on travaille :
Modèles EF
Créer un modèle EF s’effectue suivant 2 cas : Vous possédez ou non une base de données.
Vous possédez déjà une base de données. Il vous est possible d’utiliser le mode :
Database First : Vous générez tout le modèle .NET à partir de la base de données
Code First : Vous avez déjà des entités, et vous mappez ces entités sur votre table avec EF.
Vous ne possédez pas de base de données. Il vous est possible d’utiliser le mode :
Model First : Vous créez un modèle dans le concepteur EF, qui créera la base de données au
premier lancement
Code First : Vous créez des entités C#, et EF créera la base de données au premier lancement
Dans ce cours, nous allons voir comment utiliser EF avec une base de données existante sous SQL
Server avec le modèle Database First. Dans un second temps, nous ferons évoluer le code du cours
N°2 avec le modèle Code First.
Database First
L’ajout d’un élément de type EF se fait via le menu « Ajouter nouvel élément » à votre projet,
comme dans les copies d’écrans suivants :
Une fois la génération terminée, vous avez dans votre projet un élément supplémentaire nommé
dans notre cas Northwind.edmx.
Si on ouvre le fichier Customer.cs , on retrouve une classe Customer, qui ressemble fortement à la
classe Customer que nous avions créé dans les précédents cours.
C’est un des avantages de Visual Studio associé avec Entity Framework, qui va générer pour vous
tout le code nécessaire !
public partial class Customer
{
public Customer()
{
this.Orders = new HashSet<Order>();
this.CustomerDemographics = new HashSet<CustomerDemographic>();
}
Dans le fichier de configuration, vous retrouvez de même la chaine de connexion, certes un peu plus
complexe que celles que nous avons déjà utilisées dans le cours N°2 :
<add name="NorthwindEntities"
connectionString="metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.
msl;provider=System.Data.SqlClient;provider connection string="data
source=.\SQL2014;initial catalog=Northwind;integrated
security=True;MultipleActiveResultSets=True;App=EntityFramework""
providerName="System.Data.EntityClient" />
Linq to Entity
Interroger la base de données via EF6 et le code généré (on parle de modèle généré) se fait via un
langage particulier appelé Linq to Entity (Language Integrated Query)
Ce langage va nous permettre d’écrire des requêtes qui seront ensuite transformés en langage SQL
(ou Oracle ou MySQL) à partir d’un langage typé.
Les requêtes LINQ s’exécutent toujours dans le contexte de votre modèle EF6. Un peu comme l’objet
SqlCommand qui a besoin de l’ouverture d’une connexion SqlConnection.
Requêtes de sélection
Une première requête LINQ qui permet de récupérer tous les objets Customer de votre base
Northwind :
var query = from c in context.Customers select c;
Note : L’exécution de la requête se fait lors de l’appel de la méthode ToList() et non pas lors de la
déclaration de la requête !
SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]
Quelques exemples de requêtes LINQ que vous aurez surement l’occasion d’utiliser :
Filtre
L’idée ici est de passer une variable comme nous aurions passé un paramètre. EF va générer une
requête paramétrée et l’exécuter sur votre base :
String filtre = "Al";
On voit bien que la syntaxe C# StartWith() est transformée en commande SQL Like et la variable de
type string est bien remplacée par un paramètre nvarchar() nommé @p__linq__0
Jointure
L’idée ici est de récupérer des éléments suivants une jointure SQL en utilisant la syntaxe from from :
var query = from e in context.Employees
from r in context.Regions
from t in context.Territories
where t.TerritoryDescription == "Seattle"
select e;
Le gros avantage ici c’est qu’il est inutile de spécifier les jointures à utiliser car elles sont déjà
connues par le modèle Northwind.
Ce n’est pas le cas dans notre modèle, mais si votre base de données ne contient pas de relations (et
donc le modèle ne les connaissant pas) vous pouvez réaliser une relation explicite comme ceci :
var query = from t in context.Territories
join r in context.Regions on t.RegionID equals r.RegionID
where t.TerritoryDescription == "Seattle"
select r;
Notez l’utilisation de la syntaxe « join … in … on … equals » qui permet d’établir la relation explicite
entre deux entités (donc 2 tables)
Tri
L’idée ici est de trier une liste
var query = from c in context.Customers
orderby c.ContactName ascending
select c;
Regroupement
L’idée ici est de grouper les éléments dans une nouvelle structure. Nous allons utiliser un nouveau
concept qui est l’utilisation d’un type anonyme dans la requête LINQ.
Un type anonyme permet de créer un objet dynamiquement sans forcément avoir au préalable écrit
la classe correspondante.
Voici un exemple sur la table Products où l’on souhaite avoir les CategoryId pour chaque SupplierId
var query = from p in context.Products
group p by new { p.CategoryID, p.SupplierID } into productGroupbyCategory
select new
{
CategoryId = productGroupbyCategory.Key.CategoryID,
SupplierID = productGroupbyCategory.Key.SupplierID
};
Agrégations
L’idée ici est de voir les fonctions d’agrégations avec LINQ :
Moyenne (Average) :
var query = from orderDetail in context.Order_Details
group orderDetail by orderDetail.ProductID into groupProd
select new
{
ProductId = groupProd.Key,
AverageQuantity = groupProd.Average(od => od.Quantity)
};
foreach (var p in query.ToList())
Console.WriteLine(p.ProductId + " " + p.AverageQuantity);
Cumul (Count) :
var query = from orderDetail in context.Order_Details
group orderDetail by orderDetail.ProductID into groupProd
join product in context.Products on groupProd.Key equals product.ProductID
orderby product.ProductName ascending
select new
{
ProductId = groupProd.Key,
ProductName = product.ProductName,
Quantity = groupProd.Count()
};
Notez l’utilisation d’une jointure explicite et d’un order by pour plus de clareté.
Maximum (Max) :
var query = from orderDetail in context.Order_Details
group orderDetail by orderDetail.ProductID into groupProd
select new
{
ProductId = groupProd.Key,
MaxQuantity = groupProd.Max(od => od.Quantity)
};
L’objet retourné n’est pas une List<Customer> mais un objet Customer simple.
Attention : Si il n’existe pas de ligne en base, la méthode First() va lever une exception. Si vous n’êtes
pas sûr de récupérer un enregistrement, préférez utiliser la méthode FirstOrDefault() et la
vérification de la nullité via une instruction if :
using (NorthwindEntities context = new NorthwindEntities())
{
var query = from c in context.Customers
where c.ContactName.Contains("Lioooooo")
select c;
if (customer != null)
Console.WriteLine(customer.CustomerID + " " + customer.ContactName);
}
Code First
Le mode Code First permet de créer un contexte EF avec des entités déjà existantes. Si nous
reprenons le code du cours n° 2, nous avons déjà les entités mappées de notre base de données.
Ce sont des entités que nous avons déjà écrites en code C#.
L’idée ici c’est de se passer de toute la tuyauterie Service que nous avons écrit (CustomerServices)
avec les appels aux objets ADO.NET bas niveau comme SqlConnection, SqlCommand, etc….
DbContext
Un objet DbContext est très similaire à l’objet généré dans le modèle Database First.
Il hérite de DbContext et fédère toutes les entités que nous souhaitons mapper à notre base de
données.
}
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<Product> Products { get; set; }
public virtual DbSet<Region> Regions { get; set; }
public virtual DbSet<Shipper> Shippers { get; set; }
public virtual DbSet<Supplier> Suppliers { get; set; }
public virtual DbSet<Territory> Territories { get; set; }
La classe générique DbSet<T> permet de créer des collections d’objets, et rajoute des méthodes
comme l’ajout la suppression, la recherche par clé primaire etc ….
Dans un prochaine cours, nous allons approfondir l’utilisation d’Entity Framework pour résoudre des
problématiques liés à l’accès aux données en général.
Références EF6
Blog de l’équipe Entity Framework : http://blogs.msdn.com/b/adonet/