SQL Server http://blogs.ugidotnet.org/PietroLibroBlog/category/SQL Server.aspx SQL Server it-IT Pietro Libro pietro.libro@libero.it Subtext Version 2.6.0.0 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 Confronto di schemi di database in Visual Studio http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/12/26/confronto-di-schemi-di-database-in-visual-studio.aspx <p> </p> <p>Da quando ho avuto la fortuna di poter installare Visual Studio Team System, per gestire le modifiche sui database di SQL Server utilizzo il tool integrato “Nuovo confronto schema” raggiungibile dal menu dati di Visual Studio. Il tool si presenta come nello screenshot seguente (in questo caso la “versione italiana”):</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_thumb.png" width="244" height="112" /></a> </p> <p>A questo punto possiamo scegliere i database su cui eseguire il confronto degli schemi e premere “OK”. Secondo della complessità dei database su cui viene eseguito il confronto, la procedura potrebbe impiegare qualche minuto prima di terminare. Completato il processo, verrà visualizzata una schermata in cui viene riepilogato lo stato dei due database: </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_thumb_1.png" width="244" height="129" /></a> </p> <p>Con qualche click del mouse è possibile indicare quali oggetti (tabelle, colonne, utenti…) bisognerà creare, aggiornare o eliminare nei due database. A questo punto è possibile eseguire direttamente l’aggiornamento sul database di destinazione o salvare lo script di aggiornamento su file:</p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ConfrontodischemididatabaseinVisualStudi_ED64/image_thumb_2.png" width="244" height="51" /></a> </p> <p>Avendo installato sulla mia macchina due istanze di SQL Server 2005, una di “Sviluppo” ed una di “Produzione”, dopo ogni release, in caso di modifiche al database, tenere aggiornate le due istanze è veramente semplice. Spesso mi sono trovato ad utilizzare il prodotto “SQL Delta”, ma la comodità di avere tutti gli strumenti integrati in un unico ambiente di sviluppo è un’altra cosa.</p><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/97766.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/12/26/confronto-di-schemi-di-database-in-visual-studio.aspx Sat, 26 Dec 2009 17:47:00 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/12/26/confronto-di-schemi-di-database-in-visual-studio.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/97766.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/97766.aspx Shrink, BackUp e ...SSIS http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/26/shrink-backup-e-.ssis.aspx <p> </p> <p>Uno dei task più frequenti nella manutenzione dei database (nel caso SQL Server) è quello di  effettuarne lo Shrink (<span style="font-weight: bold;">a riguardo, suggerisco di leggere il commento lasciato da Davide Mauri</span>) ed il BackUp. Finchè le operazioni riguardano un solo database possiamo completare il task utilizzando, ad esempio, SQL Server Management Studio. Nel caso in cui le suddette operazioni debbano essere effettuate su più database la "faccenda si complica" (più che altro diventa ripetitiva) e possiamo <br /> risolvere il problema creando un package SSIS (SQL Server Integration Services). Premetto che googlando si possono trovare diverse soluzioni, utilizzando anche lo stesso sistema. Per iniziare, apriamo Visual Studio (2008 Professional nel mio caso) e partiamo con il creare un nuovo progetto "Integration Services Project" (ovviamente per visualizzare questo template è necessario che tutti i componenti relativi siano correttamente installati e configurati) </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_1_4.jpg"><img height="167" border="0" width="244" style="border-width: 0px;" alt="Figura_1" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_1_thumb_1.jpg" /></a> </p> <p>Creiamo una nuova connessione al server sql interessato nel "Connection Managers" (tasto destro-&gt;"New OLE DB Connection...") </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_2_2.jpg"><img height="146" border="0" width="244" style="border-width: 0px;" alt="Figura_2" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_2_thumb.jpg" /></a> </p> <p>Se si vuole, dopo la creazione della connessione è sempre possibile rinominare la stessa. </p> <p>A questo punto, nel "Control Flow" non ci resta che trascinare due Task presenti nel Tab <br /> "Maintenance Plan Tasks" della Toolbox, in particolare "Shrink Database Task" e "Back Up Database Task". Per impostare le opzioni ed i database su cui eseguire le operazioni è sufficiente un doppio click sui task aggiunti. Ad esempio cliccando due volte su "Shrink Database Task", otteniamo: </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_3_2.jpg"><img height="210" border="0" width="244" style="border-width: 0px;" alt="Figura_3" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_3_thumb.jpg" /></a> </p> <p>Effettuiamo la stessa operazione sul task relativo al BackUp. A questo punto abbiamo quasi terminato, ma vogliamo che al completamento delle operazioni venga inviata all'amministratore una e-mail di notifica. Potremmo utilizzare "Send Mail Task", ma ha delle limitazioni (tra lequali non è possibile specificare le credenziali d'accesso al server SMTP, ma utilizza l'autenticazione di Windows), per questo, aggiungiamo uno "Script Task" nel nostro "Data Flow". Solito doppio click, e nella finestra visualizzata premiamo <br /> sul bottone con la dicitura "Edit Script...". </p> <p><a href="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_4_2.jpg"><img height="232" border="0" width="244" style="border-width: 0px;" alt="Figura_4" src="http://blogs.ugidotnet.org/images/blogs_ugidotnet_org/PietroLibroBlog/WindowsLiveWriter/ShrinkBackUpe.SSIS_7867/Figura_4_thumb.jpg" /></a> </p> <p>Verrà visualizzata una finestra di Visual Studio in cui possiamo scrivere il codice (C#) per l'invio dell'e-mail. Per chi è abituato ad usare Visual Basic, nella finestra delle proprietà del Task è possibile scegliere quale linguaggio utilizzare (nota: nella versione 2005 non era possibile scegliere tra i due linguaggi). Prima di iniziare a scrivere è necessario aggiungere un riferimento all'assembly System.Net.dll al progetto. Scriviamo del codice simile al seguente: </p> <!-- code formatted by http://manoli.net/csharpformat/ --><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> <div class="csharpcode"> <pre><span class="lnum"> 1: </span>System.Net.Mail.MailMessage message = <span class="kwrd">new</span> System.Net.Mail.MailMessage();</pre> <pre><span class="lnum"> 2: </span>message.From = <span class="kwrd">new</span> System.Net.Mail.MailAddress(<span class="str">"indirizzo@dominio.it"</span>);</pre> <pre><span class="lnum"> 3: </span>message.To.Add (<span class="kwrd">new</span> System.Net.Mail.MailAddress("<span class="str"><a href="mailto:amministratore@dominio.it">amministratore@dominio.it</a>"</span>));</pre> <pre><span class="lnum"> 4: </span>message.Body =<span class="kwrd">string</span>.Format (<span class="str">"BackUp dei database completato ({0:d})"</span>,DateTime.Now );</pre> <pre><span class="lnum"> 5: </span>message.Subject =<span class="str">"Backup"</span>; </pre> <pre><span class="lnum"> 6: </span> </pre> <pre><span class="lnum"> 7: </span><span class="kwrd">try</span></pre> <pre><span class="lnum"> 8: </span>{</pre> <pre><span class="lnum"> 9: </span> System.Net.Mail.SmtpClient smtp = <span class="kwrd">new</span> System.Net.Mail.SmtpClient(<span class="str">"smtp.dominio.it"</span>);</pre> <pre><span class="lnum"> 10: </span> smtp.Credentials = <span class="kwrd">new</span> NetworkCredential(<span class="str">"username"</span>, <span class="str">"password"</span>);</pre> <pre><span class="lnum"> 11: </span> smtp.Send(message); </pre> <pre><span class="lnum"> 12: </span> </pre> <pre><span class="lnum"> 13: </span> Dts.TaskResult = (<span class="kwrd">int</span>)ScriptResults.Success;</pre> <pre><span class="lnum"> 14: </span>}</pre> <pre><span class="lnum"> 15: </span><span class="kwrd">catch</span> (Exception ex)</pre> <pre><span class="lnum"> 16: </span>{</pre> <pre><span class="lnum"> 17: </span> Dts.Log(ex.Message, 123, <span class="kwrd">null</span>);</pre> <pre><span class="lnum"> 18: </span> Dts.TaskResult = (<span class="kwrd">int</span>)ScriptResults.Failure;</pre> <pre><span class="lnum"> 19: </span>} </pre> </div> <p>Tramite un costrutto Try...Catch catturiamo eventuali eccezioni ed indichiamo  il successo o il fallimento del task. </p> <p>Ogni volta che abbiamo la necessità di utilizzare questo Package, non è necessario aprire  un'istanza di Visual Studio, ma è possibile utilizzare l'utility "DTExec.exe"  da riga di comando (ad esempio, DTExec.exe /FILE "package.dtsx". (E' possibile trovare il package nella directory "bin" del progetto)</p> <div style="margin: 0px; padding: 0px; display: inline; float: none;" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f8afcab4-c11d-4a48-b0ab-c20fb8bf9a8c" class="wlWriterSmartContent">Technorati Tag: <a href="http://technorati.com/tags/SSIS" rel="tag">SSIS</a>,<a href="http://technorati.com/tags/Backup" rel="tag">Backup</a>,<a href="http://technorati.com/tags/Shrink" rel="tag">Shrink</a></div><img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/96923.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/26/shrink-backup-e-.ssis.aspx Wed, 26 Aug 2009 09:30:29 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/26/shrink-backup-e-.ssis.aspx#feedback 2 http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/96923.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/96923.aspx SQL Server 2005 Build List http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/22/sql-server-2005-build-list.aspx Al seguente indirizzo <a href="http://www.sqlservercentral.com/articles/Administration/2960/">http://www.sqlservercentral.com/articles/Administration/2960/</a> è presente un articolo aggiornato al 17/06/2009 da <a href="http://www.sqlservercentral.com/Authors/Articles/Steve_Jones/3/">Steve Jones</a> con l'elenco delle Build relative a SQL Server 2005. Per ogni build è presente il link alla relativa pagina di descrizione. Sono ben messe in evidenza (in testa all'articolo) le Major Builds (RTM, SP1, SP2, SP3).<img src="http://blogs.ugidotnet.org/PietroLibroBlog/aggbug/96907.aspx" width="1" height="1" /> Pietro Libro http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/22/sql-server-2005-build-list.aspx Sat, 22 Aug 2009 11:36:36 GMT http://blogs.ugidotnet.org/PietroLibroBlog/archive/2009/08/22/sql-server-2005-build-list.aspx#feedback http://blogs.ugidotnet.org/PietroLibroBlog/comments/commentRss/96907.aspx http://blogs.ugidotnet.org/PietroLibroBlog/services/trackbacks/96907.aspx