Mon monde en .NET - LINQ2023-01-02T14:25:42+01:00Thierry Thouaurn:md5:feacb53e7f5d22db05e2d6025a495e81DotclearLINQ to SQL (detach / re attach dans un modèle N-tiers)urn:md5:3271a2def4b05de1bd0d4d72896ad9432008-06-24T21:54:00+00:002008-06-29T17:19:18+00:00Thierry ThouaLINQLorsque l'on commence à réellement utiliser Linq to SQL, on en arrive vite à se prendre la tête sur certaines parties. Je vais vous présenter ici la problématique d'update d'un objet récupéré via différents DataContext. En effet, il n'est pas simple de faire fonctionner LINQ to SQL avec une instance de DataContext différente pour le "GET" et l' "UPDATE". Voici une solution pour arriver à utiliser les entités en mode "détaché".
<ul>
<li>Gestion du versonning des tuples</li>
Il est indispensable d'avoir un système de versionning de tuple pour vérifier que l'objet a des modifications // aux données en base de données. Il suffit d'ajouter un champ "timestamp" en DB et de positionner les options comme ci-dessous.<br />
<img src="http://www.lelibre.net/download/106/update_policy.png" alt="" />
<li>Méthode Detach sur l'entité</li>
Bizarrement, cette méthode n'existe plus (elle était disponible dans certaines bêta :/) dans le code généré. Il est cependant possible de l'implémenter à la main.
<pre>public partial class Bill<br />{<br />public void Detach()<br />{<br />this.PropertyChanged = null;<br />this.PropertyChanging = null;<br />this.BillItems = default(EntitySet<billItem>);<br /> }<br />}<br /><br /></billItem></pre>
<li>Exemple</li>
<pre>TestDataContext context = new TestDataContext();<br />Bill cust = context.Bills.Single(c => c.Id == 1);<br />context = null;<br />cust.Name = DateTime.Now.ToShortTimeString();<br />cust.Detach();<br />TestDataContext context2 = new TestDataContext();<br />context2.Bills.Attach(cust, true);<br />context2.SubmitChanges();</pre>
</ul>
Quid de Entity Framework ?<br />
Je vous conseille ce <a href="http://blogs.codes-sources.com/matthieu/archive/2008/06/05/les-relations-avec-l-entity-framework-probl-matique-d-ajout-d-entit.aspx">lien</a> pour répondre à une partie de cette question.Npgsql2 Beta3 est sorti !!!urn:md5:12fe75b36d3f0937c62e476c03dcb3972008-04-07T22:35:36+00:002008-04-07T22:35:36+00:00Thierry ThouaLINQ Npgsql2 Beta3 est sorti et support à présent (toujours en bêta) Entity Framework pour Postgresql. Il s'agit là de la première version ... Je vous invite donc à le tester et ainsi contribuer à améliorer la qualité de ce driver ;-). Voici le <a href="http://pgfoundry.org/frs/download.php/1730/Npgsql2.0beta3-bin-EntityFrameworkBeta3.zip">lien</a>.LINQ to SQL (locking)urn:md5:1b3d9ea821cf2bc474e67b59b846c64e2008-03-19T22:05:13+00:002008-03-19T22:05:13+00:00Thierry ThouaLINQ Il y a quelques jours, j'ai testé la gestion du "lock" sous Linq to SQL en cas de modification de tuple pendant la manipulation de l'objet chargé. J'avais rédigé un mini commentaire <a href="http://www.lelibre.net/index.php?2008/03/17/86-acces-et-lock-de-tuples-sous-linq-to-sql">ici</a>. Après avoir vu la remarque de Steve, j'ai décidé d'aller plus loin dans LINQ to SQL et sa gestion du lock. Après avoir réutilisé mon exemple, voici quelques remarques.<br/>
<p>Par défaut, lors d'une mise à jour d'un tuple, Linq2SQL va utiliser tous les champs dans sa clause WHERE. Il est cependant possible de modifier ce comportement via un paramètre "UpdateCheck" nommé de l'attribut ColumnAttribute qui se place au dessus des propriétés ... Ce paramètre accepte un enum qui a trois valeurs.
<ol>
<li>Never => La propriété n'entre jamais en ligne de compte.</li>
<li>Allways => (valeur par défaut). La propriété entre toujours en ligne de compte dans un update.</li>
<li>WhenChanged => La propriété n'est prise en compte que si elle est modifiée.</li>
</ol>
Cela offre une grande souplesse, il est donc possible de faire un lock sur un champ d'une table .. par exemple un timestamp ... et les autres champs prendront "never".
<br/><br/><br/>
L'autre possibilité est le "pessimistic locking" (désolé ... je ne connais pas le terme en français ;-). Il est possible de bloquer un tuple le temps d'une modification. Ceci ne m'a jamais semblé une bonne idée ... Cela ajoute un lag voir des problèmes d'accès en cas de plantage etc. Mais il est intéressant d'avoir connaissance de l'existence de cette possibilité dans LINQ to SQL.</p>Accès/modification de tuples en // sous Linq to SQL (lock ?)urn:md5:e7fe4d934f4cd604b5a91f4568a155132008-03-17T21:06:25+00:002008-03-17T21:09:24+00:00Thierry ThouaLINQ Aujourd'hui, je me suis demandé comment fonctionnait le lock des entités sous LINQ to SQL. Sous NHibernate, il existe des mécanismes de lock via un champ timestamp/version dans la table ... Mais qu'en est-il sous LINQ to SQL ... Simple ... clair ... net ... Par défaut tout est géré nativement et une exception se lance en cas de modification d'un tuple qui a été modifié par un autre DataContext ou via la base de données ...<br/><br/>
<center>
<img src="http://www.lelibre.net/download/86/exception_dlinq.png"/>
</center>Log des requêtes SQL effectuées par DLINQurn:md5:7119d464ae69b4f0d80f4a7c97ef4afb2008-02-21T21:52:34+00:002008-02-21T21:52:34+00:00Thierry ThouaLINQ Il est parfois intéressant de pouvoir tracer les requêtes SQL générées par n'importe quelle bibliothèque afin de pouvoir optimiser certaines choses mais comment le faire sous LINQ ? Voici la réponse ... Il suffit d'utiliser un TextWriter et de positionner la propriété Log de l'objet DataContext .... Et pour faire simple ... il est tout à fait possible de le faire sortir directement sous la console en sortie en utilisant "Console.Out" qui renvoie un TextWriter ;-)Pagination sous SQL Serverurn:md5:aa54685e0959777357246ed01af3e3972008-02-21T21:32:15+00:002008-02-21T21:35:37+00:00Thierry ThouaLINQ Depuis quelques temps, dans le cadre du développement des bibliothèques Lelibre.Fwk, j'étais à la recherche de la solution pour supporter la pagination sous SQL Server ... Après, quelques tests et autres demandes, j'ai trouvé la solution ^_^. Je vous la donne. J'ai par après eu l'idée de valider ou améliorer celle-ci en regardant ce qu'executait LINQ lorsque je demandais une "requête" ....
<pre>[csharp]
var q = (from c in dataContext.Blog select c).Skip(5).Take(10);
</pre>
Et voici le code SQL généré:
<pre>
SELECT [t1].[BlogID], [t1].[Title]
FROM (
SELECT ROW_NUMBER() OVER
(ORDER BY [t0].[BlogID], [t0].[Title]) AS [ROW_NUMBER], [t0].[BlogID], [t0].[Title]
FROM [Blogs] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]
</pre>
En SQL simple, on aurait également pu effectuer un query du type:
<pre>
SELECT TOP 10 * FROM (SELECT ROW_NUMBER() OVER (ORDER BY BlogID, Title) AS RowNumber, *
FROM Blogs
WHERE RowNumber > 5
</pre>
Cependant, je reste d'avis qu'il aurait été plus sympathique de pouvoir utiliser LIMIT / OFFSET ;-).DLinq 1..N, la base des relations + selecturn:md5:10a8ac4c11f8c6a13dcbf560491f8ad22007-10-18T18:40:25+00:002007-10-18T18:40:25+00:00Thierry ThouaLINQComme expliqué dans un des mes précédents <a href="http://www.lelibre.net/index.php?2007/09/23/32-dlinq-c-est-magique">post</a>, il y a une nouvelle technologie qui sortira en .NET 3.5. Ainsi, je me suis décidé à réaliser un mini exemple en SQL Server pour un peu expliquer comment fonctionne la base des select en LINQ. Tout développement "propre" en DLINQ repose sur une classe manager qui est détaillée ci-dessous. Cette classe offre tous les mécanismes classiques que l'on utilise en ado.net et sera la classe utilisée pour effectuer des changements ou appliquer ceux-ci a la base de données.
<pre>[csharp]
public partial class DBManager : DataContext
{
public Table<Blog> Blog;
public Table<Post> Post;
public DBManager(IDbConnection connection)
: base(connection)
{
}
}
</pre>
Après cette classe "manager", il faut définir les classes "modèles" qui contiendront les informations récupérées de la DB,... On mappe chaque propriété/champ sur une colonne. On y définit également les relations. Celles-ci se reposent sur des nouvelles classes ajoutées en .net 3.5 à savoir EntityRef et EntitySet.
<pre>[csharp]
[Table(Name = "Blogs")]
public class Blog
{
[Column(IsPrimaryKey = true)]
public int BlogID;
[Column]
public string Title;
[Association(Storage = "m_posts", OtherKey = "PostID")]
public EntitySet<Post> Posts
{
get { return this.m_posts; }
set { this.m_posts.Assign(value); }
}
private EntitySet<Post> m_posts = new EntitySet<Post>();
}
[Table(Name = "Post")]
public class Post
{
[Column(IsPrimaryKey = true)]
public int PostID;
[Column]
public string Comment;
[Column]
public int BlogID;
[Association(Storage = "m_blog", ThisKey = "BlogID")]
public Blog Blog
{
get { return this.m_blog.Entity; }
set { this.m_blog.Entity = value; }
}
private EntityRef<Blog> m_blog;
}
</pre>
Exemple de query:
<pre>[sql]
var d = from c in dataContext.Post select c;
var posts2 = from c in dataContext.Post where c.Comment.Contains("omment") orderby c.BlogID descending, c.PostID descending select c;
</pre>
Par défaut, la valeur renvoyé d'un select est de type IEnumerable<T>. Il est possible de transformer le type de retour en y appliquant par exemple la méthode "ToList" ou "ToArray".
<pre>[csharp]
List<Post> posts2List = posts2.ToList<Post>();
</pre>
Si vous voulez plus d'informations sur les différents select, etc. Je vous conseille ce <a href="http://msdn2.microsoft.com/fr-fr/library/bb425822.aspx">lien</a>.DLinq, c'est magique ?urn:md5:95b269878d2df5efef4b41784e2361602007-09-23T00:58:42+00:002007-10-02T12:04:49+00:00Thierry ThouaLINQEtant sur un nouveau projet associatif, je me suis tourné depuis quelques semaines dans la découverte et l'utilisation de DLINQ. D'entrée, j'ai été confronté à un problème que je juge de taille... En effet, DLinq ne supporte actuellement que SQL Server (et déclinaison Express) ainsi que Access (que je n'ai pas réussi à faire fonctionner ...). La question qui me vient directement à l'esprit ... Et les autres ??? On fait quoi ??? ... Heureusement qu'il existe un petit <a href="http://code2code.net/DB_Linq/">projet</a> en développement qui fournit un connecteur pour MySQL, Oracle et PostgreSQL. Et heureusement que je partais d'un existant nul .... ;-). J'espère que Mr Microsoft implémenera ces classes pour d'autres SGBD. Je profite de ce post pour donner un petit conseil ;-). L'utilisation d'un DataContext permet de récuperer les données etc. Il est tout à fait possible de ne pas devoir récuperer a chaque fois l'objet Table<IEntity>. En effet, il suffit d'implémenter une classe "custom" héritant de l'objet DataContext et ayant en champ public les différentes tables. La classe sera initialisée une fois pour toute si on garde une référénce au datacontext.
<br/><br/>
Voici un exemple:<br/>
<br/>
Au lieu de :<br/>
<pre>[csharp]
Table<Blog> blogs = dataContext.GetTable<Blog>();
</pre>
<br/>
Il est possible de faire :
<pre>[csharp]
public class DBManager : DataContext
{
public Table<Blog> Blog;
public Table<Post> Post;
public DBManager(IDbConnection connection)
: base(connection)
{
}
}
</pre>