{"id":1136,"date":"2010-07-29T23:05:08","date_gmt":"2010-07-29T22:05:08","guid":{"rendered":"http:\/\/blog.capdata.fr\/?p=1136"},"modified":"2022-11-21T16:56:34","modified_gmt":"2022-11-21T15:56:34","slug":"generer-le-ddl-complet-dune-base-en-csmo","status":"publish","type":"post","link":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/","title":{"rendered":"G\u00e9n\u00e9rer le DDL complet d&#8217;une base en C# \/ SMO \/ multithreading"},"content":{"rendered":"<a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-twitter nolightbox\" data-provider=\"twitter\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Twitter\" href=\"https:\/\/twitter.com\/intent\/tweet?url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136&#038;text=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"twitter\" title=\"Share on Twitter\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/twitter.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-linkedin nolightbox\" data-provider=\"linkedin\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Linkedin\" href=\"https:\/\/www.linkedin.com\/shareArticle?mini=true&#038;url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136&#038;title=G%C3%A9n%C3%A9rer%20le%20DDL%20complet%20d%E2%80%99une%20base%20en%20C%23%20%2F%20SMO%20%2F%20multithreading\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"linkedin\" title=\"Share on Linkedin\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/linkedin.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox\" data-provider=\"mail\" rel=\"nofollow\" title=\"Share by email\" href=\"mailto:?subject=G%C3%A9n%C3%A9rer%20le%20DDL%20complet%20d%E2%80%99une%20base%20en%20C%23%20%2F%20SMO%20%2F%20multithreading&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"mail\" title=\"Share by email\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/mail.png\" \/><\/a><p>Pour faire suite \u00e0 un<a href=\"https:\/\/www.developpez.net\/forums\/d945501\/bases-donnees\/ms-sql-server\/administration\/recuperer-script-base\/\"> post<\/a> sur d\u00e9veloppez concernant l&#8217;existence d&#8217;outils de scripting d&#8217;objets en dehors de ceux propos\u00e9s par SSMS, j&#8217;ai d\u00e9cid\u00e9 de regarder d&#8217;un peu plus pr\u00e8s le sujet et d&#8217;en cr\u00e9er un (<em>getMsDDLmt<\/em>) pour voir ce que \u00e7a donne. Afin de suivre\u00a0 les explications du code, les sources sont disponibles sur <a href=\"https:\/\/sourceforge.net\/projects\/getmsddlmt\/\">SourceForge<\/a>.<\/p>\n<p>Je vais juste d\u00e9tailler un peu les points qui sortent de l&#8217;ordinaire dans le programme. Il s&#8217;appuie comme la plupart des outils de ce genre sur <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms162557.aspx\">SMO<\/a> et<a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/618ayhy6.aspx\"> C#<\/a>.<\/p>\n<h2>Comment g\u00e9n\u00e9rer le script d&#8217;un objet en SMO:<\/h2>\n<p>L&#8217;objet au sommet de la hi\u00e9rarchie des objets SMO pour ce qui nous int\u00e9resse est <span style=\"color: #333333;\"><strong>Database<\/strong><\/span>:<\/p>\n<pre><span style=\"color: #333333;\">Microsoft.SqlServer.Management.Smo.Database\r\n                                    |__ Users\r\n                                    |__ Schemas\r\n                                    |__ Role\r\n                                    |__ Table\r\n                                    |__ View\r\n                                    |__ StoredProcedure\r\n                                    |__ UserDefinedFunction\r\n                                    |__ etc...<\/span>\r\n<\/pre>\n<p>Chaque sous-objet de la hi\u00e9rarchie de <em>Database <\/em>poss\u00e8de une m\u00e9thode <em>script()<\/em> qu&#8217;il suffit d&#8217;invoquer, et qui renvoie une <em>StringCollection<\/em>. Il suffit d&#8217;afficher chaque item de la collection pour obtenir le script de tous les objets. Par exemple pour sortir toutes les UDF:<\/p>\n<pre><span style=\"color: #0000ff;\">Server Srv\u00a0 = new Server(\".\");\r\nDatabase Db = Srv.Databases[\"AdventureWorks\"];\r\n\r\nUserDefinedFunctionCollection _func = Db.UserDefinedFunctions;\r\nforeach (UserDefinedFunction _f in _func)\r\n{\r\n     StringCollection _fscr = _f.Script();\r\n     foreach (string _line in _fscr)\r\n         Console.WriteLine(_line);\r\n}\r\nConsole.WriteLine(\"GO\");<\/span>\r\n<\/pre>\n<p>Vous remarquerez qu&#8217;il n&#8217;y a pas explicitement de connexion, elle est r\u00e9alis\u00e9e dans le constructeur de <em>Srv<\/em>. Une fois que la base est instanci\u00e9e, on r\u00e9cup\u00e8re notre collection d&#8217;UDF, et pour chaque fonction on applique la fonction <em>script()<\/em>. Comme<em> script()<\/em> renvoie elle aussi une collection, on est encore oblig\u00e9 de boucler pour afficher chaque ligne.<\/p>\n<p><strong>Bien que relativement simple sur le papier, \u00e7a n&#8217;est pas sans poser de gros soucis dans la r\u00e9alit\u00e9:<\/strong><br \/>\n&#8211; Il faut faire attention de filtrer les objets syst\u00e8mes car il n&#8217;y a pas de distinction entre une vue syst\u00e8me et une vue utilisateur dans la classe View, ou la classe StoredProcedure.<br \/>\n&#8211; La m\u00e9thode Script() ne peut pas \u00eatre appel\u00e9e si l&#8217;objet a \u00e9t\u00e9 encrypt\u00e9 (par exemple une fonction ou une proc\u00e9dure stock\u00e9e).<br \/>\n&#8211; Dans un programme simple, la g\u00e9n\u00e9ration du DDL est s\u00e9rialis\u00e9e: elle n&#8217;est effectu\u00e9e que par un seul thread.<br \/>\n&#8211; Ca pollue le plan cache avec des tonnes de plans ad hoc qui sont sans cesse recompil\u00e9s.<\/p>\n<h3>R\u00e9sultat, la g\u00e9n\u00e9ration du DDL par la m\u00e9thode classique est affreusement lente:<\/h3>\n<ul>\n<li><strong>Compilation des plans ad hoc:<\/strong> c&#8217;est le le premier facteur en termes de temps de r\u00e9ponse. Une mani\u00e8re simple de le voir est de lancer la g\u00e9n\u00e9ration deux fois de suite. La seconde fois, le script complet sera g\u00e9n\u00e9r\u00e9 beaucoup plus vite. Pour pallier ce probl\u00e8me, pas beaucoup de solutions: passer la base en PARAMETERIZATION = FORCED pour que l&#8217;optimiseur choisisse de param\u00e9triser aussi les plans non \u00e9ligibles par d\u00e9faut (avec des jointures, etc&#8230;) peut \u00eatre extr\u00eamement efficace le temps de la g\u00e9n\u00e9ration (28 secondes en FORCED contre 1 minute 30 en SIMPLE pour sortir AdventureWorks sur mon laptop avec un seul thread), mais l&#8217;impact peut quand m\u00eame \u00eatre important sur une base de production, car l&#8217;option s&#8217;applique \u00e0 toute la base. Fort heureusement elle est dynamique et peut \u00eatre retir\u00e9e rapidement \u00e0 la fin de la g\u00e9n\u00e9ration.<\/li>\n<\/ul>\n<ul>\n<li><strong>Le parcours de la liste des objets \u00e0 scripter: <\/strong> parce que pour toutes les proc\u00e9dures stock\u00e9es, fonctions, vues il faut v\u00e9rifier si l&#8217;objet n&#8217;est pas un objet syst\u00e8me (avec <em>isSystemObject<\/em>) et si l&#8217;objet n&#8217;est pas encrypt\u00e9 (avec <em>isEncrypted<\/em>). Il existe une astuce qui a \u00e9t\u00e9 r\u00e9v\u00e9l\u00e9e\u00a0 par <a href=\"https:\/\/www.sqlteam.com\/article\/scripting-database-objects-using-smo-updated\">Bill Graziano<\/a> pour acc\u00e9l\u00e9rer un peu ce processus en pr\u00e9-fetchant ces propri\u00e9t\u00e9s, mais globalement les gains\u00a0 restent tr\u00e8s moyens. Le plus rapide pour g\u00e9n\u00e9rer la liste des objets \u00e0 filtrer reste encore d&#8217;\u00e9crire une requ\u00eate qui le ferait, au moins pour filtrer les objets syst\u00e8me, et de placer le tout dans un DataTable ADO.NET. C&#8217;est la m\u00e9thode qui a \u00e9t\u00e9 retenue pour getMsDDLmt.<\/li>\n<\/ul>\n<ul>\n<li><strong>La s\u00e9rialisation du processus<\/strong>: par d\u00e9faut, seul un thread ferait tout le travail, sans tirer parti des multiples cores que l&#8217;on trouve aujourd&#8217;hui m\u00eame sur des portables. La solution est de cr\u00e9er un programme <a href=\"https:\/\/www.gotw.ca\/publications\/concurrency-ddj.htm\">qui utilise de multiples threads<\/a> pour g\u00e9n\u00e9rer le DDL des objets les plus couramment rencontr\u00e9s (tables, fonctions, proc\u00e9dures stock\u00e9es, vues, etc&#8230;)\u00a0 en parall\u00e8le.<\/li>\n<\/ul>\n<h2>Le programme getMsDDLmt:<\/h2>\n<p>L&#8217;objectif est d&#8217;extraire sur la console le DDL de tous les objets  d&#8217;une base en particulier. L&#8217;utilisateur peut rediriger la sortie de la  console ensuite dans un fichier.\u00a0 Il  prend en argument un fichier de configuration au format XML tr\u00e8s simple  qui ressemble \u00e0 ceci:<\/p>\n<pre><span style=\"color: #0000ff;\">&lt;?xml version='1.0'?&gt;\r\n&lt;Host&gt;\r\n    &lt;Instance&gt;.&lt;\/Instance&gt;\r\n    &lt;Database&gt;AdventureWorks&lt;\/Database&gt;\r\n&lt;\/Host&gt;<\/span><\/pre>\n<p>Le point repr\u00e9sente l&#8217;instance locale, si vous avez une instance  nomm\u00e9e il faudra renseigner son nom complet. L&#8217;authentification est  suppos\u00e9e en mode int\u00e9gr\u00e9e, la raison pour laquelle on ne passe pas de  user, de mot de passe, etc&#8230;<\/p>\n<p>Un appel au programme s&#8217;effectue donc comme ceci:<\/p>\n<pre><span style=\"color: #0000ff;\">DOS&gt;getMsDDLmt -I myConfigFile.xml &gt; maBase.sql<\/span>\r\n<\/pre>\n<p>Les\u00a0 \u00e9tapes critiques \u00e9tant la constitution de la liste d&#8217;objets\u00a0 et la partie multithreading, je vais surtout m&#8217;attarder sur ces deux points principaux.<\/p>\n<h3>1\u00e8re \u00e9tape: prise de l&#8217;environnement et des variables principales ( <em>Main()<\/em> ):<\/h3>\n<p>C&#8217;est le v\u00e9ritable d\u00e9but, une fois les param\u00e8tres r\u00e9cup\u00e9r\u00e9s depuis le fichier de config XML, il faut tenter d&#8217;initier la connexion \u00e0 l&#8217;instance et \u00e0 la base. Quelques tests basiques sont effectu\u00e9s pour trapper une instance qui n&#8217;existe pas ou qui ne serait pas d\u00e9marr\u00e9e, par exemple. Tout cela a lieu dans le point d&#8217;entr\u00e9e principal du programme:<\/p>\n<pre> <span style=\"color: #008000;\">\/\/ Validation de la connexion et de la base <\/span>\r\n <span style=\"color: #0000ff;\">try\r\n {\r\n     Srv = new Server(_srv);\r\n     if (Srv.Databases.Contains(_dbname))\r\n     {\r\n         Db = Srv.Databases[_dbname];\r\n     }\r\n     else\r\n     {\r\n         Console.WriteLine(\"La base \" + _dbname + \" n'a pas \u00e9t\u00e9 trouv\u00e9e\");\r\n         return;\r\n     }\r\n }\r\n catch(Exception exsql)\r\n {\r\n     Console.WriteLine(\"Une erreur est survenue dans le module: \"+exsql.Source+\" : \"+exsql.Message);\r\n     Console.WriteLine(\"V\u00e9rifiez le nom de l'instance dans le fichier \"+ _xmlcfg +\" et v\u00e9rifiez que celle-ci est bien d\u00e9marr\u00e9e\");\r\n     return;\r\n }<\/span>\r\n\r\n<span style=\"color: #008000;\"> \/\/ Test de la version de SQL Server. Support\u00e9 9+<\/span>\r\n<span style=\"color: #0000ff;\"> if (Srv.Information.Version.Major &lt; 9)\r\n {\r\n     Console.WriteLine(\"Version de SQL Server support\u00e9e \u00e0 partir de SQL Server 2005 et plus\");\r\n     Usage();\r\n     return;\r\n }<\/span>\r\n\r\n <span style=\"color: #008000;\">\/\/ Revision 1.1: l'appel \u00e0 la m\u00e9thode Script() sur un objet encrypt\u00e9 retourne une exception non g\u00e9r\u00e9e de type PropertyCannotBeRetrievedException.\r\n \/\/ On fetche donc la propri\u00e9t\u00e9 d\u00e8s le d\u00e9part pour pouvoir la tester plus rapidement.<\/span>\r\n <span style=\"color: #0000ff;\">Srv.SetDefaultInitFields(typeof(StoredProcedure), \"IsEncrypted\");\r\n Srv.SetDefaultInitFields(typeof(View), \"IsEncrypted\");\r\n Srv.SetDefaultInitFields(typeof(UserDefinedFunction), \"IsEncrypted\");<\/span>\r\n\r\n<span style=\"color: #008000;\"> \/\/ R\u00e9cup\u00e9ration de la liste des objet et alimentation du DataTable.<\/span>\r\n<span style=\"color: #0000ff;\"> _DT_ObjList = getUsrObjList();<\/span>\r\n<\/pre>\n<p>La partie int\u00e9ressante est celle qui concerne la Revision 1.1. C&#8217;est la mise en place du workaround de Bill Graziano, pour permettre de pr\u00e9-fetcher la propri\u00e9t\u00e9 <em>isEncrypted<\/em> pour les proc\u00e9dures, fonctions et vues, et d&#8217;y acc\u00e9der plus rapidement ensuite. La derni\u00e8re ligne affecte au DataTable global <em>_DT_ObjList<\/em> la liste des objets \u00e0 scripter, en utilisant la fonction <em>getUsrObjList()<\/em>.<\/p>\n<h3>2i\u00e8me \u00e9tape: lister les objets \u00e0 scripter par les threads ( <em>getUsrObjList() )<\/em>:<\/h3>\n<p>La liste va constituer le panier dans lequel les worker threads vont venir piocher les objets \u00e0 scripter. Elle est constitu\u00e9e\u00a0 d&#8217;une requ\u00eate qui va placer l&#8217;object_id et le type d&#8217;objet dans un objet DataTable:<\/p>\n<pre><span style=\"color: #008000;\">\/* ================================================================================================================\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Peuplement de La liste des objets \u00e0 scripter par le Main Thread.\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n * Le plus efficace revient \u00e0 r\u00e9cup\u00e9rer directement la liste des objets user dans un DataTable,\r\n * plut\u00f4t que de boucler sur chaque collection.On ne fait qu'un seul appel \u00e0 la base pour tout\r\n * r\u00e9cup\u00e9rer d'un coup.On ordonne par type asc pour que les vues (V) soient cr\u00e9\u00e9es apr\u00e8s les\r\n * tables (U)\r\n * *\/<\/span>\r\n <span style=\"color: #0000ff;\">static DataTable getUsrObjList()\r\n {\r\n     String _sqlText = @\"<span style=\"color: #993300;\">select object_id, type from <\/span>\" + _dbname + @\"<span style=\"color: #993300;\">.sys.objects\r\n                        where isnull(OBJECTPROPERTY(object_id,'IsMSShipped'),0) = 0\r\n                        and type in ('P','FN','R','SN','U','V')\r\n                        and name not in ('sysdiagrams','sp_upgraddiagrams','sp_helpdiagrams',\r\n                        'sp_helpdiagramdefinition','sp_creatediagram','sp_renamediagram',\r\n                        'sp_alterdiagram','sp_dropdiagram','fn_diagramobjects')\r\n                         order by type asc<\/span>\";\r\n\r\n     DataTable _DT_ObjList = new DataTable();\r\n     String _cnStr = \"server=\" + _srv + \";trusted_connection=yes\";\r\n     SqlConnection _cnx = new SqlConnection(_cnStr);\r\n     SqlDataAdapter _sqlDataAdapt = new SqlDataAdapter(_sqlText, _cnx);\r\n\r\n     try\r\n     {\r\n         _cnx.Open();\r\n     }\r\n     catch (Exception ex)\r\n     {\r\n         Console.WriteLine(ex.ToString());\r\n     }\r\n\r\n     try\r\n     {\r\n         _sqlDataAdapt.Fill(_DT_ObjList);\r\n     }\r\n     catch (Exception ex)\r\n     {\r\n         Console.WriteLine(ex.ToString());\r\n     }\r\n\r\n     return _DT_ObjList;\r\n }<\/span><\/pre>\n<p>On effectue d\u00e9j\u00e0 pas mal de filtres, en ne retenant pas les objets li\u00e9s aux diagrammes dans SSMS, et les objets syst\u00e8mes. Malheureusement il n&#8217;existe pas de propri\u00e9t\u00e9 pour identifier les fonctions ou proc\u00e9dures qui auraient \u00e9t\u00e9 encrypt\u00e9es, donc ont sera oblig\u00e9 de le faire au moment de la g\u00e9n\u00e9ration. On ne recherche que les objets de type Proc\u00e9dures stock\u00e9es (P), UDF (FN), r\u00e8gles (R), synonymes (SN), tables (U) et vues (V). Les indexes, triggers, et contraintes check seront g\u00e9n\u00e9r\u00e9es lors du passage sur la table donc ils ne sont pas mentionn\u00e9s explicitement dans la liste.<\/p>\n<p>Les autres objets de la base (la base elle-m\u00eame, les users, roles, sch\u00e9mas, fonctions et sch\u00e9mas de partition, etc&#8230;) seront sortis par le thread principal car ils ne pr\u00e9sentent pas de difficult\u00e9 particuli\u00e8re. On choisit \u00e9galement d&#8217;extraire les contraintes de cl\u00e9s \u00e9trang\u00e8res apr\u00e8s la g\u00e9n\u00e9ration des tables pour \u00e9viter les probl\u00e9matiques d&#8217;ordonnancement des tables dans le script (pour \u00e9viter qu&#8217;une contrainte ne soit \u00e9crite par un thread avant que la table \u00e0 laquelle elle fait r\u00e9f\u00e9rence ne soit pr\u00e9sente).<\/p>\n<p>Le tout est plac\u00e9 dans un objet DataTable <em>_DT_ObjList<\/em>, qui est retourn\u00e9 au Main(), pour \u00eatre ensuite parcouru par chaque worker thread.<\/p>\n<h3>3i\u00e8me \u00e9tape: cr\u00e9ation des worker threads et d\u00e9but de script des objets ( <em>initWorker()<\/em> et <em>scriptThis()<\/em> ).<\/h3>\n<p>Avant de rentrer dans le code, il faut peut \u00eatre expliquer un peu la notion de programmation avec plusieurs threads. L&#8217;avantage d&#8217;une telle philosophie est de pouvoir tirer pleinement parti des ressources de la machine. Le probl\u00e8me, c&#8217;est qu&#8217;\u00e0 un moment ou \u00e0 un autre, les threads vont entrer en concurrence sur l&#8217;aquisition d&#8217;une ressource partag\u00e9e. Dans notre cas, il s&#8217;agit de <em>_DT_ObjList<\/em>, la liste des objets \u00e0 scripter. Il n&#8217;est pas question que deux threads parcourent la liste\u00a0 en m\u00eame temps et d\u00e9cident de scripter le m\u00eame objet. Il va donc falloir trouver un moyen de les synchroniser.<\/p>\n<p>Il existe de nombreuses m\u00e9thodes pour synchroniser des threads entre eux. On distingue en gros deux grandes familles, les constructions utilisateur (comme les spinlocks ou les sections critiques en C++) et les kernel objects, qui sont des objets sp\u00e9cialement con\u00e7us pour \u00e7a, mais qui ont l&#8217;inconv\u00e9nient de ne pas \u00eatre manipulables directement dans l&#8217;espace d&#8217;adressage du programme utilisateur , et donc de n\u00e9cessiter une transition vers le mode kernel \u00e0 chaque fois qu&#8217;il sont invoqu\u00e9s.\u00a0 Toutefois, la m\u00e9thode la plus simple\u00a0 pour synchroniser des threads en C# est d&#8217;utiliser la m\u00e9thode <em>lock()<\/em> sur un kernel object, c&#8217;est donc celle que nous allons utiliser.<\/p>\n<p>Concernant le nombre de threads \u00e0 cr\u00e9er, j&#8217;ai d\u00e9cid\u00e9 d&#8217;observer la r\u00e8gle suivante:\u00a0 on cr\u00e9\u00e9 1 worker thread\u00a0 pour deux CPU, pour ne pas saturer compl\u00e8tement la machine sur laquelle on effectue l&#8217;extraction.\u00a0 La valeur est arrondie au sup\u00e9rieur. Donc si on a 1 ou 2 CPU, on ne cr\u00e9era qu&#8217;un seul worker thread, si on a 3 ou 4 CPU, on en cr\u00e9era 2, 5 ou 6 on en cr\u00e9era 3, etc&#8230;<\/p>\n<pre><span style=\"color: #0000ff;\">int _wtcnt = 1;\r\nint _corecnt = System.Environment.ProcessorCount;\r\n\r\n if (_corecnt == 1)\r\n {\r\n     _wtcnt = 1;\r\n }\r\n else\r\n {\r\n     if ((_corecnt % 2) != 0)\r\n     {\r\n         _wtcnt = (_corecnt \/ 2) + 1;\r\n     }\r\n     else\r\n     {\r\n         _wtcnt = (_corecnt \/ 2);\r\n     }\r\n }<\/span><\/pre>\n<p>Ensuite on dispatche les worker threads toutes les 1 ms. Le worker va passer par son point d&#8217;entr\u00e9e ( initWorker() ) et imm\u00e9diatement bloquer sur le kernel object. En appelant <em>Join()<\/em>, le main thread va attendre que les worker threads lui signalent la fin de la g\u00e9n\u00e9ration des objets, pour qu&#8217;il puisse continuer avec les autres objets r\u00e9siduels (foreign keys, fonctions et sch\u00e9mas de partition, etc&#8230;) .<\/p>\n<pre><span style=\"color: #008000;\">\/\/ Dispatch des wt toutes les 1 ms<\/span>\r\n<span style=\"color: #0000ff;\"> Thread[] _T_wt = new Thread[_wtcnt];\r\n\r\n for (int i = 0; i &lt; _wtcnt; i++)\r\n {\r\n     _T_wt[i] = new Thread(new ThreadStart(initWorker));\r\n     _T_wt[i].Start();\r\n     Thread.Sleep(1);\r\n }<\/span>\r\n\r\n<span style=\"color: #008000;\"> \/\/ Le main Thread attend la fin de l'ex\u00e9cution de chaque wt<\/span>\r\n<span style=\"color: #0000ff;\"> for (int i = 0; i &lt; _wtcnt; i++)\r\n {\r\n     if (_T_wt[i] != null)\r\n     {\r\n         _T_wt[i].Join();\r\n     }\r\n }<\/span>\r\n<\/pre>\n<p>Chaque worker thread va dans le m\u00eame temps bloquer sur le kernel object, et le premier qui obtient la ressource peut commencer \u00e0 d\u00e9piler le DataTable pendant que les autres worker threads attendent:<\/p>\n<pre><span style=\"color: #008000;\">\/* =================================================================================================================\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Point d'entr\u00e9e des wt.\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n * Chaque wt bloque sur le kernel object et d\u00e9pile une ligne du DataTable \u00e0 chaque fois qu'il obtient la ressource.\r\n * Chaque wt op\u00e9re tant qu'il reste des lignes dans le DataTable.\r\n * *\/<\/span>\r\n <span style=\"color: #0000ff;\">static void initWorker()\r\n {\r\n     Server srv = new Server(_srv);\r\n     Database _curdb = srv.Databases[_dbname];\r\n     DataRow _DR;\r\n     int _objid = 0;\r\n     string _type;\r\n\r\n     while (_DT_ObjList.Rows.Count &gt; 0)\r\n     {<\/span>\r\n         <span style=\"color: #008000;\">\/\/ le wt courant bloque sur le kernel object pour obtenir la ligne courante<\/span>\r\n         <span style=\"color: #0000ff;\">lock (_DTlocker)\r\n         {<\/span>\r\n             <span style=\"color: #008000;\">\/\/ On acc\u00e8de \u00e0 chaque fois \u00e0 la derni\u00e8re ligne, on la stocke et on la supprime pour\r\n             \/\/ qu'elle ne soit plus visible aux autres wt.<\/span>\r\n             <span style=\"color: #0000ff;\">_DR = _DT_ObjList.Rows[0];\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n             _objid = ((int)_DR[\"object_id\"]);\r\n             _type = ((string)_DR[\"type\"]).Trim();\r\n             _DR.Delete();\r\n             _DR.AcceptChanges();\r\n         }<\/span>\r\n\r\n         <span style=\"color: #008000;\">\/\/ execution du script de l'objet courant<\/span>\r\n    <span style=\"color: #0000ff;\">     try\r\n         {\r\n             scriptThis(_objid, _type, _curdb);\r\n         }\r\n         catch (Exception ex)\r\n         {\r\n             Console.WriteLine(ex.ToString());\r\n         }\r\n     }\r\n }<\/span>\r\n<\/pre>\n<p>Il acc\u00e8de toujours \u00e0 la derni\u00e8re ligne car \u00e0 chaque fois qu&#8217;il en fetche une, il la supprime imm\u00e9diatement apr\u00e8s pour que le worker suivant puisse lire la ligne suivante (qui devient \u00e0 son tour la premi\u00e8re dans la liste). Une fois le kernel object rel\u00e2ch\u00e9, il a stock\u00e9 les informations n\u00e9cessaires (object_id et type d&#8217;objet) et peut appeler scriptThis() pour scripter l&#8217;objet courant. Pendant ce temps, un autre worker a obtenu la ressource et peut d\u00e9piler son objet, etc&#8230; jusqu&#8217;\u00e0 ce qu&#8217;il n&#8217;y ait plus d&#8217;\u00e9l\u00e9ments dans le DataTable. Allons voir maintenant ce que fait scriptThis():<\/p>\n<pre><span style=\"color: #008000;\">\/* =========================================================================================================\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Script de l'objet par appel de sa m\u00e9thode SMO Script()\r\n *\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n * Types remont\u00e9s par le DataTable:\r\n * 'P','FN','R','SN', 'U','V'\r\n * Revision 1.1: Attention aux objets encrypt\u00e9s, la m\u00e9thode Script() n'est pas invocable.\r\n *\/<\/span>\r\n <span style=\"color: #0000ff;\">static bool scriptThis(int _objid, string _type, Database _curdb)\r\n {\r\n     switch (_type)\r\n     {\r\n\r\n         case \"P\":\u00a0\u00a0 <span style=\"color: #008000;\">\/\/ PROCEDURES STOCKEES -----------------------------------------------------<\/span>\r\n             StoredProcedure _sp = _curdb.StoredProcedures.ItemById(_objid);\r\n\r\n             if (_sp.IsEncrypted == false)\r\n             {\r\n                 StringCollection _spscr = _curdb.StoredProcedures.ItemById(_objid).Script();\r\n                 foreach (string _line in _spscr)\r\n                     Console.WriteLine(_line);\r\n             }\r\n             Console.WriteLine(\"GO\");\r\n             break;\r\n\r\n         case \"FN\":\u00a0 <span style=\"color: #008000;\">\/\/ UDF ----------------------------------------------------------------------<\/span>\r\n             UserDefinedFunction _f = _curdb.UserDefinedFunctions.ItemById(_objid);\r\n             if (_f.IsEncrypted == false)\r\n             {\r\n                 StringCollection _fscr = _f.Script();\r\n                 foreach (string _line in _fscr)\r\n                     Console.WriteLine(_line);\r\n             }\r\n             Console.WriteLine(\"GO\");\r\n             break;\r\n\r\n         case \"R\":\u00a0\u00a0 <span style=\"color: #008000;\">\/\/ RULES -------------------------------------------------------------------<\/span>\r\n              StringCollection _ruscr = _curdb.Rules.ItemById(_objid).Script();\r\n              foreach (string _line in _ruscr)\r\n                  Console.WriteLine(_line);\r\n              Console.WriteLine(\"GO\");\r\n              break;\r\n\r\n         case \"SN\":\u00a0 <span style=\"color: #008000;\">\/\/ SYNONYMS -----------------------------------------------------------------<\/span>\r\n             StringCollection _synscr = _curdb.Synonyms.ItemById(_objid).Script();\r\n             foreach (string _line in _synscr)\r\n                 Console.WriteLine(_line);\r\n             Console.WriteLine(\"GO\");\r\n             break;\r\n\r\n         case \"U\":\u00a0\u00a0 <span style=\"color: #008000;\">\/\/ TABLES UTILISATEUR + CONTRAINTES + TRIGGERS + INDEXES -------------------<\/span>\r\n             Table _t = _curdb.Tables.ItemById(_objid);\r\n             StringCollection _tablescr = _curdb.Tables.ItemById(_objid).Script();\r\n\r\n             foreach (string _line in _tablescr)\r\n                 Console.WriteLine(_line);\r\n\r\n             IndexCollection _indexes = _t.Indexes;\r\n             foreach (Index _i in _indexes)\r\n             {\r\n                 StringCollection _iscr = _i.Script();\r\n                 foreach (string _line in _iscr)\r\n                     Console.WriteLine(_line);\r\n             }\r\n\r\n             CheckCollection _checks = _t.Checks;\r\n             foreach (Check _c in _checks)\r\n             {\r\n                 StringCollection _cscr = _c.Script();\r\n                 foreach (string _line in _cscr)\r\n                     Console.WriteLine(_line);\r\n             }\r\n\r\n             TriggerCollection _triggers = _t.Triggers;\r\n             foreach (Trigger _trg in _triggers)\r\n             {\r\n                 StringCollection _trgscr = _trg.Script();\r\n                 foreach (string _line in _trgscr)\r\n                     Console.WriteLine(_line);\r\n             }\r\n\r\n             <span style=\"color: #008000;\">\/\/ Pour les contraintes par d\u00e9faut c'est un peu diff\u00e9rent, il faut\r\n             \/\/ passer par chaque colonne de la table, et faire attention si la\r\n             \/\/ colonne ne contient pas de contrainte par d\u00e9faut ( != null)<\/span>\r\n             foreach (Column _col in _t.Columns)\r\n             {\r\n                 if (_col.DefaultConstraint != null)\r\n                 {\r\n                     StringCollection _def = _col.DefaultConstraint.Script();\r\n                     foreach (string _line in _def)\r\n                         Console.WriteLine(_line);\r\n                 }\r\n             }\r\n\r\n             Console.WriteLine(\"GO\");\r\n             break;\r\n\r\n        case \"V\":\u00a0\u00a0 <span style=\"color: #008000;\">\/\/ VUES UTILISATEUR -----------------------------------------------------<\/span>\r\n             View _v = _curdb.Views.ItemById(_objid);\r\n             if (_v.IsEncrypted == false)\r\n             {\r\n                  StringCollection _vscr = _v.Script();\r\n                  foreach (string _line in _vscr)\r\n                      Console.WriteLine(_line);\r\n             }\r\n             Console.WriteLine(\"GO\");\r\n             break;\r\n\r\n       default:\r\n            Console.WriteLine(\"Objet {0} non support\u00e9.\", _type);\r\n            break;\r\n     }\r\n\r\n return true;\r\n\r\n }<\/span>\r\n<\/pre>\n<p>L&#8217;objet est r\u00e9cup\u00e9r\u00e9 en passant son object_id \u00e0 la m\u00e9thode <em>ItemById()<\/em> \u00e0 chaque fois. Et en fonction du type, on applique la m\u00e9thode correspondante avec le principe expliqu\u00e9 au d\u00e9but de cet article. Lorsque le thread a termin\u00e9 la g\u00e9n\u00e9ration, il retourne bloquer \u00e0 nouveau sur le kernel object pour lire la ligne suivante. On remarquera le filtrage avec <em>isEncrypted <\/em>pour les proc\u00e9dures, les fonctions et les vues.<\/p>\n<h2>Conclusion:<\/h2>\n<p>Au final, le gain de passer en multithread est de plus de 30% rien que sur mon laptop avec 2 threads au lieu d&#8217;un seul. En plus en activant PARAMETERIZATION = FORCED, le temps de g\u00e9n\u00e9ration pour AdventureWorks tombe de 1 minute 30 \u00e0 20 secondes. A la prochaine !<\/p>\n<p>David B.<\/p>\n<p><script src=\"https:\/\/tcr.tynt.com\/javascripts\/Tracer.js?user=d4FlbGI04r35lZadbi-bpO\" type=\"text\/javascript\"><\/script><\/p>\n<a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-twitter nolightbox\" data-provider=\"twitter\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Twitter\" href=\"https:\/\/twitter.com\/intent\/tweet?url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136&#038;text=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"twitter\" title=\"Share on Twitter\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/twitter.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-linkedin nolightbox\" data-provider=\"linkedin\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Linkedin\" href=\"https:\/\/www.linkedin.com\/shareArticle?mini=true&#038;url=https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136&#038;title=G%C3%A9n%C3%A9rer%20le%20DDL%20complet%20d%E2%80%99une%20base%20en%20C%23%20%2F%20SMO%20%2F%20multithreading\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px;margin-right:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"linkedin\" title=\"Share on Linkedin\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/linkedin.png\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-24 synved-social-resolution-single synved-social-provider-mail nolightbox\" data-provider=\"mail\" rel=\"nofollow\" title=\"Share by email\" href=\"mailto:?subject=G%C3%A9n%C3%A9rer%20le%20DDL%20complet%20d%E2%80%99une%20base%20en%20C%23%20%2F%20SMO%20%2F%20multithreading&#038;body=Article%20sur%20le%20blog%20de%20la%20Capdata%20Tech%20Team%20%3A%20:%20https%3A%2F%2Fblog.capdata.fr%2Findex.php%2Fwp-json%2Fwp%2Fv2%2Fposts%2F1136\" style=\"font-size: 0px;width:24px;height:24px;margin:0;margin-bottom:5px\"><img loading=\"lazy\" decoding=\"async\" alt=\"mail\" title=\"Share by email\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"24\" height=\"24\" style=\"display: inline;width:24px;height:24px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/blog.capdata.fr\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/48x48\/mail.png\" \/><\/a>","protected":false},"excerpt":{"rendered":"<p>Pour faire suite \u00e0 un post sur d\u00e9veloppez concernant l&#8217;existence d&#8217;outils de scripting d&#8217;objets en dehors de ceux propos\u00e9s par SSMS, j&#8217;ai d\u00e9cid\u00e9 de regarder d&#8217;un peu plus pr\u00e8s le sujet et d&#8217;en cr\u00e9er un (getMsDDLmt) pour voir ce que&hellip; <a href=\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\" class=\"more-link\">Continuer la lecture <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":7942,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[158,161,162,160,159],"class_list":["post-1136","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-sqlserver","tag-c","tag-getmsddlmt","tag-multithreading","tag-script","tag-smo"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>G\u00e9n\u00e9rer le DDL complet d&#039;une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"G\u00e9n\u00e9rer le DDL complet d&#039;une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG\" \/>\n<meta property=\"og:description\" content=\"Pour faire suite \u00e0 un post sur d\u00e9veloppez concernant l&#8217;existence d&#8217;outils de scripting d&#8217;objets en dehors de ceux propos\u00e9s par SSMS, j&#8217;ai d\u00e9cid\u00e9 de regarder d&#8217;un peu plus pr\u00e8s le sujet et d&#8217;en cr\u00e9er un (getMsDDLmt) pour voir ce que&hellip; Continuer la lecture &rarr;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\" \/>\n<meta property=\"og:site_name\" content=\"Capdata TECH BLOG\" \/>\n<meta property=\"article:published_time\" content=\"2010-07-29T22:05:08+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-11-21T15:56:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2010\/08\/code2.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"852\" \/>\n\t<meta property=\"og:image:height\" content=\"480\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"David Baffaleuf\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u00c9crit par\" \/>\n\t<meta name=\"twitter:data1\" content=\"David Baffaleuf\" \/>\n\t<meta name=\"twitter:label2\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\"},\"author\":{\"name\":\"David Baffaleuf\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf\"},\"headline\":\"G\u00e9n\u00e9rer le DDL complet d&#8217;une base en C# \/ SMO \/ multithreading\",\"datePublished\":\"2010-07-29T22:05:08+00:00\",\"dateModified\":\"2022-11-21T15:56:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\"},\"wordCount\":1881,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/blog.capdata.fr\/#organization\"},\"keywords\":[\"C#\",\"getmsddlmt\",\"multithreading\",\"SCRIPT\",\"SMO\"],\"articleSection\":[\"SQL Server\"],\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\",\"url\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\",\"name\":\"G\u00e9n\u00e9rer le DDL complet d'une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG\",\"isPartOf\":{\"@id\":\"https:\/\/blog.capdata.fr\/#website\"},\"datePublished\":\"2010-07-29T22:05:08+00:00\",\"dateModified\":\"2022-11-21T15:56:34+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/blog.capdata.fr\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"G\u00e9n\u00e9rer le DDL complet d&#8217;une base en C# \/ SMO \/ multithreading\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.capdata.fr\/#website\",\"url\":\"https:\/\/blog.capdata.fr\/\",\"name\":\"Capdata TECH BLOG\",\"description\":\"Le blog technique sur les bases de donn\u00e9es de CAP DATA Consulting\",\"publisher\":{\"@id\":\"https:\/\/blog.capdata.fr\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.capdata.fr\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/blog.capdata.fr\/#organization\",\"name\":\"Capdata TECH BLOG\",\"url\":\"https:\/\/blog.capdata.fr\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp\",\"contentUrl\":\"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp\",\"width\":800,\"height\":254,\"caption\":\"Capdata TECH BLOG\"},\"image\":{\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/company\/cap-data-consulting\/mycompany\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf\",\"name\":\"David Baffaleuf\",\"sameAs\":[\"http:\/\/www.capdata.fr\"],\"url\":\"https:\/\/blog.capdata.fr\/index.php\/author\/dbaffaleuf\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"G\u00e9n\u00e9rer le DDL complet d'une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/","og_locale":"fr_FR","og_type":"article","og_title":"G\u00e9n\u00e9rer le DDL complet d'une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG","og_description":"Pour faire suite \u00e0 un post sur d\u00e9veloppez concernant l&#8217;existence d&#8217;outils de scripting d&#8217;objets en dehors de ceux propos\u00e9s par SSMS, j&#8217;ai d\u00e9cid\u00e9 de regarder d&#8217;un peu plus pr\u00e8s le sujet et d&#8217;en cr\u00e9er un (getMsDDLmt) pour voir ce que&hellip; Continuer la lecture &rarr;","og_url":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/","og_site_name":"Capdata TECH BLOG","article_published_time":"2010-07-29T22:05:08+00:00","article_modified_time":"2022-11-21T15:56:34+00:00","og_image":[{"width":852,"height":480,"url":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2010\/08\/code2.jpg","type":"image\/jpeg"}],"author":"David Baffaleuf","twitter_card":"summary_large_image","twitter_misc":{"\u00c9crit par":"David Baffaleuf","Dur\u00e9e de lecture estim\u00e9e":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#article","isPartOf":{"@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/"},"author":{"name":"David Baffaleuf","@id":"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf"},"headline":"G\u00e9n\u00e9rer le DDL complet d&#8217;une base en C# \/ SMO \/ multithreading","datePublished":"2010-07-29T22:05:08+00:00","dateModified":"2022-11-21T15:56:34+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/"},"wordCount":1881,"commentCount":0,"publisher":{"@id":"https:\/\/blog.capdata.fr\/#organization"},"keywords":["C#","getmsddlmt","multithreading","SCRIPT","SMO"],"articleSection":["SQL Server"],"inLanguage":"fr-FR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/","url":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/","name":"G\u00e9n\u00e9rer le DDL complet d'une base en C# \/ SMO \/ multithreading - Capdata TECH BLOG","isPartOf":{"@id":"https:\/\/blog.capdata.fr\/#website"},"datePublished":"2010-07-29T22:05:08+00:00","dateModified":"2022-11-21T15:56:34+00:00","breadcrumb":{"@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.capdata.fr\/index.php\/generer-le-ddl-complet-dune-base-en-csmo\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/blog.capdata.fr\/"},{"@type":"ListItem","position":2,"name":"G\u00e9n\u00e9rer le DDL complet d&#8217;une base en C# \/ SMO \/ multithreading"}]},{"@type":"WebSite","@id":"https:\/\/blog.capdata.fr\/#website","url":"https:\/\/blog.capdata.fr\/","name":"Capdata TECH BLOG","description":"Le blog technique sur les bases de donn\u00e9es de CAP DATA Consulting","publisher":{"@id":"https:\/\/blog.capdata.fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.capdata.fr\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/blog.capdata.fr\/#organization","name":"Capdata TECH BLOG","url":"https:\/\/blog.capdata.fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/","url":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp","contentUrl":"https:\/\/blog.capdata.fr\/wp-content\/uploads\/2023\/01\/logo_capdata.webp","width":800,"height":254,"caption":"Capdata TECH BLOG"},"image":{"@id":"https:\/\/blog.capdata.fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/company\/cap-data-consulting\/mycompany\/"]},{"@type":"Person","@id":"https:\/\/blog.capdata.fr\/#\/schema\/person\/136297da9f61d6e4878abe0f48bc5fbf","name":"David Baffaleuf","sameAs":["http:\/\/www.capdata.fr"],"url":"https:\/\/blog.capdata.fr\/index.php\/author\/dbaffaleuf\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/1136","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/comments?post=1136"}],"version-history":[{"count":151,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/1136\/revisions"}],"predecessor-version":[{"id":9514,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/posts\/1136\/revisions\/9514"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/media\/7942"}],"wp:attachment":[{"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/media?parent=1136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/categories?post=1136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.capdata.fr\/index.php\/wp-json\/wp\/v2\/tags?post=1136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}