developpement:dotnet:linq:linq-to-sql:introduction
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
developpement:dotnet:linq:linq-to-sql:introduction [2015/08/01 01:42] – modification externe 127.0.0.1 | developpement:dotnet:linq:linq-to-sql:introduction [2022/03/15 14:51] (Version actuelle) – supprimée sgariepy | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ====== LINQ to SQL ====== | ||
- | Cette page s' | ||
- | ===== Prise en main rapide ===== | ||
- | |||
- | Pour créer une simple application utilisant LINQ to SQL, voici les étapes : | ||
- | |||
- | - Créer une application (console, Windows Form, WPF ou ASP.NET) avec Visual Studio 2008. | ||
- | - Afficher l' | ||
- | - Ajouter un nouvel élément au projet : **Classes LINQ to SQL**. | ||
- | - Eclater la liste des tables de la base de données et glisser-déposer une table dans le fichier DBML (ie : '' | ||
- | * Pour les exemples avec // | ||
- | * Idéalement toutes les clés étrangères ont été créés dans la BD, ce qui crée automatiquement les associations. | ||
- | - Ajouter, par glisser-déposer, | ||
- | - Ajouter les sprocs directement sur l' | ||
- | |||
- | |||
- | {{: | ||
- | |||
- | ===== Syntaxe illustrée ===== | ||
- | {{: | ||
- | |||
- | Source : [[http:// | ||
- | |||
- | ====== Requêtes CRUD ====== | ||
- | |||
- | Les exemples de code suivants ont besoin de la déclaration de '' | ||
- | |||
- | <code csharp> | ||
- | AdventureWorksDataContext db = new AdventureWorksDataContext(); | ||
- | </ | ||
- | |||
- | Autre exemple qu'il est possible d' | ||
- | <code csharp> | ||
- | AdventureWorksDataContext db = new AdventureWorksDataContext (@" | ||
- | </ | ||
- | |||
- | Aussi, il est préférable d' | ||
- | |||
- | <code csharp> | ||
- | try | ||
- | { | ||
- | // code LINQ... | ||
- | } | ||
- | catch (Exception ex) | ||
- | { | ||
- | throw ex; | ||
- | } | ||
- | </ | ||
- | |||
- | ===== Retrouver ===== | ||
- | Les requêtes pour retrouver les données sont simples. | ||
- | |||
- | ==== Exemples de requêtes ==== | ||
- | |||
- | <code csharp> | ||
- | var p = (from em in db.Employees | ||
- | join edh in db.EmployeeDepartmentHistories | ||
- | on em.HireDate equals edh.StartDate | ||
- | where em.EmployeeID == edh.EmployeeID | ||
- | select new | ||
- | { | ||
- | em.EmployeeID, | ||
- | Date = edh.StartDate | ||
- | }).ToList(); | ||
- | </ | ||
- | |||
- | Retrouver des enregistrements en précisant les Ids dans un tableau ('' | ||
- | <code csharp> | ||
- | List< | ||
- | (from p in this.Database.Personnels | ||
- | where OrgIds.Contains(p.OrgID) select p).ToList(); | ||
- | </ | ||
- | |||
- | === Limiter le nombre de résultats === | ||
- | <code csharp> | ||
- | results = (from e in Employee | ||
- | | ||
- | | ||
- | </ | ||
- | |||
- | |||
- | |||
- | |||
- | ==== Retrouver un seul enregistrement ==== | ||
- | <code csharp> | ||
- | public Product GetProduct(int productID) | ||
- | { | ||
- | AdventureWorksDBDataContext db = new AdventureWorksDBDataContext(); | ||
- | Product product = db.Products.Single(p => p.ProductID == productID); | ||
- | return product; | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | ==== Assigner null à des colonnes ==== | ||
- | |||
- | < | ||
- | La valeur null ne peut pas être assignée à un membre de type System.Int32, | ||
- | </ | ||
- | |||
- | Quand on crée un objet anonyme lors de la sélection LINQ-to-SQL, | ||
- | |||
- | <code csharp> | ||
- | var employe = from e in EMPLOYEs | ||
- | join fe in DOCUMENTs on uc.FORMULAIRE_EMBAUCHE_PK equals fe.DOCUMENT_PK into tempFE | ||
- | from tfe in tempFE.DefaultIfEmpty() | ||
- | select new { | ||
- | Nom = e.NOM, | ||
- | Prenom = e.PRENOM, | ||
- | FormulaireNom = tfe.NOM, | ||
- | FormulaireId = (int?) tfe.DOCUMENT_ID | ||
- | }; | ||
- | </ | ||
- | |||
- | ==== Utiliser une valeur possiblement null dans une clause Where ==== | ||
- | |||
- | Si on veut spécifier une valeur possiblement '' | ||
- | |||
- | <code csharp> | ||
- | public static List< | ||
- | { | ||
- | using (DataContext db = new DataContext()) | ||
- | { | ||
- | var employes = from e in db.EMPLOYES | ||
- | where object.Equals(e.PersonneId, | ||
- | | ||
- | } | ||
- | // etc | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | ==== Sous-requêtes ==== | ||
- | L' | ||
- | |||
- | <code sql> | ||
- | EXISTS( | ||
- | SELECT * | ||
- | FROM query | ||
- | WHERE predicate | ||
- | ) | ||
- | </ | ||
- | |||
- | Par exemple, cette requête : | ||
- | <code csharp> | ||
- | from c in db.Customers | ||
- | where db.Employees.Any(e => e.City == c.City) | ||
- | select c; | ||
- | </ | ||
- | |||
- | |||
- | <code csharp> | ||
- | var subquery = from product in MySandbox.Products | ||
- | where product.Supplier.City.StartsWith(" | ||
- | select product.CategoryID; | ||
- | |||
- | var query = from category in MySandbox.Categories | ||
- | where | ||
- | category.CategoryName.Contains(" | ||
- | && subquery.Contains(category.CategoryID) | ||
- | select category; | ||
- | </ | ||
- | |||
- | |||
- | ==== DataLoadOptions ==== | ||
- | |||
- | L' | ||
- | |||
- | <code csharp> | ||
- | System.Data.Linq.DataLoadOptions options = new System.Data.Linq.DataLoadOptions(); | ||
- | options.LoadWith< | ||
- | db.LoadOptions = options; | ||
- | Employee x = db.Employees.Single(r => r.EmployeeID == 1); | ||
- | Debug.Print(x.Contact.FirstName); | ||
- | </ | ||
- | |||
- | ==== AssociateWith ==== | ||
- | |||
- | <code csharp> | ||
- | System.Data.Linq.DataLoadOptions options = new System.Data.Linq.DataLoadOptions(); | ||
- | options.AssociateWith< | ||
- | (r=> | ||
- | db.LoadOptions = options; | ||
- | foreach (Employee emp in db.Employees) | ||
- | { | ||
- | foreach (EmployeeDepartmentHistory edh in emp.EmployeeDepartmentHistories) | ||
- | { } | ||
- | } | ||
- | </ | ||
- | ==== Concaténation et jointure ==== | ||
- | Le code suivant montre comment créer une requête LINQ un peu plus // | ||
- | |||
- | <code csharp> | ||
- | protected void GridView1_Selecting(object sender, LinqDataSourceSelectEventArgs e) | ||
- | { | ||
- | CustomDataContext db = new CustomDataContext(); | ||
- | var requestList = from t1 in db.NomTable1 | ||
- | join t2 in db.NomTable2 on t1.IdNomTable2 equals t2.IdNomTable2 | ||
- | join t3 in db.NomTable3 on t1.IdNomTable3 equals t3.IdNomTable3 | ||
- | select new { | ||
- | Id = t1.IdNomTable1, | ||
- | t2.Field1, | ||
- | t3.Field1, | ||
- | Nom = t1.Prenom + " " + t1.Nom, | ||
- | Description = t2.Description, | ||
- | }; | ||
- | e.Result = requestList; | ||
- | } | ||
- | </ | ||
- | |||
- | Note : Il est préférable de mettre '' | ||
- | |||
- | === Jointure avec méthode chainée === | ||
- | |||
- | <code csharp> | ||
- | var query = db.Companies.Where(c => c.CompanyName == companyName) | ||
- | .Join(db.Persons, | ||
- | .Select(p => new | ||
- | { | ||
- | Id = p.PersonId, | ||
- | Name = string.Format(" | ||
- | }); | ||
- | </ | ||
- | |||
- | [[http:// | ||
- | |||
- | |||
- | === Jointure avec clé composite === | ||
- | |||
- | <code csharp> | ||
- | var query = from o in db.Orders | ||
- | from p in db.Products | ||
- | join d in db.OrderDetails | ||
- | on new {o.OrderID, p.ProductID} equals new {d.OrderID, | ||
- | d.ProductID} into details | ||
- | from d in details | ||
- | select new {o.OrderID, p.ProductID, | ||
- | </ | ||
- | -> Exemple avec la base de données Northwind. | ||
- | |||
- | === Jointure avec DefaultIfEmpty === | ||
- | |||
- | <code csharp> | ||
- | join ea in EmployesAdresse on em.IdEmploye equals ea.IdEmploye into tempAdresse | ||
- | from EmployeAdresse in tempAdresse.DefaultIfEmpty() | ||
- | </ | ||
- | |||
- | === Jointure sur plusieurs colonnes === | ||
- | |||
- | <code csharp> | ||
- | var query = from s in context.ShoppingMalls | ||
- | join h in context.Houses | ||
- | on | ||
- | new { s.CouncilCode, | ||
- | equals | ||
- | new { h.CouncilCode, | ||
- | select s; | ||
- | </ | ||
- | |||
- | * [[http:// | ||
- | ==== Groupement ==== | ||
- | |||
- | <code csharp> | ||
- | var cities = from c in Customers | ||
- | group c.ContactName by new {c.City, c.ContactTitle} into g | ||
- | where g.Count() > 1 | ||
- | select g; | ||
- | </ | ||
- | |||
- | <code csharp> | ||
- | var count = from r in result | ||
- | group r by r.CODE into g | ||
- | orderby g.Count() descending | ||
- | select new { | ||
- | code = g.Key, | ||
- | count = g.Count() | ||
- | }; | ||
- | count.Dump(); | ||
- | </ | ||
- | |||
- | |||
- | ---- | ||
- | \\ | ||
- | Pour regrouper dans une même colonne les données non-groupées par '' | ||
- | |||
- | {{: | ||
- | |||
- | On voit que la colonne '' | ||
- | |||
- | <code csharp> | ||
- | var result = | ||
- | collection.GroupBy(x => new { x.IdPlace, x.IdInternship }) | ||
- | .Select(x => new { | ||
- | | ||
- | | ||
- | | ||
- | }); | ||
- | </ | ||
- | |||
- | ==== Colonne null ==== | ||
- | |||
- | S'il y a une colonne null dans la requête LINQ, par une jointure (**LEFT JOIN**) qui peut possiblement ne pas avoir d' | ||
- | La valeur null ne peut pas être assignée à un membre de type System.< | ||
- | |||
- | Il faut alors //caster// par un type nullable. | ||
- | |||
- | <code csharp> | ||
- | select new | ||
- | { | ||
- | Id = (int?) tRE.ID, | ||
- | Date = (DateTime?) tRE.DATE | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | ===== Insérer ===== | ||
- | Dans certains exemples trouvés sur Internet et dans des livres de références, | ||
- | |||
- | <code csharp> | ||
- | Department dept = new Department(); | ||
- | dept.Name = txtDepartmentName.Text; | ||
- | dept.GroupName = txtGroupName.Text; | ||
- | dept.ModifiedDate = DateTime.Now; | ||
- | db.Department.InsertOnSubmit(dept); | ||
- | db.SubmitChanges(); | ||
- | MessageBox.Show(" | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ===== Mise à jour ===== | ||
- | <code csharp> | ||
- | Employee emp = db.Employee.Single(r => r.Contact.LastName == txtLastName.Text); | ||
- | emp.VacationHours = short.Parse(txtVacationHours.Text); | ||
- | db.SubmitChanges(); | ||
- | MessageBox.Show(" | ||
- | </ | ||
- | |||
- | Pour régler les conflits potentiels : | ||
- | <code csharp> | ||
- | try { | ||
- | db.SubmitChanges(); | ||
- | } | ||
- | catch (ChangeConflictException) { | ||
- | db.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges); | ||
- | } | ||
- | </ | ||
- | |||
- | ===== Supprimer ===== | ||
- | |||
- | Tout comme l' | ||
- | |||
- | <code csharp> | ||
- | Department dept = db.Department.Single(d => d.Name == txtDepartmentName.Text); | ||
- | db.Department.DeleteOnSubmit(dept); | ||
- | db.SubmitChanges(); | ||
- | MessageBox.Show(" | ||
- | </ | ||
- | |||
- | |||
- | |||
- | |||
- | ===== Lier une procédure stockée à une opération CUD ===== | ||
- | - Créer une procédure stockée au besoin. | ||
- | CREATE PROCEDURE dbo.uspUpdateDepartment | ||
- | @Name varchar(50), | ||
- | @GroupName varchar(50), | ||
- | @ModifiedDate datetime | ||
- | AS | ||
- | UPDATE HumanResources.Department | ||
- | SET [Name] = @Name, | ||
- | [GroupName] = @GroupName, | ||
- | [ModifiedDate] = @ModifiedDate | ||
- | RETURN | ||
- | </ | ||
- | - Glisser-déposer la procédure stockée sur la liste des méthodes du fichier DBML. | ||
- | - Aller dans les propriétés de l' | ||
- | - Associer la propriété //Delete//, //Insert// ou //Update// avec la méthode ajoutée précédemment. | ||
- | |||
- | ====== Opérateurs d' | ||
- | * Union -> Retourne les éléments du premier ensemble ajouté au deuxième sans les doublons. | ||
- | * Distinct -> Enlève les doublons des ensembles. | ||
- | * Concat -> Retourne les éléments du premier ensemble ajouté au deuxième avec les doublons. | ||
- | * Intersect -> Retourne les éléments communs aux deux ensembles. | ||
- | * Except -> Enlève les éléments d'un ensemble qui se trouve dans un autre. | ||
- | |||
- | |||
- | ===== Union ===== | ||
- | <code csharp> | ||
- | var q = ( | ||
- | from c in db.Customers | ||
- | select c.Country | ||
- | ).Union( | ||
- | from e in db.Employees | ||
- | select e.Country | ||
- | ); | ||
- | </ | ||
- | |||
- | ===== Distinct ===== | ||
- | |||
- | |||
- | ===== Concat ===== | ||
- | |||
- | <code csharp> | ||
- | var q = ( | ||
- | from c in db.Customers | ||
- | select c.Phone | ||
- | ).Concat( | ||
- | from c in db.Customers | ||
- | select c.Fax | ||
- | ).Concat( | ||
- | from e in db.Employees | ||
- | select e.HomePhone | ||
- | ); | ||
- | </ | ||
- | |||
- | ===== Intersect ===== | ||
- | <code csharp> | ||
- | var q = ( | ||
- | from c in db.Customers | ||
- | select c.Country | ||
- | ).Intersect( | ||
- | from e in db.Employees | ||
- | select e.Country | ||
- | ); | ||
- | </ | ||
- | ===== Except ===== | ||
- | <code csharp> | ||
- | var q = ( | ||
- | from c in db.Customers | ||
- | select c.Country | ||
- | ).Except( | ||
- | from e in db.Employees | ||
- | select e.Country | ||
- | ); | ||
- | </ | ||
- | |||
- | Source : [[http:// | ||
- | |||
- | ====== Énumération des tables de référence ====== | ||
- | |||
- | |||
- | ^ EmployeeType ^^ | ||
- | | Id | int | | ||
- | | Value | varchar(50) | ||
- | |||
- | <code csharp> | ||
- | public class EmployeeType | ||
- | { | ||
- | public static EmployeeType FullTime; | ||
- | public static EmployeeType PartTime; | ||
- | public static EmployeeType Occasional; | ||
- | |||
- | static EmployeeType() | ||
- | { | ||
- | var lookup = (from t in CustomDataContext.GetInstance().EmployeeType | ||
- | select t).ToDictionary(i => t.Value); | ||
- | |||
- | FullTime = lookup[" | ||
- | PartTime = lookup[" | ||
- | Occasional = lookup[" | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | ====== DataContext avec Singleton ====== | ||
- | |||
- | <code csharp> | ||
- | public partial class CustomDataContext : System.Data.Linq.DataContext | ||
- | { | ||
- | private static CustomDataContext _instance; | ||
- | |||
- | public static CustomDataContext GetInstance() | ||
- | { | ||
- | if (_instance != null) | ||
- | { | ||
- | return _instance; | ||
- | } | ||
- | else | ||
- | { | ||
- | _instance = new CustomDataContext(); | ||
- | return _instance; | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ====== Debugging ====== | ||
- | |||
- | Pour déboguer le code SQL généré par LINQ, il peut être utile d' | ||
- | |||
- | - Télécharger le fichier '' | ||
- | - Dans le fichier ZIP, extraire le fichier '' | ||
- | - Copier ce fichier dans '' | ||
- | |||
- | Dans Visual Studio il suffit de mettre un point d' | ||
- | |||
- | {{: | ||
- | |||
- | En cliquant sur la loupe, on obtient la fenêtre **SQL Server Query Visualizer**. | ||
- | |||
- | {{: | ||
- | ===== Profilers ===== | ||
- | * [[http:// | ||
- | * [[http:// | ||
- | ====== LINQ-to-SQL de base ====== | ||
- | |||
- | Il est possible d' | ||
- | |||
- | Dans le code suivant, on spécifie le nom de la table ('' | ||
- | |||
- | <code csharp> | ||
- | using System; | ||
- | using System.Collections.Generic; | ||
- | using System.Linq; | ||
- | using System.Text; | ||
- | using System.Data.Linq; | ||
- | using System.Data.Linq.Mapping; | ||
- | |||
- | namespace DomainModel.Entities | ||
- | { | ||
- | [Table(Name = " | ||
- | public class Product | ||
- | { | ||
- | [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)] | ||
- | public int ProductID { get; set; } | ||
- | |||
- | [Column] public string Name { get; set; } | ||
- | [Column] public string Description { get; set; } | ||
- | [Column] public decimal Price { get; set; } | ||
- | [Column] public string Category { get; set; } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | Et le // | ||
- | |||
- | <code csharp> | ||
- | using System; | ||
- | using System.Collections.Generic; | ||
- | using System.Linq; | ||
- | using System.Text; | ||
- | using System.Data.Linq; | ||
- | using DomainModel.Entities; | ||
- | |||
- | namespace DomainModel.Concrete | ||
- | { | ||
- | class SqlProductsRepository | ||
- | { | ||
- | private Table< | ||
- | public SqlProductsRepository(string connectionString) | ||
- | { | ||
- | productsTable = (new DataContext(connectionString)).GetTable< | ||
- | } | ||
- | public IQueryable< | ||
- | { | ||
- | get { return productsTable; | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | On peut utiliser dans un contrôleur : | ||
- | |||
- | <code csharp> | ||
- | using System; | ||
- | using System.Collections.Generic; | ||
- | using System.Linq; | ||
- | using System.Web; | ||
- | using System.Web.Mvc; | ||
- | using DomainModel.Abstract; | ||
- | using DomainModel.Concrete; | ||
- | |||
- | namespace WebUI.Controllers | ||
- | { | ||
- | public class ProductsController : Controller | ||
- | { | ||
- | private IProductsRepository productsRepository; | ||
- | public ProductsController() | ||
- | { | ||
- | string connString = @" | ||
- | productsRepository = new SqlProductsRepository(connString); | ||
- | } | ||
- | |||
- | public ViewResult List() | ||
- | { | ||
- | return View(productsRepository.Products.ToList()); | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | * Source : //Pro ASP.NET MVC Framework//, | ||
- | |||
- | ====== MetaModel ====== | ||
- | |||
- | |||
- | <code csharp> | ||
- | /// < | ||
- | /// Cette méthode remet les IDENTITY SEED à 0 pour les tables présentes dans le DataContext. | ||
- | /// </ | ||
- | public static void ReseedAllTables() | ||
- | { | ||
- | using (DataContext db = new DataContext()) | ||
- | { | ||
- | var model = db.Mapping; | ||
- | foreach (var mt in model.GetTables()) | ||
- | { | ||
- | db.ExecuteCommand(" | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | <code csharp> | ||
- | Northwind db = ...from somewhere | ||
- | var model = db.Mapping; | ||
- | foreach (var mt in model.GetTables()) | ||
- | { | ||
- | Console.WriteLine(mt.TableName); | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | Retrouver les noms de colonnes : | ||
- | |||
- | <code csharp> | ||
- | var model = new AttributeMappingSource().GetModel(typeof(Northwind)); | ||
- | foreach (var mt in model.GetTables()) { | ||
- | Console.WriteLine(mt.TableName); | ||
- | foreach (var dm in mt.RowType.DataMembers) | ||
- | Console.WriteLine(" | ||
- | </ | ||
- | |||
- | * [[http:// | ||
- | |||
- | |||
- | ====== Insérer un fichier ====== | ||
- | |||
- | |||
- | <code csharp> | ||
- | string inputFilename = @" | ||
- | |||
- | byte[] fileBytes = File.ReadAllBytes(inputFilename); | ||
- | |||
- | Fichiers.InsertOnSubmit(new Fichier { | ||
- | Contenu = fileBytes, | ||
- | Nom = " | ||
- | DateCreation = DateTime.Now, | ||
- | Taille = fileBytes.Count() | ||
- | }); | ||
- | |||
- | SubmitChanges(); | ||
- | </ | ||
- | |||
- | ====== Ressources ====== | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - LINQ to XML | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | ===== Code generation ===== | ||
- | |||
- | * [[http:// | ||
- | * [[http:// | ||
- | |||
- | ====== Sources ====== | ||
- | - P. MEHTA, Vijay, //Pro LINQ Object Relational Mapping with C%%#%% 2008//, Apress, 2008. | ||
- | - C. RATTZ, Joseph, //Pro LINQ - Language Integrated Query in C-Sharp 2008//, Apress, 2007. | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// | ||
- | - [[http:// |
developpement/dotnet/linq/linq-to-sql/introduction.1438386138.txt.gz · Dernière modification : 2022/02/02 00:43 (modification externe)