János's profileJános JankaPhotosBlogListsMore ![]() | Help |
|
June 28 SQL Server adatbázisok szinkronizálásaErre a témára kénytelen vagyok egy picivel több szót fecsérelni, mert ez az, ami az életben is talán a legtöbbször előfordul. Több megoldás is létezik egyébként a problémára. Egyik ezek közül a Microsoft SQL Server Integration Services használata. Ez utóbbival egyetlen probléma, hogy SQL Server Express felhasználók nem tudják használni. Másik megoldás a Sync Framework használata, én most ezzel fogok folglalkozni, mely nem csak kiadás-független, hanem SQL Server független is. Még mielőtt tovább mennénk, vegyünk egy pillantást a provider modellre, ami lényegesen változott az első CTP óta: Mint látható a képen, az újítások javarésze az adatbázisok szinkronizálását célozta meg. Született egy új, a relációs adatbázisok szinkronizálására alkalmas provider RelationalSyncProvider néven. Ennek leszármazottja egy DbSyncProvider, mely egy általános célú adatbázis szinkronizáló provider, illetve két specializált provider, melyek SQL Server / SQL CE adatbázisokhoz készültek (SqlSyncProvider, SqlCeSyncProvider). Mi most az utóbbi kettővel fogunk foglalkozni, de ez nem jelenti azt, hogy maga a feladat csupán ezekkel oldható meg. Ha saját változás-követési infrastruktúrát szeretnénk kialakítani, illetve magunk szeretnénk kezelni az alacsonyszintű metaadattárolási műveleteket, akkor még mindig lehetőség adódik használni ezek őseit. Néhány előnyét azért mégis felsorolnám a készhez kapott SqlSync providereknek:
Alapvető Sql Sync fogalmak Szinkronizálása a csomópontoknak egy topológiában két fázisra bontható: a szinkronizációs csomópontok beállítása, illetve futtatása a szinkronizációnak egypár csomópont között. Az SqlSync providerek esetében a csomópontok beállítása két feladatból áll: 1. Definiálni, hogy mit szeretnénk szinkronizálni private DbSyncTableDescription SetPrimaryKeys(DbSyncTableDescription tableSchema, params string[] keys) { Contract.Requires(tableSchema != null); Contract.Requires(keys != null); Contract.Requires(Contract.ForAll(keys, key => key != null)); for (var i = tableSchema.Columns.Count - 1; i >= 0; i--) { var column = tableSchema.Columns[i]; if (column.IsPrimaryKey && column.AutoIncrementStepSpecified) { tableSchema.Columns.Remove(column); } else { column.IsPrimaryKey = keys.Contains(column.UnquotedName); } } return tableSchema; } 2. Az adatbázisok ellátása változás-követési információkkal Szinkronizációs területek (scopes) Fontos megérteni, hogy egy scope kombinációja tábláknak és szűréseknek. Definiálhatunk olyan scope-ot is, ami csak a debreceni cégeket szinkronizálja, de mellé még egy olyat is, ami csak a pesti cégeket. Megjegyzendő, hogy a SyncFx nem kezeli azokat a törlési eseteket, amikor például egy debreceni cégből pesti lesz. Ilyenkor az egyik scope-ból átkerül a másikba az adat a szűrési kritériumok miatt, de a régi scope-ból nekünk kell azt törölni. Az alkalmazás feladata kezelni ezt a szituációt. Amennyiben nem szűrögetünk, hanem ökör módjára mindent szinkronizálunk (mint én is :)), ilyen gond nincs. A scope-ok lehetnek eltérőek, vagy átfedhetik egymást. Két scope átfedi egymást, ha egymás között osztanak meg közös adatokat. Van egy pár lehetőség scope-ok definiálására, de a következő elvet mindenesetben követni kell: bármilyen adat ami szinkronizált két adatbázis között a szinkronizációs topológiában csak egy scope-hoz tartozhat. Például az A és B adatbázis szinkronizálhat a Scope 1-el, az A és C adatbázis pedig a Scope 2-el egymás között. Az A és B nem tud szinkronizálni a Scope 2-el, mert az adott tábla sorok (pl. termékek) mindkét scope-hoz hozzátartoznak. Hogyan is működik ez a gyakorlatban? Először is, leírjuk a scope-ot és a táblákat. Én ezen általánosítottam egy picit, mint ahogy az SDK-ban van: /// <summary> Fontos látni, hogy a konfigurálásnak ez csak az egyik módja. Jelen esetben a meghatározott scope-ot és sémát alkalmazzuk először a szerverre, azt követően pedig a kliensre, de lehetőség van már magából a szerverből kinyerni a szinkronizációs sémát (ha már ott létezik) és azt alkalmazni a kliens adatbázisra. Ez utóbbi módszer különösen hasznos, ha biztosítani szeretnénk, hogy konzisztens maradjon a szerver szinkronizációs séma a kliensével. Használatára egy példa (a tábla sorrend számít): ApplyConfig(ScopeFromTableSchemas
(
GetTableSchema("Country"),
GetTableSchema("StateProvince"),
GetTableSchema("Settlement")
Ha ezzel idáig megvagyunk és megnézzük az adatbázist, akkor láthatjuk, hogy létrejöttek a change-tracking meta-adat táblák/triggerek/tárolt eljárások a szerveren és a kliensen egyaránt. Amennyiben valami gubanc van a létrehozás közben, megtehetjük azt is, hogy az infrastruktúrát létrehozó SQL scriptet még az alkalmazási folyamat előtt kimentjük például egy fájlba és mi magunk kézzel kezdjük el javítgatgni, majd futtatni azt az SQL Management Studio segítségével: Ezt még a config.Apply() előtt meg kell tenni, ha esetleg valami probléma adódna a script futtatásakor. Utolsó lépésben pedig semmi más dolgunk nincs, mint egy SyncOrchestrator-t felhasználva szinkronizálni a két csomópont között: public void Synchronize() { Init(); var clientProvider = new SqlSyncProvider(ScopeName, ClientConnection); var serverProvider = new SqlSyncProvider(ScopeName, ServerConnection); Orchestrator.Direction = SyncDirectionOrder.UploadAndDownload; Orchestrator.LocalProvider = clientProvider; Orchestrator.RemoteProvider = serverProvider; clientProvider.SyncProgress += ...; serverProvider.SyncProgress += ...; clientProvider.ApplyChangeFailed += ...; DisplayStat(Orchestrator.Synchronize()); } A kliens és a szerver szinkronizáció is szépen végigkövethető a SyncProgress események segítségével, de ezen felül kapunk egy végső összegzést is, amivel a Synchronize() szinkron metódus-hívás tér vissza. Az ApplyChangeFailed eseményre feliratkozva kezelhetjük a szinkronizálás közben bekövetkező konfliktusokat is a következő Action értékekből választva: private void clientProvider_ApplyChangeFailed(object sender, Mindebből egész jól látszik, hogy mennyire egyszerű szinkronizálni adatbázisok között. Ha van érdeklődés a téma iránt, akkor folytatom ezt a cikksorozatot a későbbiekben is, mert ez még hiányos egy picit, de ízelítőnek egyenlőre ennyi is elég. June 16 Balatonlelle (július 3.-4.-5.)Szeretném tudatni mindenkivel (hátha arra kószál valaki a blogomról), hogy a lellei juliális ezeken a napokon lesz és én is ott leszek. A főzőversenyen halászlét fogunk csinálni, mint minden évben, úgyhogy ha gondoljátok, ugorjatok le egy hétvégére és egy díjnyertes ebédre. Mellesleg ilyenkor mindig jó a felhozatal is. :) June 11 WPF 4.0 Data Binding PipelineA WPF 4.0 adatkötési pipeline-ba is történt némi változás, ami képes a tulajdonságok újraolvasására a kötési forrásból miután a control beállítja a tulajdonság értékét. Például:
Tehát mondhatjuk, hogy van egy új feedback mehanizmus, amit a WPF adatkötési motorja képes kezelni mostantól kezdve. XAML 2009 (.NET FX 4 Beta 1)Engedelmetekkel larántám a leplet néhány XAML 2009 újdonságról, mert kemény fejlesztéseken megy keresztül a WPF 4.0 is. Arról rengeteg hír van már most, hogy a VS Cider WPF designer designtime támogatása mennyit javult a VS2010-re - ténylegesen stabilabb és “egészségesebb” designer mint az előző verzió volt - de arról kevesen ejtenek szót, hogy a XAML nyelv merre fejlődik tovább. Megjegyzendő, hogy az alábbi XAML 2009 featurök még csak nyomokban működnek a VS 2010 Beta 1-ben. Például át kell állítani az adott XAML markup fájl Build Action tulajdonságát Page-ről Resource-ra, mely elkerüli, hogy a XAML markup leforduljon BAML/IL-re. Ez szükséges a Beta 1-ben, mivel az új nyelvi szintaxis még nem mindenhol támogatott. Illetve szükséges felvenni a System.Xaml assembly-t is a referenciák közé. Én ezeket az új featuröket úgy teszteltem, hogy készítettem egy ResourceDictionary-t, abba raktam DataTemplate-eket, majd az ablakba egy ContentPresenter segítségével megjelenítettem a tartalmat. Még mielőtt nyelvi dolgokról írnék, némi összefoglalóval szolgálnék már így az elején. Először is a XAML kezeléssel kapcsolatos létező összes kód folyamatosan kerül ki a System.Xaml.dll assembly-be a WindowsBase.dll-ből. Tehát maga a XAML nyelv teljesen WPF független lesz. Létezik egy új következetes XAML stack, ami megosztott mind WPF/WCF/WF között egyaránt. További jó hír, hogy egyetlen parser lesz x db helyett (sablonokhoz, stílusokhoz, stb.), ami garantálja a konzisztens viselkedést mindenhol. Ezenfelül a WPF team készít egy API-t BAML olvasáshoz, mely többek közt lehetőséget ad analizálni a XAML/BAML-t ugyanazzal az API-val… De ami igazán érdekel bennünket, hogy milyen nyelvi újdonságok várnak ránk: 1. Name References <DataTemplate x:Key="CT"> <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> <TextBox Name="myTextBox" Width="200"/> <Label Target="{x:Reference myTextBox}" Content="Text"/> </StackPanel> Így már nem működik: <DataTemplate x:Key="CT"> <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> <Label Target="{x:Reference myTextBox}" Content="Text"/> <TextBox Name="myTextBox" Width="200"/> </StackPanel> </DataTemplate> Valójában mindössze egy NameReferenceConverter nevű típuskonverter van a háttérben. Azt, hogy valójában mi egy típuskonverter, már korábban leírtam: itt. Illetve mégegy apróság, ellenben a Binding-al, ez nem támogatja az adatkontextus kötéseket {x:Reference} != {Binding}. 2. Generikus típusok támogatása XAML-ben <DataTemplate x:Key="Template"> <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> <StackPanel.DataContext> <om:ObservableCollection x:TypeArguments="x:String"> <x:String>A</x:String> <x:String>B</x:String> <x:String>C</x:String> </om:ObservableCollection> </StackPanel.DataContext> <ListBox ItemsSource="{Binding}"/> </StackPanel> </DataTemplate> Generikus típus-paraméterek az új x:TypeArguments segítségével adhatók meg. Ez a példa egyben azt is demonstrálja, hogy mostantól az alap típusok az {xmlns:x:..} XAML névtéren belül úgyszint elérhetők, mint pl. a string típus is: <x:String/>. Nincs szükség 76 névteret felvenni. Több típus-argumentumot egymással vesszővel elválasztva adunk meg: <gcc:Dictionary x:TypeArguments="x:String, x:Double"/> Mi van akkor, ha egy generikus típus-paraméter is egy generikus típus? Semmi, ugyanis lehetőség van generikus típusok beágyazására is a következő szintaxissal: <gcc:Dictionary x:TypeArguments="x:String, gcc:List(x:Double)"/> 3. Egyszerűsített elérése az alap típusoknak Néhány példa:
4. Paraméter nélküli (nem default) konstruktorok és factory metódusok támogatása x:Arguments 5. Események XAML által XAML: Events in compiled and uncompiled scenarios Egyenlőre még nem nagyon merülök bele a további részletekbe, mert még a VS 2010 Beta 1 is nagyon gyengén muzsikál ezekkel a dolgokkal. Az viszont a napnál is világosabb, hogy a legnagyobb cél a közös XAML stack megalkotása, így pl. a WCF is ezt fogja használni. Ezzel megy el a legtöbb idő a csapaton belül (a nyilatkozatok alapján), mivel jóval bonyolultabb ezt kivitelezni, mint elsőre gondolták. June 10 SQL szerverből SQL szerverbeAz SqlSyncProvider használatával kapcsolatban készítettem egy nagyon alap kis demonstrációs bemutatót. Nem csalás, nem ámítás, ténylegesen ennyi kód megvalósítani az SQL Server adatbázisok közötti szinkronizációt: using System; using System.Data.SqlClient; using Microsoft.Synchronization; using Microsoft.Synchronization.Data; using Microsoft.Synchronization.Data.SqlServer; namespace VVMF.Samples.Sync { class Program { static void Main(string[] args) { // Helyi és távoli kapcsolatok létrehozása (SqlConnection). var localConnection = new SqlConnection( @"Data Source=local;Initial Catalog=DB1;Integrated Security=True"); var remoteConnection = new SqlConnection( @"Data Source=local;Initial Catalog=DB2;Integrated Security=True"); // Létrehoz egy szinkronizációs scope-ot, // amin belül minden látszik a SyncFx számára. var scopeDesc = new DbSyncScopeDescription("Filtered_Organization"); // Hozzáadjuk a scope-hoz az Organization táblát. // A GetDescriptionForTable() metódus kinyeri a tábla sémát. scopeDesc.Tables.Add( SqlSyncDescriptionBuilder.GetDescriptionForTable( "Organization", remoteConnection)); // Konfiguráljuk a szinkronizációs scope-ot. // Nem kell létrehozni a táblákat, mert már léteznek. var remoteConfig = new SqlSyncScopeProvisioning(scopeDesc); remoteConfig.SetCreateTableDefault(DbSyncCreationOption.Skip); // Jelen esetben csak az W betűvel keződőek lesznek szinkronizálva. remoteConfig["Organization"].AddFilterColumn("Name"); remoteConfig["Organization"].FilterClause = "[side].[Name] LIKE 'W%'"; // Konfiguráljuk a scope és változás-követési infrastruktúrát, // feltéve, hogy még nem létezik. Ez fogja létrehozni a táblanév_tracking // táblákat, illetve triggereket, SP-ket, stb. if (!remoteConfig.ScopeExists("Filtered_Organization", remoteConnection)) remoteConfig.Apply(remoteConnection); // A távoli adatbázison már elkészítettük a change tracking infrastruktúrát. // Itt csak kinyerjük a jelenlegi állapotot a szerverről és alkalmazzuk // a kliens adatbázisra is. var localSqlDesc = SqlSyncDescriptionBuilder .GetDescriptionForScope("Filtered_Organization", remoteConnection); var localConfig = new SqlSyncScopeProvisioning(localSqlDesc); if (!localConfig.ScopeExists("Filtered_Organization", localConnection)) localConfig.Apply(localConnection); // Helyi és távoli szinkronizációs providerek létrehozása (SqlSyncProvider). var orchestrator = new SyncOrchestrator(); orchestrator.LocalProvider = new SqlSyncProvider("Filtered_Organization", localConnection); orchestrator.RemoteProvider = new SqlSyncProvider("Filtered_Organization", remoteConnection); // Egyéni adat-transzformációs konverterek hozzáadása. // Jelen esetben szükségtelen, mert mindkét oldalon ugyanolyan // szerkezetben van ábrázolva a cég. // orchestrator.LocalDataConverter = ... // orchestrator.RemoteDataConverter = ... // Néhány eseménykezelő, hogy tudjuk hol tart a folyamat. orchestrator.StateChanged += (sender, e) => Console.WriteLine( A SyncFx minden mást megcsinál helyettünk. Íme az eredmény: Kb. 3 sorral több elintézni azt, hogy ez egy harmadik, teszem fel SQL Server Compact adatbázisba is leszinkronizálódjon. Természetesen amit fentebb mutattam csak egy alap példa, jóval testreszabhatóbb ennél a dolog. Például még a szinkronizáció során fenntartott cache memória mérete is szabályozható. Microsoft Sync Framework 2.0 CTP2A Sync csapat kiadta a következő CTP-t. Végre van szinkronizáció provider direkt SQL Server adatbázisokhoz is! Az újdonságokról röviden: SqlSyncProvider / SqlCeSyncProvider Custom filters Az egyéni szűrők lehetőséget adnak egy szűrt replikának hogy tároljon tételadatokat csak azon tételekhez, amelyek a szűrésben szerepelnek, mint pl. egy média tároló replika, mely csak tárolja azokat a dalokat, melyek az értékelés során legalább 3 csillagot kaptak. Amint megváltozik egy dal értékelése, egy tétel bekerülhet/kikerülhet a szűrésből. Egy szűrés-követő replika egy másik replika a közösségben, amely tudja azonosítani, hogy mely tételek szerepelnek a szűrésben és melyek voltak betéve/kivéve mostanában. Egy szűrés-követő replika tárolhat tétel adatokat azon tételekhez is, melyek nem szerepelnek a szűrésben. A Sync Framework támogatja a hatékony szinkronizációt a szűrt replikák és a szűrés-követő replikák között és gondoskodik a meta-adatok kezeléséről. Improved conflict handling A CTP2 hozzáad jónéhány új funkciót a SyncFx-hez, ami egyszerűsíti a szinkronizáció során előfordulható konfliktusok kezelését. További részletek erről itt. Data conversion between providers Na ez már egy érdekesebb dolog, főként, hogy nekem is pont ez kell. Néhány esetben a szinkronizációs providerek szinkronizálnak ugyanolyan típusú adatokat (jelen esetben nálam pl. céges adatokat), de az adat formátuma ami a provdereknek szükséges különböző. Jelen esetben nálam ez egy bazi nagy denormalizált excel tábla az egyik végen, a másik végen pedig egy EF model alapú adatbázis. A Sync Framework úgy tűnik, hogy mostantól lehetőséget ad arra, hogy implementáljunk interfaceket, melyek konvertálják az adatokat a providereknek megfelelő formátumba. Ez az adatkonverziós API lehet használt bármilyen típusú egyéni szinkronizációs providerrel, továbbá a Sync Framework tartalmaz konverziós API-t kimondottan a fájl szinkronizációs providerhez is. Change application service A Sync Framework (a korábbi CTP is) tartalmaz egy change applier implementációt, amit a legtöbb alkalmazás használ alkalmazni a változásokat egy replikára. A CTP2 viszont bevezeti a change application service-t, mely ugyanazokat a műveleteket teljesíti, mint a change applier, de sokkal testreszabhatóbb módon. Ha egy destination providernek szükséges nagyobb rugalmasság, mint az alap változás alkalmazó, lehetősége van használni a change application service-t, hogy teljesítse csak egy halmazát a szükséges műveleteknek. A telepítéssel kapcsolatban annyit még, hogy le kell szedni az előző CTP-ket előtte. A telepítő pedig innen letölthető: Microsoft Sync Framework 2.0 CTP2. Hajrá! June 01 Microsoft BingElindult a Microsoft új keresője, a Bing. Egyenlőre még az összes feature használatához szükséges US-re állítani az ország/régió beállításokat, illetve angolra a nyelvet, de ígéretesnek néz ki nagyon. Néhány hasznos dolog, amit kigyűjtöttem erről (a képeken pirossal részletezve vannak a figyelemre méltó újdonságok): itt. |
|
|