[.NET] DataTable ou DataAdapter et update/insert -goto en C# oo-

DataTable ou DataAdapter et update/insert -goto en C# oo- [.NET] - C#/.NET managed - Programmation

Marsh Posté le 10-07-2006 à 19:36:26    

Bonjour,
 
A la façon d'un RecordSet avec ADODB, je voudrais pouvoir modifier/ajouter des données dans un DataTable retourné par un "select ...".
 
Voici mon code pour le moment (ça ne fait que le select) :
 

Code :
  1. cmd.CommandText = sql;
  2.             cmd.CommandType = CommandType.Text;
  3.             cmd.Prepare();
  4.             DataTable dt = new DataTable();
  5.             SqlDataAdapter da = new SqlDataAdapter(cmd);
  6.             da.Fill(dt);
  7.             foreach (DataRow row in dt.Rows)
  8.             {
  9.                 XmlNode rLine = outputNode.OwnerDocument.CreateElement("line" );
  10.                 foreach (DataColumn col in dt.Columns)
  11.                 {
  12.                     // ICI, je veux pouvoir modifier les données de la cellule en cours de lecture
  13.                     XmlElement rNode = rLine.OwnerDocument.CreateElement(col.ColumnName);
  14.                     rNode.InnerXml = row[col].ToString();
  15.                     rLine.AppendChild(rNode);
  16.                 }
  17.                 outputNode.AppendChild(rLine);
  18.                 // For each row, we can have embeded views
  19.                 if (view.SelectNodes("views/view" ) != null)
  20.                 {
  21.                     RenderViews(outputNode, view.SelectNodes("views/view" ), row);
  22.                 }
  23.             }
  24.             // ICI, je veux pouvoir ajouter une ligne
  25.             dt.Dispose();
  26.             da.Dispose();
  27.             cmd.Dispose();


 
Je ne connais pas la structure de la table lue. Cependant, j'utilise SQL Server, qui permet tout à fait de modifier des données retournées dans un select (via des mises à jours sur curseur).
Je dois modifier à la fois les données de mon DataTable, ainsi que celle de la table lue.


Message édité par Arjuna le 12-07-2006 à 22:29:48
Reply

Marsh Posté le 10-07-2006 à 19:36:26   

Reply

Marsh Posté le 10-07-2006 à 21:05:50    

Youpi tralala !
 
Ca marche :)
 

Code :
  1. cmd.CommandText = sql;
  2.             cmd.CommandType = CommandType.Text;
  3.             cmd.Prepare();
  4.             DataTable dt = new DataTable();
  5.             SqlDataAdapter da = new SqlDataAdapter(cmd);
  6.             da.Fill(dt);
  7.            
  8.             bool addRow = false;
  9.             DataRow newRow = null;
  10.             foreach (string param in Request.Params.Keys)
  11.             {
  12.                 if (param.StartsWith(string.Format("add:{0}-", viewName)))
  13.                 {
  14.                     addRow = true;
  15.                     break;
  16.                 }
  17.             }
  18.             if (addRow)
  19.             {
  20.                 bool notNullRow = false;
  21.                 newRow = dt.NewRow();
  22.                 foreach (DataColumn col in dt.Columns)
  23.                 {
  24.                     foreach (string param in Request.Params.Keys)
  25.                     {
  26.                         if (param == string.Format("add:{0}-{1}", viewName, col.ColumnName))
  27.                         {
  28.                             switch (col.DataType.Name)
  29.                             {
  30.                                 case "String":
  31.                                     if (Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString() != string.Empty)
  32.                                     {
  33.                                         notNullRow = true;
  34.                                         newRow[col] = Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString();
  35.                                     }
  36.                                     break;
  37.                             }
  38.                             break;
  39.                         }
  40.                     }
  41.                 }
  42.                 if (notNullRow)
  43.                 {
  44.                     dt.Rows.Add(newRow);
  45.                 }
  46.                 else
  47.                 {
  48.                     newRow = null;
  49.                 }
  50.             }
  51.             bool delRow = false;
  52.             foreach (string param in Request.Params.Keys)
  53.             {
  54.                 if (param == string.Format("del:{0}", viewName))
  55.                 {
  56.                     delRow = true;
  57.                     break;
  58.                 }
  59.             }
  60.             if (delRow)
  61.             {
  62.                 string[] param = Request.Params[string.Format("del:{0}", viewName)].Split(',');
  63.                 foreach(string id in param)
  64.                 {
  65.                     dt.Rows[int.Parse(id) - 1].Delete();
  66.                 }
  67.             }
  68.             int cptLine = 0;
  69.             foreach (DataRow row in dt.Rows)
  70.             {
  71.                 cptLine++;
  72.                 if (row.RowState != DataRowState.Deleted)
  73.                 {
  74.                     XmlNode rLine = outputNode.OwnerDocument.CreateElement("line" );
  75.                     foreach (DataColumn col in dt.Columns)
  76.                     {
  77.                         foreach (string param in Request.Params.Keys)
  78.                         {
  79.                             if (param == string.Format("upd:{0}-{1}-{2}", viewName, col.ColumnName, cptLine.ToString()))
  80.                             {
  81.                                 switch (col.DataType.Name)
  82.                                 {
  83.                                     case "String":
  84.                                         if (row[col] != Request.Params[string.Format("upd:{0}-{1}-{2}", viewName, col.ColumnName, cptLine.ToString())].ToString())
  85.                                         {
  86.                                             row[col] = Request.Params[string.Format("upd:{0}-{1}-{2}", viewName, col.ColumnName, cptLine.ToString())].ToString();
  87.                                         }
  88.                                         break;
  89.                                 }
  90.                                 break;
  91.                             }
  92.                         }
  93.                         XmlElement rNode = rLine.OwnerDocument.CreateElement(col.ColumnName);
  94.                         rNode.InnerXml = row[col].ToString();
  95.                         XmlAttribute rAtt = rLine.OwnerDocument.CreateAttribute("type" );
  96.                         rAtt.Value = col.DataType.Name;
  97.                         rNode.Attributes.Append(rAtt);
  98.                         rLine.AppendChild(rNode);
  99.                     }
  100.                     outputNode.AppendChild(rLine);
  101.                     // For each row, we can have embeded views
  102.                     if (view.SelectNodes("views/view" ) != null)
  103.                     {
  104.                         RenderViews(outputNode, view.SelectNodes("views/view" ), row);
  105.                     }
  106.                 }
  107.             }
  108.             DataTable updDataTabe = dt.GetChanges();
  109.             if (updDataTabe != null)
  110.             {
  111.                 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(da);
  112.                 if (newRow != null)
  113.                 {
  114.                     da.InsertCommand = cmdBuilder.GetInsertCommand();
  115.                 }
  116.                 if (delRow)
  117.                 {
  118.                     da.DeleteCommand = cmdBuilder.GetDeleteCommand();
  119.                 }
  120.                 da.UpdateCommand = cmdBuilder.GetUpdateCommand();
  121.                 da.Update(updDataTabe);
  122.                 cmdBuilder.Dispose();
  123.                 cmdBuilder = null;
  124.             }
  125.             dt.Dispose();
  126.             da.Dispose();
  127.             cmd.Dispose();


 
Merci bien à ce lien :
http://samples.gotdotnet.com/quick [...] romDB.aspx
 
--edit--
1/ Mise à jour du code : maintenant, seules les lignes non vides sont ajoutées. Deplus, seules les lignes réellement modifiées sont mises à jour.
2/ Nouvelle mise à jour : maintenant on peut aussi supprimer des lignes


Message édité par Arjuna le 10-07-2006 à 22:22:42
Reply

Marsh Posté le 12-07-2006 à 14:48:22    

Bon, je me permet de remonter ce topic en réouvrant la question, puisque ma solution actuelle n'est pas satisfaisante.
 
En effet, retrouver les lignes dans mon datatable via leur "position" n'est pas satisfaisant, dans la mesure où, pendant que je modifie la ligne 2 par exemple, une personne peut ajouter une nouvelle ligne, qui, à cause de ma clause de tri, va se trouver en position numéro deux.
Ainsi, quand je vais soumettre la requête "modifier la ligne 2", je vais modifier la mauvaise ligne.
 
J'ai vu une propriété "primaryKey" dans datatable. Vu qu'on est obligé d'avoir une PK dans une datatable, à priori c'est une source plus fiable.
 
Cependant, j'ai toujours le souci de généricité. Ainsi, la clé peut être composées ou autre, ce qui ne facilite en rien mon truc...
 
Si vous voyez une meilleur solution (ou avez une idée précise de comment implémenter cette dernière, je suis preneur)

Reply

Marsh Posté le 12-07-2006 à 15:32:18    

En fait, tu veux recoder l'Enterprise Manager de SQL Server ?
 
Déjà, le fait que tu utilises des string ça t'empêche de considérer toutes les clefs primaires possibles, donc niveau généricité, ça marchera que dans 99% des cas. (mais c déjà pas mal :)
 
Les clefs primaires c bien : ca t'identifie une ligne de façon unique.
Si tu as plusieurs clefs primaires, bah il faut faire un système pour les passer toutes en paramètres pour identifier tes lignes.
Pour éviter de faire une using à gaz, je collerai la gestion de la clef primaire dans un object spécialisé (DataIdentifier ?) qui gère les clefs simples et composées de façon transparent au niveau de ton code 'fonctionnel'.
Cet objet à pour but de lire les clefs primaires d'un DataRow et les coller dans une chaine à la demande, et vice-versa. Avec une redéfinition de Equals tu peux aussi vérifier l'égalité de deux lignes, quel que soit le nombre de clefs qui le compose.
 
Chui clair ou pas ?

Reply

Marsh Posté le 12-07-2006 à 15:46:36    

Dans mon flux XML, je récupère le type et les infos du type. Donc y'a pas de souci, je sais dans tous les cas recomposer ma clé.
 
Par contre, je viens de me rendre compte d'un truc très con : "primaryKey" de mon datatable est vide. Il est nécessaire d'avoir une PK dans la requête qui l'allimente, mais il n'est pas foutu de le retrouver dans l'objet :/ comment faire :sweat:

Reply

Marsh Posté le 12-07-2006 à 15:47:47    

sinon, une clé primaire, par définition, y'en a qu'une par table, après ce sont des clés alternatives ;) si je fais le choix d'utiliser la pk je n'ai pas à me soucier de savoir combien il y a de clé ;)

Reply

Marsh Posté le 12-07-2006 à 21:37:14    

Ca y est !
 
J'ai donc trouvé comme mettre à jour selon la PK.
Ce fût quelque peut difficile, mais finalement c'est plutôt simple :)
 
Pour récupérer la PK dans l'objet DataTable, il suffit de faire un "FillSchema()" à partir du DataAdapter.
 
Pour le reste, je vous laisse contempler le bordel :D
 
Edit :
Correction des bugs, et surtout, rechargement des données en cas de modification. Désolé pour le goto :D --
 
Edit :
Correction d'un nouveau bug : Si la requête ne retourne pas de PK identifiable, alors le .FillSchema() vide la DataTable. Il faut donc ensuite la recharger sans schema
Cette correction est largement améliorable (ici, on doit obligatoirement faire deux fois la requête, avec toutes les requêtes imbriquées !). Mais ça demande la duplication d'une grande partie du code...

Code :
  1. cmd.CommandText = sql;
  2.             cmd.CommandType = CommandType.Text;
  3.             cmd.Prepare();
  4.             DataTable dt = new DataTable();
  5.             SqlDataAdapter da = new SqlDataAdapter(cmd);
  6.             da.Fill(dt);
  7.             int nbRows = dt.Rows.Count;
  8.             da.FillSchema(dt, SchemaType.Mapped);
  9.             int nbRowsSchema = dt.Rows.Count;
  10.             bool addRow = false;
  11.             bool delRow = false;
  12.             if (nbRows == nbRowsSchema)
  13.             {
  14.                 // Insert
  15.                 DataRow newRow = null;
  16.                 foreach (string param in Request.Params.Keys)
  17.                 {
  18.                     if (param != null && param.StartsWith(string.Format("add:{0}-", viewName)) && Request.Params[param] != string.Empty)
  19.                     {
  20.                         addRow = true;
  21.                         break;
  22.                     }
  23.                 }
  24.                 if (addRow)
  25.                 {
  26.                     bool notNullRow = false;
  27.                     newRow = dt.NewRow();
  28.                     foreach (DataColumn col in dt.Columns)
  29.                     {
  30.                         foreach (string param in Request.Params.Keys)
  31.                         {
  32.                             if (param != null && param == string.Format("add:{0}-{1}", viewName, col.ColumnName))
  33.                             {
  34.                                 switch (col.DataType.Name)
  35.                                 {
  36.                                     case "String":
  37.                                         if (Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString() != string.Empty)
  38.                                         {
  39.                                             notNullRow = true;
  40.                                             newRow[col] = Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString();
  41.                                         }
  42.                                         break;
  43.                                 }
  44.                                 break;
  45.                             }
  46.                         }
  47.                     }
  48.                     if (notNullRow)
  49.                     {
  50.                         dt.Rows.Add(newRow);
  51.                     }
  52.                     else
  53.                     {
  54.                         newRow = null;
  55.                     }
  56.                 }
  57.                 // Delete
  58.                 foreach (string param in Request.Params.Keys)
  59.                 {
  60.                     if (param != null && param == string.Format("del:{0}", viewName))
  61.                     {
  62.                         delRow = true;
  63.                         break;
  64.                     }
  65.                 }
  66.                 if (delRow)
  67.                 {
  68.                     // Primary keys retrieve
  69.                     foreach (string lineId in Request.Params[string.Format("del:{0}", viewName)].Split(','))
  70.                     {
  71.                         bool deleted = false;
  72.                         string[] keys = Request.Params[string.Format("pk:{0}-{1}", viewName, lineId)].Split(',');
  73.                         foreach (DataRow row in dt.Rows)
  74.                         {
  75.                             if (row.RowState != DataRowState.Deleted)
  76.                             {
  77.                                 bool wouldDelete = true;
  78.                                 for (int i = 0; i < keys.Length; i++)
  79.                                 {
  80.                                     if (row[dt.PrimaryKey[i]].ToString() != keys[i].Substring(1, keys[i].Length - 2))
  81.                                     {
  82.                                         wouldDelete = false;
  83.                                         break;
  84.                                     }
  85.                                 }
  86.                                 if (wouldDelete)
  87.                                 {
  88.                                     row.Delete();
  89.                                     deleted = true;
  90.                                 }
  91.                                 if (deleted)
  92.                                 {
  93.                                     break;
  94.                                 }
  95.                             }
  96.                         }
  97.                     }
  98.                 }
  99.             }
  100.         BuildLineNode:
  101.             if (nbRows == nbRowsSchema)
  102.             {
  103.                 if (!addRow && !delRow)
  104.                 {
  105.                     foreach (DataRow row in dt.Rows)
  106.                     {
  107.                         XmlNode rLine = outputNode.OwnerDocument.CreateElement("line" );
  108.                         // Create Primary Key definition
  109.                         XmlAttribute pk = rLine.OwnerDocument.CreateAttribute("primaryKey" );
  110.                         string pkVal = string.Empty;
  111.                         foreach (DataColumn dc in dt.PrimaryKey)
  112.                         {
  113.                             pkVal += string.Format("<{0}>,", row[dc].ToString());
  114.                         }
  115.                         if (pkVal != string.Empty)
  116.                         {
  117.                             pk.Value = pkVal.Substring(0, pkVal.Length - 1);
  118.                         }
  119.                         rLine.Attributes.Append(pk);
  120.                         foreach (DataColumn col in dt.Columns)
  121.                         {
  122.                             // Primary keys retrieve
  123.                             foreach (string param in Request.Params.Keys)
  124.                             {
  125.                                 bool updated = false;
  126.                                 if (param != null && param.StartsWith(string.Format("upd:{0}-{1}-", viewName, col.ColumnName)))
  127.                                 {
  128.                                     bool wouldUpdate = true;
  129.                                     string lineId = param.Substring(param.LastIndexOf('-') + 1);
  130.                                     string[] keys = Request.Params[string.Format("pk:{0}-{1}", viewName, lineId)].Split(',');
  131.                                     for (int i = 0; i < keys.Length; i++)
  132.                                     {
  133.                                         if (row[dt.PrimaryKey[i]].ToString() != keys[i].Substring(1, keys[i].Length - 2))
  134.                                         {
  135.                                             wouldUpdate = false;
  136.                                             break;
  137.                                         }
  138.                                     }
  139.                                     if (wouldUpdate)
  140.                                     {
  141.                                         switch (col.DataType.Name)
  142.                                         {
  143.                                             case "String":
  144.                                                 if (row[col].ToString() != Request.Params[param].ToString())
  145.                                                 {
  146.                                                     row[col] = Request.Params[param].ToString();
  147.                                                 }
  148.                                                 break;
  149.                                         }
  150.                                         updated = true;
  151.                                     }
  152.                                     if (updated)
  153.                                     {
  154.                                         break;
  155.                                     }
  156.                                 }
  157.                             }
  158.                             XmlElement rNode = rLine.OwnerDocument.CreateElement(col.ColumnName);
  159.                             rNode.InnerXml = row[col].ToString();
  160.                             XmlAttribute rAtt = rLine.OwnerDocument.CreateAttribute("type" );
  161.                             rAtt.Value = col.DataType.Name;
  162.                             rNode.Attributes.Append(rAtt);
  163.                             rLine.AppendChild(rNode);
  164.                         }
  165.                         outputNode.AppendChild(rLine);
  166.                         // For each row, we can have embeded views
  167.                         if (view.SelectNodes("views/view" ) != null)
  168.                         {
  169.                             RenderViews(outputNode, view.SelectNodes("views/view" ), row);
  170.                         }
  171.                     }
  172.                 }
  173.             }
  174.             DataTable updDataTabe = dt.GetChanges();
  175.             if (updDataTabe != null)
  176.             {
  177.                 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(da);
  178.                 if (addRow || delRow)
  179.                 {
  180.                     if (addRow)
  181.                     {
  182.                         da.InsertCommand = cmdBuilder.GetInsertCommand();
  183.                     }
  184.                     if (delRow)
  185.                     {
  186.                         da.DeleteCommand = cmdBuilder.GetDeleteCommand();
  187.                     }
  188.                 }
  189.                 else
  190.                 {
  191.                     da.UpdateCommand = cmdBuilder.GetUpdateCommand();
  192.                 }
  193.                 da.Update(updDataTabe);
  194.                 cmdBuilder.Dispose();
  195.                 cmdBuilder = null;
  196.             }
  197.             if (updDataTabe != null || nbRows != nbRowsSchema)
  198.             {
  199.                 dt.Clear();
  200.                 dt.Dispose();
  201.                 da.Dispose();
  202.                 da = new SqlDataAdapter(cmd);
  203.                 da.Fill(dt);
  204.                 outputNode.RemoveAll();
  205.                 delRow = false;
  206.                 addRow = false;
  207.                 nbRows = dt.Rows.Count;
  208.                 nbRowsSchema = nbRows;
  209.                 goto BuildLineNode;
  210.             }
  211.             dt.Dispose();
  212.             da.Dispose();
  213.             cmd.Dispose();


Message édité par Arjuna le 12-07-2006 à 23:07:07
Reply

Marsh Posté le 12-07-2006 à 22:14:43    

je suis certain que j'arriverais à faire la même chose en faisant 5x moins de ligne. Il y a une technique bien particulière pour cela. Traiter déjà des event sur le dataset. Après, ça fait tout à fait la même chose que tu fais la, en plus facile et en plus cours :D

Reply

Marsh Posté le 12-07-2006 à 22:31:29    

yep, sauf que moi je ne bosse pas en mode connecté et j'ai pas de databind,  j'ai pas de viewstate ni rien, donc pour les event...

Reply

Marsh Posté le 12-07-2006 à 22:33:11    

ceci dit, il y a pas mal d'optimisations à faire. si tu te sens le courrage, je suis preneur d'optimisations :) (le premier, ce serait de faire un Request.Params["toto%"] afin d'éviter de tous me les palucher un peu partout... Là les boucles imbriquées qui servent à rien, y'en a une trippoté :D

Reply

Marsh Posté le 12-07-2006 à 22:33:11   

Reply

Marsh Posté le 12-07-2006 à 23:14:51    

Groumpf...
 
Je viens de m'appercevoir que pour les cas tordus, genre je met à jours les données d'une table liée, pour chaque ligne de la table principale, je vais avoir un truc très con : pas capable de retrouver la PK correspondant à chaque champ :sweat:
 
Bon, là je suis mal, y'a plus qu'à réécrire une grande partie du code... Dans le principe, ça reste pareil, sauf qu'il faut que j'inclue la PK dans le nom du champ si je veux pouvoir le retrouver :sweat:
 
Edit :
Ceci dit, je me rend compte aussi que pour les INSERT, je suis encore plus dans la merde dans le cas d'une table liée... Obligé dans tous les cas de passer par un JS pour pas avoir d'embrouille... Et là je ne vois pas de solution simple pour faire une correction qui colle...
 
Du coup, une solution JS est possible pour le coup du INSERT et une autre, côté XSL, pour le UPDATE et DELETE... du coup ben... Ouais, en fait c'est bien comme ça :D


Message édité par Arjuna le 12-07-2006 à 23:19:10
Reply

Marsh Posté le 13-07-2006 à 00:12:18    

FillSchema, c'est trop trop trop con comme fonction :fou:
 
Non seulement ça vite le DT s'il est vide, mais en plus, si je filtre par la PK lors de la requête, il doit décider que l'information de PK est inutile, donc il la vire :fou:

Reply

Marsh Posté le 13-07-2006 à 01:05:33    

c'est même pire que tout...
 
si je met id = 1 en dur, j'ai bien une pk
si je met id = @idval alors ça marche plus, y'a plus de pk
 
en fait, il me vire le schema si un paramètre porte sur une composante de la pk :sweat:

Reply

Marsh Posté le 13-07-2006 à 01:06:33    

à moins que ce ne soit carrément dès que j'ai un paramètre...
 
ouais, en fait ça c'est. dès que j'utilise un paramètre à ma requêtes, j'ai plus d'info de pk...

Reply

Marsh Posté le 13-07-2006 à 09:27:19    

Arjuna a écrit :

yep, sauf que moi je ne bosse pas en mode connecté et j'ai pas de databind,  j'ai pas de viewstate ni rien, donc pour les event...


 
ma technique consisterait également à travailler en mode déconnecté. Bien entendu, j'aurais besoin du viewstate pour conserver les data d'un passage à l'autre.  
Cependant, je ne l'ai jms implémenté en Web, donc finalement, ça deviendrait peut etre marteau burin aussi...  

Reply

Marsh Posté le 13-07-2006 à 10:25:40    

ouais, chais pas trop.
 
d'un autre côté, mon truc est récursif, on peut donc avoir n requêtes imbriquées et m requêtes à la queue leu-leue. Du coup, pour retrouver quelque chose dans le viewstate... j'ai préféré écarter cette option dès le départ, jugeant les problèmes d'accès mémoire trop compliqués par rapport à réxécuter les requêtes.
 
de plus, à la sortie, je crée un flux XML que je transforme en XSL, je n'ai donc pas la possibilité de lier un contrôle à chaque datatable comme on ferait avec des datarepeater.
 
dans tous les cas, ça ne résoud pas mon problème de fillschema qui ne marche pas quand j'ai un paramètre dans ma requête... :/


Message édité par Arjuna le 13-07-2006 à 10:27:14
Reply

Marsh Posté le 13-07-2006 à 13:23:09    

Et le goto , tu l'as enlevé ?
 
Pourquoi tu découpes pas ton code en fonctions ? Ca te simplifiera la vie méchamment !
Les méthodes 'usines à gaz' de 200 lignes de long c'est super chiant à maintenir, tu passes des lustres à essayer de re-comprendre ce que tu as fait quand tu les reprends un mois plus tard, tu factorises aucun traitement et du coup tu multiplies les copier-coller là ou tu pourrais l'éviter, etc...
 
Vive les méthodes privée !  :)

Reply

Marsh Posté le 13-07-2006 à 14:26:35    

=> dans le dernier code que j'ai posté en date, non, il est toujours présent le gogo.
=> pour les fonction, je suis d'accord que ça deviens un peu usine à gaz. ceci dit, c'est carrément du oneshot à chaque fois : même si parfois je fais deux fois un traîtement similaire, il y a suffisament de différences pour ne pas le rendre factorisable. du coup j'ai tout laissé en pense de vache, mais je ne vois pas comment faire mieux, du moins si je veux conserver le peu d'optimisations qu'il y a dans le code...
 
sinon, voici ma bidouille pour contourner le problème du fillschema... d'un côté, j'ai fortement optimisé le code, et surtout, il n'y a plus de goto... mais d'un autre côté, là je lance systématiquement la requête une première fois sans filtre ! du coup je vous raconte pas les perfs si y'a beaucoup de ligne :/
 
j'espère qu'une bonne âme voudra bien me donner une explication de ce "bug" de fillschema, afin de retrouver un semblant de performances dans mon truc...
 
du coup, j'en suis à me demander si ce ne serait pas plus simple de tout charger, et de faire les filtres dans le DataTable au lieu de les faire en SQL. A priori, ça serait pas moins performant :/
 
Voilà le code complet qui marche.
Y'a plus de bugs. Et je peux mettre à jour n'importe quelle requête du moment qu'il n'y a pas d'agrégations dedans (ni de jointures complexes, ça va de soit)
 

Code :
  1. SqlCommand cmd = cnx.CreateCommand();
  2.             string sql = string.Format("select * from ({0}) res", xmlDefinition.SelectSingleNode("view/sql" ).InnerText);
  3.             cmd.CommandText = sql;
  4.             cmd.CommandType = CommandType.Text;
  5.             cmd.Prepare();
  6.             DataTable dts = new DataTable();
  7.             SqlDataAdapter das = new SqlDataAdapter(cmd);
  8.             das.Fill(dts);
  9.             das.FillSchema(dts, SchemaType.Source);
  10.             bool addRow = false;
  11.             bool delRow = false;
  12.             // Insert
  13.             DataRow newRow = null;
  14.             foreach (string param in Request.Params.Keys)
  15.             {
  16.                 if (param != null && param.StartsWith(string.Format("add:{0}-", viewName)) && Request.Params[param] != string.Empty)
  17.                 {
  18.                     addRow = true;
  19.                     break;
  20.                 }
  21.             }
  22.             if (addRow)
  23.             {
  24.                 bool notNullRow = false;
  25.                 newRow = dts.NewRow();
  26.                 foreach (DataColumn col in dts.Columns)
  27.                 {
  28.                     foreach (string param in Request.Params.Keys)
  29.                     {
  30.                         if (param != null && param == string.Format("add:{0}-{1}", viewName, col.ColumnName))
  31.                         {
  32.                             switch (col.DataType.Name)
  33.                             {
  34.                                 case "String":
  35.                                     if (Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString() != string.Empty)
  36.                                     {
  37.                                         notNullRow = true;
  38.                                         newRow[col] = Request.Params[string.Format("add:{0}-{1}", viewName, col.ColumnName)].ToString();
  39.                                     }
  40.                                     break;
  41.                             }
  42.                             break;
  43.                         }
  44.                     }
  45.                 }
  46.                 if (notNullRow)
  47.                 {
  48.                     dts.Rows.Add(newRow);
  49.                 }
  50.                 else
  51.                 {
  52.                     newRow = null;
  53.                 }
  54.             }
  55.             // Delete
  56.             foreach (string param in Request.Params.Keys)
  57.             {
  58.                 if (param != null && param == string.Format("del:{0}", viewName))
  59.                 {
  60.                     delRow = true;
  61.                     break;
  62.                 }
  63.             }
  64.             if (delRow)
  65.             {
  66.                 // Primary keys retrieve
  67.                 foreach (string lineId in Request.Params[string.Format("del:{0}", viewName)].Split(','))
  68.                 {
  69.                     bool deleted = false;
  70.                     string[] keys = Request.Params[string.Format("pk:{0}-{1}", viewName, lineId)].Split(',');
  71.                     foreach (DataRow row in dts.Rows)
  72.                     {
  73.                         if (row.RowState != DataRowState.Deleted)
  74.                         {
  75.                             bool wouldDelete = true;
  76.                             for (int i = 0; i < keys.Length; i++)
  77.                             {
  78.                                 if (row[dts.PrimaryKey[i]].ToString() != keys[i].Substring(1, keys[i].Length - 2))
  79.                                 {
  80.                                     wouldDelete = false;
  81.                                     break;
  82.                                 }
  83.                             }
  84.                             if (wouldDelete)
  85.                             {
  86.                                 row.Delete();
  87.                                 deleted = true;
  88.                             }
  89.                             if (deleted)
  90.                             {
  91.                                 break;
  92.                             }
  93.                         }
  94.                     }
  95.                 }
  96.             }
  97.             // Update
  98.             foreach (DataRow row in dts.Rows)
  99.             {
  100.                 if (row.RowState != DataRowState.Deleted)
  101.                 {
  102.                     foreach (DataColumn col in dts.Columns)
  103.                     {
  104.                         // Primary keys retrieve
  105.                         foreach (string param in Request.Params.Keys)
  106.                         {
  107.                             bool updated = false;
  108.                             if (param != null && param.StartsWith(string.Format("upd:{0}-{1}-", viewName, col.ColumnName)))
  109.                             {
  110.                                 bool wouldUpdate = true;
  111.                                 string lineId = param.Substring(param.LastIndexOf('-') + 1);
  112.                                 string[] keys = Request.Params[string.Format("pk:{0}-{1}", viewName, lineId)].Split(',');
  113.                                 for (int i = 0; i < keys.Length; i++)
  114.                                 {
  115.                                     if (row[dts.PrimaryKey[i]].ToString() != keys[i].Substring(1, keys[i].Length - 2))
  116.                                     {
  117.                                         wouldUpdate = false;
  118.                                         break;
  119.                                     }
  120.                                 }
  121.                                 if (wouldUpdate)
  122.                                 {
  123.                                     switch (col.DataType.Name)
  124.                                     {
  125.                                         case "String":
  126.                                             if (row[col].ToString() != Request.Params[param].ToString())
  127.                                             {
  128.                                                 row[col] = Request.Params[param].ToString();
  129.                                             }
  130.                                             break;
  131.                                     }
  132.                                     updated = true;
  133.                                 }
  134.                                 if (updated)
  135.                                 {
  136.                                     break;
  137.                                 }
  138.                             }
  139.                         }
  140.                     }
  141.                 }
  142.             }
  143.             DataTable updDataTable = dts.GetChanges();
  144.             if (updDataTable != null)
  145.             {
  146.                 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(das);
  147.                 das.InsertCommand = cmdBuilder.GetInsertCommand();
  148.                 das.DeleteCommand = cmdBuilder.GetDeleteCommand();
  149.                 das.UpdateCommand = cmdBuilder.GetUpdateCommand();
  150.                 das.Update(updDataTable);
  151.                 cmdBuilder.Dispose();
  152.                 cmdBuilder = null;
  153.             }
  154.             SqlParameterCollection sqlParams = cmd.Parameters;
  155.             foreach (XmlNode filterNode in xmlDefinition.SelectNodes("view/filters/filter" ))
  156.             {
  157.                 if (filterNode.Attributes["name"].Value == filterName)
  158.                 {
  159.                     if (filterNode.Attributes["sql"] != null)
  160.                     {
  161.                         sql += " where " + filterNode.Attributes["sql"].Value;
  162.                         foreach (XmlNode parameterNode in filterNode.SelectNodes("parameter" ))
  163.                         {
  164.                             SqlParameter param = new SqlParameter();
  165.                             param.ParameterName = parameterNode.Attributes["name"].Value;
  166.                             param.Size = int.Parse(parameterNode.Attributes["length"].Value);
  167.                             param.Scale = byte.Parse(parameterNode.Attributes["scale"].Value);
  168.                             param.Precision = byte.Parse(parameterNode.Attributes["precision"].Value);
  169.                             switch (parameterNode.Attributes["sqltype"].Value)
  170.                             {
  171.                                 case "nvarchar":
  172.                                     param.SqlDbType = SqlDbType.NVarChar;
  173.                                     param.Value = parameters[parameterNode.Attributes["name"].Value];
  174.                                     sqlParams.Add(param);
  175.                                     break;
  176.                                 case "numeric":
  177.                                     param.SqlDbType = SqlDbType.Decimal;
  178.                                     param.Value = Int64.Parse(parameters[parameterNode.Attributes["name"].Value]);
  179.                                     sqlParams.Add(param);
  180.                                     break;
  181.                             }
  182.                         }
  183.                     }
  184.                     break;
  185.                 }
  186.             }
  187.             if (sortName != string.Empty)
  188.             {
  189.                 foreach (XmlNode sortNode in xmlDefinition.SelectNodes("view/sorts/sort" ))
  190.                 {
  191.                     if (sortNode.Attributes["name"].Value == sortName)
  192.                     {
  193.                         sql += string.Format(" order by {0}", sortNode.Attributes["sql"].Value);
  194.                         break;
  195.                     }
  196.                 }
  197.             }
  198.             cmd.CommandText = sql;
  199.             cmd.CommandType = CommandType.Text;
  200.             cmd.Prepare();
  201.             DataTable dt = new DataTable();
  202.             SqlDataAdapter da = new SqlDataAdapter(cmd);
  203.             da.Fill(dt);
  204.             foreach (DataRow row in dt.Rows)
  205.             {
  206.                 XmlNode rLine = outputNode.OwnerDocument.CreateElement("line" );
  207.                 // Create Primary Key definition
  208.                 XmlAttribute pk = rLine.OwnerDocument.CreateAttribute("primaryKey" );
  209.                 string pkVal = string.Empty;
  210.                 foreach (DataColumn dc in dts.PrimaryKey)
  211.                 {
  212.                     pkVal += string.Format("<{0}>,", row[dc.ColumnName].ToString());
  213.                 }
  214.                 if (pkVal != string.Empty)
  215.                 {
  216.                     pk.Value = pkVal.Substring(0, pkVal.Length - 1);
  217.                 }
  218.                 rLine.Attributes.Append(pk);
  219.                 foreach (DataColumn col in dt.Columns)
  220.                 {
  221.                     XmlElement rNode = rLine.OwnerDocument.CreateElement(col.ColumnName);
  222.                     rNode.InnerXml = row[col].ToString();
  223.                     XmlAttribute rAtt = rLine.OwnerDocument.CreateAttribute("type" );
  224.                     rAtt.Value = col.DataType.Name;
  225.                     rNode.Attributes.Append(rAtt);
  226.                     rLine.AppendChild(rNode);
  227.                 }
  228.                 outputNode.AppendChild(rLine);
  229.                 // For each row, we can have embeded views
  230.                 if (view.SelectNodes("views/view" ) != null)
  231.                 {
  232.                     RenderViews(outputNode, view.SelectNodes("views/view" ), row);
  233.                 }
  234.             }
  235.             dts.Dispose();
  236.             das.Dispose();
  237.             dt.Dispose();
  238.             da.Dispose();
  239.             cmd.Dispose();
  240.             // Add data to output view node
  241.             output.AppendChild(outputNode);
  242.             // Free memory resources and files handles
  243.             dts = null;
  244.             das = null;
  245.             dt = null;
  246.             da = null;
  247.             cmd = null;
  248.             outputNode = null;
  249.             xmlDefinition = null;


Message édité par Arjuna le 13-07-2006 à 14:27:23
Reply

Marsh Posté le 13-07-2006 à 16:27:46    

Suite à la correction de ce problème :
http://forum.hardware.fr/forum2.ph [...] 0#t1406039
 
Maintenant :
- ça marche
- c'est rapide
- c'est super optimisé (autant faire se peut)
 
:bounce:

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed