SQL http://blogs.ugidotnet.org/PietroLibroBlog/category/SQL.aspx SQL it-IT Pietro Libro pietro.libro@libero.it Subtext Version 2.6.0.0 Documentazione EF 7 (Beta 5) http://blogs.ugidotnet.org/PietroLibroBlog/archive/2015/07/22/documentazione-ef-7-beta-5.aspx Documentazione "Work in Progress" della prossima release di Entity Framework : <a href="http://ef.readthedocs.org/en/latest/" target="_blank">http://ef.readthedocs.org/en/latest/</a><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/102030.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2015/07/22/documentazione-ef-7-beta-5.aspx Wed, 22 Jul 2015 09:07:28 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2015/07/22/documentazione-ef-7-beta-5.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/102030.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/102030.aspx EF 6.1 : What&rsquo;s new (3) http://blogs.ugidotnet.org/PietroLibroBlog/archive/2014/03/27/ef-6.1-whatrsquos-new-3.aspx <p>Supporto a “.ToString()” e “String.Concat()”, un esempio:</p> <pre class="csharpcode">var queryConcat = from c <span class="kwrd">in</span> db.Vehicles <span class="kwrd">where</span> <span class="kwrd">string</span>.Concat(c.EngineSize, c.HP).Equals(<span class="str">"1600110"</span>) select c; var queryToString = from c <span class="kwrd">in</span> db.Vehicles <span class="kwrd">where</span> c.HP.ToString().Equals(<span class="str">"110"</span>) select c;</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p> </p> <p>Abbiamo due Query LINQ che filtrano i dati in base a condizioni su stringhe, la prima “tira fuori” tutti i veicoli dove la concatenazione dei valori delle proprietà “EngineSize” e “HP” è uguale a “1600110”, mentre la seconda esegue un filtro su di un valore intero convertio in stringa. Se proviamo ad eseguire il codice in un ambiente con EF 6.0 otteniamo un’eccezione a runtime in tutti e due i casi:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6.1--Whats-new-3_7B13/image_2.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; border-left: 0px; display: block; padding-right: 0px; margin-right: auto" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6.1--Whats-new-3_7B13/image_thumb.png" width="244" height="223" /></a></p> <p>Con EF 6.1 le query sono eseguite correttamente:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6.1--Whats-new-3_7B13/image_4.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; border-left: 0px; display: block; padding-right: 0px; margin-right: auto" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6.1--Whats-new-3_7B13/image_thumb_1.png" width="244" height="165" /></a></p> <p>Cosa succede dietro le quinte:</p> <pre class="csharpcode"><span class="kwrd">SELECT</span> [GroupBy1].[A1] <span class="kwrd">AS</span> [C1] <span class="kwrd">FROM</span> ( <span class="kwrd">SELECT</span> <span class="kwrd">COUNT</span>(1) <span class="kwrd">AS</span> [A1] <span class="kwrd">FROM</span> [DomusDotNet].[Vehicles] <span class="kwrd">AS</span> [Extent1] <span class="kwrd">WHERE</span> N<span class="str">'1600110'</span> = (<span class="kwrd">CASE</span> <span class="kwrd">WHEN</span> ([Extent1].[EngineSize] <span class="kwrd">IS</span> <span class="kwrd">NULL</span>) <span class="kwrd">THEN</span> N<span class="str">''</span> <span class="kwrd">ELSE</span> [Extent1].[EngineSize] <span class="kwrd">END</span> + <span class="kwrd">CAST</span>( [Extent1].[HP] <span class="kwrd">AS</span> nvarchar(<span class="kwrd">max</span>))) ) <span class="kwrd">AS</span> [GroupBy1] <span class="kwrd">SELECT</span> [GroupBy1].[A1] <span class="kwrd">AS</span> [C1] <span class="kwrd">FROM</span> ( <span class="kwrd">SELECT</span> <span class="kwrd">COUNT</span>(1) <span class="kwrd">AS</span> [A1] <span class="kwrd">FROM</span> [DomusDotNet].[Vehicles] <span class="kwrd">AS</span> [Extent1] <span class="kwrd">WHERE</span> N<span class="str">'110'</span> = <span class="kwrd">CAST</span>( [Extent1].[HP] <span class="kwrd">AS</span> nvarchar(<span class="kwrd">max</span>)) ) <span class="kwrd">AS</span> [GroupBy1]</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>Da analizzare il funzionamento di “String.Concat(…)” con SQL Server 2012 per verificare la conversione in SQL con la funzione “nativa SQL” “<a href="http://technet.microsoft.com/en-us/library/hh231515.aspx" target="_blank">Concat</a>”.</p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/101829.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2014/03/27/ef-6.1-whatrsquos-new-3.aspx Thu, 27 Mar 2014 10:04:00 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2014/03/27/ef-6.1-whatrsquos-new-3.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/101829.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/101829.aspx EF 6 : Logging &amp; Interception http://blogs.ugidotnet.org/PietroLibroBlog/archive/2013/09/04/ef-6-logging-amp-interception.aspx <p> </p> <p>Una delle nuove feature introdotte nella versione 6 di Entity Framework (attualmente in RC) è il supporto al <em>logging</em> dell’SQL generato dal runtime di EF6. A tal fine è sufficiente passare un opportuno <em>delegate</em> alla proporietà <em>Log</em> esposta da <em>DbContext.Database</em>. Per gli esempi riprendiamo lo scenario del <a href="http://blogs.ugidotnet.org/PietroLibroBlog/archive/2013/08/19/ef6-beta-multiple-contexts-per-database-multi-tenant-migrations.aspx" target="_blank">post precedente</a>. </p> <p>Supponiamo di avere il seguente codice:</p> <pre class="csharpcode"> <span class="kwrd">using</span> (CarContext db = <span class="kwrd">new</span> CarContext()) { System.Console.WriteLine(<span class="str">"Cars in database : {0}"</span>, db.Cars.Count()); <span class="rem">////Add a new car.</span> Car car = <span class="kwrd">new</span> Car() { Brand = <span class="str">"Alfa Romeo"</span>, Model = <span class="str">"Giulietta"</span> }; db.Cars.Add(car); db.SaveChanges(); System.Console.WriteLine(<span class="str">"Cars in database : {0}"</span>, db.Cars.Count()); }</pre> <p /><style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>Che banalmente visualizza il numero di auto (Cars) presenti nel  <em>repository</em>, ne aggiunge una e riesegue la conta delle auto registrate. Se volessimo analizzare l’SQL generato un ottimo strumento è sicuramete il SQL Profiler di SQL Server (che non dovrebbe mai mancare quando si lavora con un ORM <img class="wlEmoticon wlEmoticon-smile" style="border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none" alt="Smile" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/wlEmoticon-smile_2.png" /> ), ma se utilizziamo EF6, ed il nostro obiettivo è semplicemente “scoprire\loggare” l’SQL generato da EF, possiamo modificare il codice precedente aggiungendo dopo la definizione del <em>DbContext,</em> la riga seguente:</p> <pre class="csharpcode"> db.Database.Log = Console.Write;</pre> <pre class="csharpcode"> </pre> <p>Dove la proprietà <em>Log</em> è cosi’ definita:</p> <pre class="csharpcode"><span class="kwrd">public</span> Action&lt;<span class="kwrd">string</span>&gt; Log { get; set; }</pre> <pre class="csharpcode"> </pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>Eseguendo il codice della nostra applicazione dovremmo ottenere una “Console” simile alla seguente:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_2.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_thumb.png" width="244" height="172" /></a></p> <p>Di default il log è molto chiaro in quanto ci restituisce il testo SQL del comando, quando è stato eseguito, il tempo impiegato, il tipo di risultato, gli eventuali parametri ed il tipo corrispondente. Possiamo ovviamente cambiare il contenuto e la formattazione del log secondo le nostre esigenze (e non solo) come vedremo piu’ avanti. Per come è definita la proprietà <em>Log</em>, qualsiasi funzione che accetta una stringa puo’ essere utilizzata per scrivere il nostro log. Quindi potremmo utilizzare un metodo tipo il seguente:</p> <pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Log(<span class="kwrd">string</span> sql) { Console.WriteLine(<span class="str">"------------------------------------"</span>); Console.WriteLine(<span class="str">"SQL LOG: {0} "</span>, sql); Console.WriteLine(<span class="str">"------------------------------------"</span>); }</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p> </p> <p>oppure:</p> <pre class="csharpcode">System.IO.StreamWriter streamWriter = <span class="kwrd">new</span> System.IO.StreamWriter (fileStream, System.Text.ASCIIEncoding.ASCII ); db.Database.Log = sql =&gt; streamWriter.Write(sql);</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p> </p> <p>Per scrivere il log direttamente su file. Il concetto dovrebbe essere abbastanza chiaro. Vediamo come personalizzare la formattazione del log. </p> <p><em>Database.Log</em> non fa altro che utilizzare un ogetto <em>DatabaseLogFormatter </em>che a sua volta implementa le interfacce <em>IDbCommandInterceptor</em> e <em>IDbInterceptor </em>che sono rispettivamente l’interfaccia che permette di registrare un oggetto che possa ricevere notifiche da parte di Entity Framework quando un comando viene eseguito e l’interfaccia base per tutte le interfacce che permettono di fornire un <em>Interception Point</em> per differenti tipi di operazione.</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_4.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_thumb_1.png" width="134" height="244" /></a></p> <p>Quindi, per creare il nostro “Formattatore Custom” non ci resta che ereditare dalla classe <em>DatabaseLogFormatter</em> ed eseguire <em>l’override</em> dei metodi che ci interessano, come nel caso seguente:</p> <pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> CustomLogFormatter : DatabaseLogFormatter { <span class="kwrd">public</span> CustomLogFormatter(DbContext context, Action&lt;<span class="kwrd">string</span>&gt; writeAction) : <span class="kwrd">base</span>(context, writeAction) { } <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> LogCommand&lt;TResult&gt;( DbCommand command, DbCommandInterceptionContext&lt;TResult&gt; interceptionContext) { Write(<span class="kwrd">string</span>.Format(<span class="str">"{0}{1}"</span>, <span class="str">"==============================================="</span>, Environment.NewLine)); Write(<span class="kwrd">string</span>.Format( <span class="str">"Context{2}{0}{2}Command{2}{1}{2}"</span>, Context.GetType().Name, command.CommandText.Replace(Environment.NewLine, <span class="str">""</span>), Environment.NewLine)); Write(<span class="kwrd">string</span>.Format(<span class="str">"{0}{1}"</span>, <span class="str">"==============================================="</span>, Environment.NewLine)); } }</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p> </p> <p>Dove, l’<em>override</em> del metodo <em>LogCommand </em>esegue la formattazione del comando prima che questo sia eseguito da Entity Framework<em>. LogCommand</em> a sua volta  chiama eventualmente il metodo <em>LogParameter</em> per ogni parametro del comando, è necessario eseguire l’override di <em>LogParameter </em>per “customizzare” la formattazione del log dei paramateri. Infine, se vogliamo cambiare la formattazione del risultato, è necessario effettuare l’override di <em>LogResult. </em>Per interrompere l’esecuzione del comando, possiamo utilizzare il metodo :</p> <pre class="csharpcode">interceptionContext.SuppressExecution()</pre> <pre class="csharpcode"> </pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>Nel corpo dell’<em>override</em> di <em>LogCommand. </em>Per vedere in “funzione” il nostro “Formatter” è sufficiente registrare l’istanza di <em>CustomLogFormatter</em> tramite una classe derivata da <em>DbConfiguration</em> presente nello stesso assembly che contiene la definizione del <em>DbContext:</em></p> <pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> MyDbConfiguration : DbConfiguration { <span class="kwrd">public</span> MyDbConfiguration() { SetDatabaseLogFormatter( (context, writeAction) =&gt; <span class="kwrd">new</span> CustomLogFormatter(context, writeAction)); } }</pre> <style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <p>Eseguendo, otteniamo il “nostro” log:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_6.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/EF-6--Logging--Interceptor_6F20/image_thumb_2.png" width="244" height="118" /></a></p> <p>Per approfondimenti:</p> <p><a title="https://entityframework.codeplex.com/wikipage?title=Interception" href="https://entityframework.codeplex.com/wikipage?title=Interception" target="_blank">Entity Framework Codeplex</a></p> <p><a href="http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/" target="_blank">One Unicorn: EF6 SQL Logging</a></p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/101658.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2013/09/04/ef-6-logging-amp-interception.aspx Wed, 04 Sep 2013 15:15:00 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2013/09/04/ef-6-logging-amp-interception.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/101658.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/101658.aspx OCA, Sync Framework e SQL Server 2008 http://blogs.ugidotnet.org/PietroLibroBlog/archive/2011/08/21/oca-e-sync-framework-e-sql-server-2008.aspx <p> </p> <p>Ultimamente mi sono trovato a sviluppare un’applicazione con la necessità di sincronizzare i dati locali con un’istanza di SQL Server 2008. Generalmente parlando, le applicazioni OCA (<em>occasionally connected application</em>) permettono di utilizzare un’applicazione <em>client</em> che fa uso di dati memorizzati in una base di dati locale che periodicamente è sincronizzata  con un database centrale disposto su un <em>server.</em>  Il processo di sincronizzazione non è mai stato un task banale (almeno nel caso bidirezionale), è penso che ognuno di noi, almeno una volta nella vita da sviluppatore si sia imbattuto in questo tipo di operazione. Fino a qualche tempo fa, spesso si creavano soluzioni ad hoc, funzionanti, ma magari non performanti o comunque a basso riuso. Per fortuna (tra l’altro da diverso tempo <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Sorriso" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/wlEmoticon-smile_2.png" />) abbiamo a disposizione il  <a href="http://msdn.microsoft.com/sync" target="_blank">Microsoft Sync Framework</a> (che personalmente, fino ad oggi non ho mai avuto occasione di utilizzare in modo proficuo), una piattaforma  che fornisce agli sviluppatori gli strumenti necessari per aggiungere funzionalità di sincronizzazione ad applicazioni, servizi e dispositivi. Nello specifico del post,  ci concentreremo sul funzionamento relativo alla sincronizzazione di database, ma il <em>Microsoft Sync Framework</em> mette a disposizione i <em>Synchronization Services </em>per <em>File Systems</em> ed i <em>Synchronization Services</em> per <em>FeedSync</em>. La versione presente in Visual Studio 2010 è la 2.1, ma è possibile scaricare la versione <a href="http://www.microsoft.com/download/en/details.aspx?id=12012" target="_blank">4.0 CTP</a> , costruita sulla versione 2.1, con supporto al protocollo   “OData + Sync” per lo sviluppo più facile di applicazioni <em>OCA</em> su qualsiasi piattaforma capace di eseguire il <em>caching</em> dei dati oltre alla sincronizzazione di dati memorizzati su SQL Server o SQL Azure.</p> <p>Durante il processo di sincronizzazione l’aspetto più complesso è sicuramente la tracciatura dei cambiamenti (ovvero i relativi INSERT, UPDATE e DELETE eseguiti sul database <em>Client</em> e\o <em>Server</em>), altrimenti, ogni volta che l’utente si collega al database centrale per la sincronizzazione dei dati deve necessariamente eseguire il download di tutte le informazioni  (in situazioni di connessioni lente o non affidabili potrebbe essere un problema di costi e\o tempi). Un metodo <em>naive</em> per eseguire il <em>tracking</em> dei cambiamenti è l’utilizzo di trigger o l’utilizzo di campi <em>rowversion</em> aggiunte alle tabelle soggette a sincronizzazione. Per la cancellazione dei record il discorso è leggermente diverso in quanto è necessario memorizzare le informazioni in una tabella separata (<em>tombstone table</em>) . Ovviamente, questa “metodologia” comporta alcuni svantaggi:</p> <ul> <li>- E’ necessario introdurre modifiche allo schema della base di dati </li> <li>- I trigger comportano problemi di performance</li> <li>- Scrittura della logica per <em>rowversion</em> e cancellazione delle righe</li> </ul> <p>Se utilizziamo SQL Server 2008 come database centrale, possiamo evitare i problemi precedentemente esposti in quanto in questa versione è stata introdotto il “SQL Server 2008 Change Tracking”, un meccanismo il cui utilizzo si “riduce” a marcare le tabelle da monitorare,  tracciando automaticamente le varie INSERT, UPDATE e DELETE, così da fornire gli opportuni cambiamenti ai client che richiedono di sincronizzarsi rispetto all’ultima operazione di sincronizzazione. Utilizzando il Sync Framework abbiamo questo supporto in modo nativo ed integrato in Visual Studio 2010 senza apportare modifiche allo schema del database, senza trigger e senza dover scrivere codice SQL di logica (maggiori dettagli su <a href="http://msdn.microsoft.com/en-us/sync/bb887608" target="_blank">MSDN</a>). </p> <p>Proviamo a chiarire i concetti esposti con un esempio. Prendiamo in considerazione un database con un schema del tipo in figura:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_25.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_11.png" width="244" height="185" /></a></p> <p>Che descrive un semplicissimo database di un possibile e-commerce (Cliente, Acquisto e Prodotto). Il nostro scopo è sincronizzare i dati tra un client (una piccola applicazione stand-alone di esempio) che utilizza una database locale di SQL Server Compact 3.5 (od eventualmente 4.0 con le apposite modifiche) ed il database centrale presente in un’istanza di SQL Server 2008.</p> <p>Creiamo un nuovo progetto Console o Windows Form (nel nostro caso) di Visual Studio 2010 a cui andiamo ad aggiungere il necessario per fornire l’accesso alla sincronizzazione dei dati. Dopo la creazione del progetto, aggiungiamo un nuovo item (dalla sezione Data) “Local Database Cache”:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_3.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb.png" width="405" height="281" /></a></p> <p>A questo punto verrà visualizzata una nuova finestra nella quale andremo a specificare i parametri di connessione lato <em>server</em> e lato <em>client</em>:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_1.png" width="243" height="166" /></a></p> <p>Se  stiamo utilizzando una connessione verso un database di SQL Server 2008, il Check<em> “</em>User SQL Server change Tracking”<em> </em>dovrebbe essere abilitato, e spuntando la voce, abbiamo la possibilità di sfruttare il Change Tracking integrato. Ancora, possiamo scegliere se l’operazione di sincronizzazione avverrà all’interno di un’unica transazione (se vogliamo che tutti  o nessuno dei dati siano scaricati) oppure (secondo dello scenario)  se vogliamo scaricare\caricare ad ogni sincronizzazione quanti più dati possibili. Restando nella finestra, sono presenti delle liste a discesa che permettono di scegliere, all’interno della nostra soluzione di Visual Studio il progetto lato <em>client</em> ed il progetto lato <em>server</em>. Nel nostro caso lasciamo la voce selezionata di Default, e quindi consideriamo il progetto precedentemente creato. E’ arrivato il momento di scegliere le tabelle che faranno parte della nostra cache di dati locale: l’operazione è molto semplice in quanto è sufficiente utilizzare il bottone con la dicitura <em>Add</em>  in fondo a sinistra della <em>ListBox</em> presente: </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_19.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_8.png" width="244" height="167" /></a></p> <p>Nella finestra “Configure Tables for Offline Use” è possibile selezionare le tabelle che vogliamo sincronizzare con i nostri client. Per ognuna di essa possiamo scegliere se ogni volta vogliamo scaricare l’interno contenuto della tabella (come durante la prima sincronizzazione)  o se  ogni successiva sincronizzazione sial di tipo incrementale. Nel caso stessimo utilizzando una versione diversa da SQL Server 2008 (ad esempio SQL Server 2005) come database centrale, non avendo a disposizione il supporto nativo al <em>Change Tracking</em> avremmo dovuto  specificare delle <em>Stored Procedure ad-hoc</em> nei campi <em>Compare update using</em>, <em>Compare insert using </em>e <em>Move deleted items to</em> a supporto della logica di sincronizzazione:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_11.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_4.png" width="244" height="151" /></a></p> <p>A questo punto il gioco è quasi finito, in quanto premendo su <em>OK, </em>al progetto verranno aggiunti tutti i file e riferimenti necessari al funzionamento. Piccola nota: nella schermata precedente premendo il link <em>Show Code Example</em> viene visualizzata una finestra di dialogo con del codice di esempio (molto comodo se si utilizza il Sync Framework per la prima volta) per effettuare la sincronizzazione dei dati:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_13.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_5.png" width="244" height="85" /></a></p> <p>A questo punto viene avviata la prima sincronizzazione con la creazione del file “.sdf” di SQL Server Compact (ricordiamo per default nella versione 3.5).  Dopo qualche secondo è visualizzata una nuova schermata in cui possiamo scegliere se utilizzare un DataSet o un file EDM (<em>Entity Data Model</em>) per il nostro <em>Object Model as Data Model</em>. Partiamo con un semplice DataSet ( <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Sorriso" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/wlEmoticon-smile_2.png" /> ):</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_17.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_7.png" width="244" height="177" /></a></p> <p>Nelle schermate che si susseguono andiamo a configurare il nostro DataSet  tipizzato con tutte le tabelle che servono al funzionamento della nostra applicazione <em>client</em>. Notiamo che oltre alle tabelle originali troviamo tre tabelle __<em>syncArticles</em>, _<em>syncSubscriptions</em> e __<em>syncTransactioncs</em> necessarie per le operazioni di sincronizzazione, ma possiamo tranquillamente ignorarle. Alla fine del Wizard, il nostro DataSet dovrebbe assomigliare a qualcosa di questo tipo:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_27.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_12.png" width="244" height="84" /></a></p> <p>Bene. Supponendo di aver aggiunto alla nostra soluzione una Windows Form contenente una griglia e due bottoni (<em>Synchronize </em>e <em>Close</em>) come nella figura seguente:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_33.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_15.png" width="244" height="136" /></a></p> <p>Nell’<em>handler </em>dell’evento click del bottone <em>Synchronize </em>possiamo scrivere del codice tipo:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <pre id="codeSnippet" class="csharpcode">LocalCacheSyncAgent syncAgent = <span class="kwrd">new</span> LocalCacheSyncAgent();<br />syncAgent.Product.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;<br /><br /><br /><br />Microsoft.Synchronization.Data.SyncStatistics syncStats = syncAgent.Synchronize();<br /><br /><span class="rem">////Server -&gt; Client</span><br />StringBuilder sb = <span class="kwrd">new</span> StringBuilder();<br /><br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Download Changes: {0}"</span>, syncStats.DownloadChangesApplied));<br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Download Failed: {0}"</span>, syncStats.DownloadChangesFailed));<br /><br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Upload Changes: {0}"</span>, syncStats.UploadChangesApplied));<br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Upload Failed: {0}"</span>, syncStats.UploadChangesFailed));<br /><br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Start Time: {0}"</span>, syncStats.SyncStartTime));<br />sb.AppendLine(<span class="kwrd">string</span>.Format(<span class="str">"Complete Time: {0}"</span>, syncStats.SyncCompleteTime));<br /><br />MessageBox.Show(sb.ToString());</pre> </div> <p>Dove come prima operazione creiamo un’istanza di <em>LocalCacheSyncAgent</em>, ovvero l’engine che si occuperà di effettuare materialmente il lavoro di sincronizzazione. Impostiamo tramite la proprietà <em>SyncDirection </em>una sincronizzazione di tipo <em>Bidirezionale</em> per la tabella <em>Product</em>. Altre possibili opzioni sono:</p> <ul> <li>- <em>Download</em>, sincronizzazione unidirezionale Server –&gt; Client </li> <li>- <em>Upload</em>, sincronizzazione unidirezionale Client –&gt; Server </li> <li>- <em>Snapshot</em>, durante la sincronizzazione viene scaricato un insieme di dati,  aggiornato completamente ad ogni sincronizzazione. </li> </ul> <p>A questo punto non resta che invocare il metodo <em>Synchronize</em> del nostro “Agent” che restituisce un’istanza di <em>SyncStatistics</em> contenente le informazioni sull’esito della sincronizzazione, che “raccogliamo” mediante uno <em>StringBuilder</em> e mostriamo all’utente al termine delle operazioni con una semplice MessageBox:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_31.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_14.png" width="244" height="189" /></a></p> <p>Sopra è mostrato il risultato della prima sincronizzazione. Aggiungendo una riga in <em>Product</em> nella tabella locale (client), e sincronizzando nuovamente dovremmo ottenere un risultato di questo tipo:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_35.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_16.png" width="244" height="189" /></a></p> <p>Dove chiaramente si evince come sia stato eseguito l’upload di dati verso il server. Ok, ma ovviamente non tutte le tabelle escono col buco, di conseguenza bisogna prepararsi ad ogni evenienza, ovvero alla gestione di possibili conflitti tra dati presenti su <em>Client</em> e <em>Server</em> durante la sincronizzazione. Modifichiamo la parte iniziale del codice precedente in questo modo:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <pre id="codeSnippet" class="csharpcode">LocalCacheSyncAgent syncAgent = <span class="kwrd">new</span> LocalCacheSyncAgent();<br />syncAgent.Product.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;<br /><span class="rem">////-----</span><br />LocalCacheClientSyncProvider localProvider = syncAgent.LocalProvider <span class="kwrd">as</span> LocalCacheClientSyncProvider;<br />LocalCacheServerSyncProvider serverProvider = syncAgent.RemoteProvider <span class="kwrd">as</span> LocalCacheServerSyncProvider;<br /><br />localProvider.ApplyChangeFailed += <span class="kwrd">new</span> EventHandler&lt;Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs&gt;(localProvider_ApplyChangeFailed);<br />serverProvider.ApplyChangeFailed += <span class="kwrd">new</span> EventHandler&lt;Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs&gt;(serverProvider_ApplyChangeFailed);<br /><span class="rem">////-----</span><br /><br />Microsoft.Synchronization.Data.SyncStatistics syncStats = syncAgent.Synchronize();<br />...</pre> </div> <p>Dove attraverso istanze di <em>LocalCacheClientSyncProvider</em> e <em>LocalCacheServerSyncProvicer</em> ci sottoscriviamo agli eventi per la gestione dei conflitti, <em>ApplyChangedFailed</em>:</p> <div id="codeSnippetWrapper" class="csharpcode-wrapper"> <pre id="codeSnippet" class="csharpcode"><span class="kwrd">if</span>(e.Conflict.ConflictType == Microsoft.Synchronization.Data.ConflictType.ClientInsertServerInsert){<br /> DataTable clientChange = e.Conflict.ClientChange;<br /> DataTable serverChange = e.Conflict.ServerChange;<br /><br /> FrmConflicts frmConflicts = <span class="kwrd">new</span> FrmConflicts();<br /> frmConflicts.SetDataSource(clientChange, serverChange);<br /> frmConflicts.ShowDialog(<span class="kwrd">this</span>);<br /><br /> e.Action = Microsoft.Synchronization.Data.ApplyAction.RetryWithForceWrite;<br />}<br /><br /><span class="kwrd">if</span> (e.Conflict.ConflictType == Microsoft.Synchronization.Data.ConflictType.ErrorsOccurred)<br />{<br /> MessageBox.Show(e.Conflict.ErrorMessage);<br /> e.Action = Microsoft.Synchronization.Data.ApplyAction.Continue;<br />}</pre> </div> <p>Nel codice intercettiamo due dei possibili conflitti che possono verificarsi: nel caso di conflitto dovuto ad  <em>Insert</em> su <em>Client</em> e <em>Server (</em>ad esempio su <em>client</em> e <em>server</em> viene aggiunto uno stesso prodotto<em>)</em>, mostriamo all’utente tramite una seconda Windows Form (FrmConflicts) il dati che creano il problema , e dopo forziamo la scrittura (causando di fatto una duplicazione dei valori all’interno della tabella <em>Product</em>, ovviamente possono\devono essere considerate altre strategie scelte eventualmente dall’utente). Nel caso di conflitto dovuto ad errore non predicibile mostriamo (in modo molto brutale <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Sorriso" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/wlEmoticon-smile_2.png" />) un MessageBox contenente informazioni di errore (in un’applicazione reale questo dovrebbe essere evitato in quanto potrebbero essere visualizzate informazioni sensibili per la sicurezza del sistema) e tentiamo di continuare la sincronizzazione dei dati.</p> <p>Se invece di utilizzare un DataSet volessimo utilizzare Entity Framework, il gioco è semplice, basta utilizzare il <em>Wizard</em> dell’Entity Data Model per costruire un modello da un database già presente (il file .sdf) . Nello specifico caso, avremmo qualcosa di questo tipo:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_39.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/image_thumb_18.png" width="207" height="244" /></a></p> <p>Le operazioni di sincronizzazione a questo punto non sono più un problema (o almeno si spera <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Sorriso" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/Windows-Live-Writer/Sync-Framework_5BBC/wlEmoticon-smile_2.png" />).</p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/100287.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2011/08/21/oca-e-sync-framework-e-sql-server-2008.aspx Sun, 21 Aug 2011 14:08:00 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2011/08/21/oca-e-sync-framework-e-sql-server-2008.aspx#feedback 2 http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/100287.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/100287.aspx The setup failed to read IIsMimeMap table&hellip; http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/01/11/the-setup-failed-to-read-iismimemap-tablehellip.aspx <p>Finalmente sono riuscito a ritagliare del tempo per formattare e ripristinare il mio portatile, prima che arrivi il mio periodo “caldo” (causa lavoro e Università). In questi giorni, mi è capitato di dover rimuovere da un PC, SQL Server 2005, nel caso in cui durante il processo di disinstallazione si presenti il messaggio : <em>Il programma di installazione non riesce a leggere tabella IIsMimeMap. Il codice di errore è-2147024893 (o The setup failed to read IIsMimeMap table. The error code is –2147024893 )</em></p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ThesetupfailedtoreadIIsMimeMaptable_11E16/image_2.png"><img height="86" border="0" width="244" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ThesetupfailedtoreadIIsMimeMaptable_11E16/image_thumb.png" alt="image" style="border-width: 0px; display: inline;" title="image" /></a> </p> <p>la soluzione è in <a href="http://support.microsoft.com/?kbid=921901">questo</a> articolo, in pratica si tratta di disabilitare in sequenza i servizi <em>Servizio  </em>di <em>Amministrazione di IIS</em> e <em>Servizio</em> <em>Pubblicazione sul Web </em>prima di procedere con la<em> </em>disinstallazione </p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/95167.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/01/11/the-setup-failed-to-read-iismimemap-tablehellip.aspx Sun, 11 Jan 2009 21:34:00 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/01/11/the-setup-failed-to-read-iismimemap-tablehellip.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/95167.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/95167.aspx The database principal...and cannot be dropped http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/18/93739.aspx <p>Promemoria. <a href="http://geekswithblogs.net/naijacoder/archive/2007/08/01/114348.aspx" target="_blank">Questo link</a> presenta una possibile soluzione nel caso in cui, lavorando con SQL Server 2005, si presenti il seguente messaggio d'errore:</p> <p><em>The database principal owns a schema in the database, and cannot be dropped. (Microsoft SQL Server, Error: 15138). </em></p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/93739.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/18/93739.aspx Mon, 18 Aug 2008 14:29:39 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/18/93739.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/93739.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/93739.aspx Trigger SQL http://blogs.ugidotnet.org/PietroLibroBlog/archive/2007/11/25/89927.aspx <p>Creazione di un trigger su tabella SQL Server. Post a titolo di promemoria. :-)</p> <p>Codice per la creazione della tabella di esempio</p> <div class="wlWriterSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:6dcaedf6-7ffa-42d1-ba87-38c74fd2736a" style="margin: 0px; padding: 0px; display: inline; float: none;"> <pre style="overflow: auto; background-color: White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: rgb(0, 0, 255);">CREATE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">TABLE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">dbo</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">.</span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utenti</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">(<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_ID</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">int</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 255);">IDENTITY</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">1</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(128, 0, 0); font-weight: bold;">1</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(128, 128, 128);">NOT</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Cognome</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">varchar</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">20</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(128, 128, 128);">NOT</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Nome</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">varchar</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">20</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(128, 128, 128);">NOT</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Piano</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">int</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Telefono</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">varchar</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">15</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Email</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">varchar</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">50</span><span style="color: rgb(0, 0, 0);">) </span><span style="color: rgb(0, 0, 255);">NULL</span><span style="color: rgb(0, 0, 0);">,<br /> </span><span style="color: rgb(0, 0, 255);">CONSTRAINT</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">PK_Utenti</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">PRIMARY</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">KEY</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">CLUSTERED</span><span style="color: rgb(0, 0, 0);"> <br />(<br /> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_ID</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">ASC</span><span style="color: rgb(0, 0, 0);"> ) </span><span style="color: rgb(0, 0, 255);">ON</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">PRIMARY</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> ) </span><span style="color: rgb(0, 0, 255);">ON</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">PRIMARY</span><span style="color: rgb(255, 0, 0);">]</span></div></pre> <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> <p>Codice per la creazione del trigger</p> <div class="wlWriterSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:09bf9653-d370-4087-9ccf-a3f47735c73b" style="margin: 0px; padding: 0px; display: inline; float: none;"> <pre style="overflow: auto; background-color: White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: rgb(0, 128, 128);">--</span><span style="color: rgb(0, 128, 128);"> Crea un trigger per l'inserimento e l'aggiornamento.</span><span style="color: rgb(0, 128, 128);"> --</span><span style="color: rgb(0, 128, 128);"> Viene recuperato il valore del campo [Utente_Cognome] inserito o aggiornato</span><span style="color: rgb(0, 128, 128);"> </span><span style="color: rgb(0, 0, 255);">CREATE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">TRIGGER</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Aggiorna</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">ON</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utenti</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> AFTER </span><span style="color: rgb(0, 0, 255);">INSERT</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(0, 0, 255);">UPDATE</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">AS</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">BEGIN</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">DECLARE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 128, 0);">@Cognome</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">varchar</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">20</span><span style="color: rgb(0, 0, 0);"> ) <br /></span><span style="color: rgb(0, 0, 255);">SET</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 128, 0);">@Cognome</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(128, 128, 128);">=</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">SELECT</span><span style="color: rgb(0, 0, 0);"> inserted.</span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">Utente_Cognome</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">FROM</span><span style="color: rgb(0, 0, 0);"> inserted) <br /></span><span style="color: rgb(0, 0, 255);">EXECUTE</span><span style="color: rgb(0, 0, 0);"> TestTrigger </span><span style="color: rgb(0, 128, 0);">@Cognome</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">END</span></div></pre> <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> <p>Codice per la Stored Procedure di Test</p> <p> </p> <div class="wlWriterSmartContent" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:1fc7adc8-d085-4e99-9643-d27fb1cd65f1" style="margin: 0px; padding: 0px; display: inline; float: none;"> <pre style="overflow: auto; background-color: White;"><div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><span style="color: rgb(0, 128, 128);">--</span><span style="color: rgb(0, 128, 128);"> Modifica il valore del campo [Utente_Cognome] in 'Nuovo Cognome'</span><span style="color: rgb(0, 128, 128);"> </span><span style="color: rgb(0, 0, 255);">CREATE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">PROCEDURE</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">TestTrigger</span><span style="color: rgb(255, 0, 0);">]</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 128, 0);">@Utente_Cognome</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">varchar</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(128, 0, 0); font-weight: bold;">50</span><span style="color: rgb(0, 0, 0);">) <br /></span><span style="color: rgb(0, 0, 255);">AS</span><span style="color: rgb(0, 0, 0);"> <br /></span><span style="color: rgb(0, 0, 255);">UPDATE</span><span style="color: rgb(0, 0, 0);"> Utenti </span><span style="color: rgb(0, 0, 255);">SET</span><span style="color: rgb(0, 0, 0);"> Utente_Nome </span><span style="color: rgb(128, 128, 128);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 0, 0);">'</span><span style="color: rgb(255, 0, 0);">Nuovo Cognome</span><span style="color: rgb(255, 0, 0);">'</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">WHERE</span><span style="color: rgb(0, 0, 0);"> Utente_Cognome </span><span style="color: rgb(128, 128, 128);">=</span><span style="color: rgb(0, 128, 0);">@Utente_Cognome</span><span style="color: rgb(0, 0, 0);"> </span></div></pre> <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> <p>La combinazione trigger/stored procedure  in questo caso è veramente insignificante, ma in realtà si potrebbero eseguire calcoli,recupero di dati, operazioni di verifica etc...<br />  </p> <div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:7d18f0af-801e-43b8-8b17-1307e9e9f9f1" style="margin: 0px; padding: 0px; display: inline; float: none;">Technorati Tag: <a href="http://technorati.com/tags/Trigger" rel="tag">Trigger</a></div><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/89927.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2007/11/25/89927.aspx Sun, 25 Nov 2007 21:34:19 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2007/11/25/89927.aspx#feedback 2 http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/89927.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/89927.aspx