Skip Navigation Links.
Skip Navigation Links.

.net student

microsoft

Vyhledávání v obsahu portálu


Přihlášení uživatele

Přihlášení uživatele

Členové:
  • Poslední nový uživatel Poslední: liborsky
  • Nový dnes Dnes nových 1
  • Nový včera Včera nových: 0
  • Počet uživatelů: Celkem: 4708
Lidé online:
  • Návštěvníci: Návštěvníků: 35
  • Registrovaných Členů: 0
  • Celkem Celkem: 35
Mapa Stranek

Microsoft Sync Framework - Synchronizace databází (1)

Microsoft Sync Framework je kolekce tříd a logiky, která nám umožní velmi jednoduše zajišťovat synchronizaci různých obsahů. Ať již pouze dva body, či do hvězdy nebo každý s každým. V minulém díle jsme si ukázali jednoduchou synchronizaci dvou adresářů. V dnešním pokračování se podíváme na databáze. Synchronizace databází je velmi často diskutované témata a patří ke snad nejčastějším věcem, které se synchronizují.

Microsoft Sync Framework je kolekce tříd a logiky, která nám umožní velmi jednoduše zajišťovat synchronizaci různých obsahů. Ať již pouze dva body, či do hvězdy nebo každý s každým. V minulém díle jsme si ukázali jednoduchou synchronizaci dvou adresářů. V dnešním pokračování se podíváme na databáze. Synchronizace databází je velmi často diskutované témata a patří ke snad nejčastějším věcem, které se synchronizují.

Pravděpodobně každý dokáže vymyslet důvody, proč se databáze synchronizují. Může se jednat o prosté zajištění proti výpadku, možnost pracovat offline či různé možnosti v odlehčení HW. Topologií, které se provozují, najdeme také nespočet. Velmi častý je takovýto požadavek. Existuje jedna „centrální“ databáze a k ní se připojují klienti, z nichž někteří nemohou být stále připojeni. Klasickou ukázkou je pracovník na cestách. Všechna data (nebo alespoň některá) potřebuje mít u sebe, provádět v nich změny a po návratu zpět do kanceláře je synchronizovat zpět s centrální databází.

Není těžké domyslet, že většina synchronizací bude znamenat změny v struktuře tabulek a že ne vše půjde lehce, především kvůli velké rozmanitosti oblastí, které databáze obsluhují. Jedinou výjimkou je případ, kdy chceme na klienta stáhnout všechna data bez ohledu na předchozí synchronizace. Potom není žádná změna nutná.  Nutné změny shrnuje níže uvedená tabulka.

  PK Update stamp Insert stamp Delete stamp ID klienta (updaty) ID klienta (inserty) ID klienta (mazání)
Stažení snapshotu dat na klienta Ne Ne Ne Ne Ne Ne Ne
Stažení inkrementálních insertů a updatů na klienta Ano Ano Ano1 Ne Ne Ne Ne
Stažení inkrementálních insertů, updatů a mazání na klienta Ano Ano Ano1 Ano Ne Ne Ne
Nahrání insertů na server Ano Ne Ne Ne Ne Ne2 Ne
Nahrání insertů a updatů na server Ano Ne Ne Ne Ne2 Ne2 Ne
Nahrání insertů, updatů, a mazání na server Ano Ne Ne Ne Ne2 Ne2 Ne
Obousměrné inserty a updaty s detekcí konfliktů Ano Ano Ano1 Ne Ano3 Ano3 Ne
Obousměrné inserty, updaty a mazání s detekcí konfliktů Ano Ano Ano1 Ano Ano3 Ano3 Ano3

1 Vyžadováno pokud potřebujeme rozlišit inserty a updaty.
2 Vyžadováno pokud více klientů může změnit řádek a chceme tyto změny rozlišit.
3 Vyžadováno pokud nechceme změny propagovat zpět na klienta, který je provedl.
4 Primární klíč musí být unikátní. Smazané hodnoty se nesmí znovu používat (vhodné používat autoincrement, GUID atp.).

Vidíme, že změna není nějak drastická, nicméně ani triviální. Pokud máme aplikaci správně napsánu, neměl by to být příliš velký zásah (ne větší než vlastní řešení).

My se rovnou vrhneme na případ, kdy chceme synchronizovat obousměrně a synchronizovat vše (změny, mazání, přidávání včetně konfliktů). Jak jsem na začátku naznačil, synchronizace databáze je velmi obtížná oblast a proto Microsoft Sync Framework nemusí vždy být nápomocen hned a bude potřeba hledat nebo napsat vlastní řešení/providery.

Pro náš první příklad budeme mít tabulku nesoucí název produkty a mající sloupce #id, nazev, cena. Tabulka je uložena na „velkém“ SQL Server. Synchronizovat budeme na SQL Server Compact Edition. Scénář tedy odpovídá stavu, kdy na cestách má pracovník data např. na PDA. Tabulku produkty rozšíříme o sloupce update_orig_id [int], update_stamp [datetime], insert_orig_id [int], insert_stamp [datetime]. Pokud si vzpomínáte, v prvním dílu jsem mluvil o tzv. náhrobcích – tombstones, které slouzí k udržování informací o smazaných položkách. Přidáme proto tabulku produkty_tombstones se sloupci id, nazev, cena a delete_orig_id [int], delete_stamp [datetime]. Obecně by stačil pouze primární klíč id a delete_orig_id, delete_stamp, ale přidáme si i další sloupce, které nám pomohou později při řešení konfliktů.

 Zbývá ještě přidat spouště, které nám dodají trochu funkcí. Pro update je třeba nastavit update_stamp (např. na aktuální datum) a update_orig_id (např. na 0, kterým si označíme „centrální“ server).  Pro delete stačí pouze překopírovat sloupce id, nazev, cena a podobně nastavit delete_stamp a delete_orig_id. V podstatě tedy poznamenání dané položky do tabulky smazaných řádků. Zde je mimo jiné vidět, proč nemohou být hodnoty primárního klíče tabulky produkty znovu používány – musíme mít (zpětnou) identifikaci smazaných záznamů. Celý skript SQL najdete na konci článku.

Co se týká změn v databázi, máme hotovo. Zbývá nastartovat Microsoft Sync Framework a využít jeho potenciálu. Začneme s jednodušší aplikací. Nejprve si popíšeme, jaké objekty budeme používat.

  • Základem je SyncAgent, kterýžto zajišťuje celou synchronizaci zavoláním metody Synchronize().
  • Další objekt, který použijeme je tzv. ServerSyncProvider pro nás konkrétně DbServerSyncProvider. DbServerSyncProvider je dodáván společně s ADO.NET Synchronization Services a umožňuje připojit se k libovolné databázi, která podporuje ADO.NET interfacy. DbServerSyncProvider má dvě zajímavé property: SelectNewAnchorCommand a SelectClientIdCommand. Druhá jmenovaná je volitelná a mapuje GUID klienta, kterým jsou implicitně označeni v MSF klienti, na běžný integer, s nímž se přece jen lépe pracuje. První property nám určuje horní hranici pro synchronizaci. Tato hranice se poté, pro další synchronizaci, stává hranicí dolní, takže nejsou zbytečně přenášena již synchronizovaná data.
  • Na druhé straně barikády se nachází ClientSyncProvider pro nás konkrétně SqlCeClientSyncProvider. Tento objekt představuje úložiště lokální straně. My použijeme SQL Server CE, avšak je možné napsat si vlastní provider pro oblíbené úložiště (nemusí se nutně jednat ani o DB).
  • Předposledním (prozatím) zajímavým objektem je SyncAdapter. Jedná se o objekt, který obsahuje osm příkazů, které „řídí“ synchronizovaná data – vybírají nová, ukazují smazaná, určují updaty atp. Nemusíte se bát, že všechny příkazy budete muset vymýšlet. Za prvé, ne všechny jsou vždy třeba (podle typu synchronizace). A za druhé existuje magická věc SqlSyncAdapterBuilder. Tento objekt nám pomůže sestavit SyncAdapter na základě několika předaných informací. Je však třeba upozornit, že právě kvůli zjišťování metadat má jistou režii a proto bych doporučil pro finální nasazení např. vygenerované příkazy zanést přímo do kódu. Mimo jiné má tento objekt také property SyncDirection, která ovlivňuje, jaké příkazy budou vygenerovány (jakou synchronizaci budeme provádět).
  • A konečně poslední objekt, který budeme potřebovat, je SyncTable. SyncTable není nic jiného, než určení jakou tabulku chceme synchronizovat. Umožňuje nám nastavit, jak chceme synchronizovat (např. obousměrně, download, upload, snapshot). Nemůžeme však nastavit „vyšší“ akci, než jaké máme příkazy v SyncAdapteru, případně jaké necháváme generovat pomocí SqlSyncAdapterBuilderu. SyncTable také umožňuje definovat, jak se má kód chovat, v případě, že tabulka (ne)existuje.

Máme vše připraveno; hurá na věc:

   1:  SyncAgent agent = new SyncAgent();
   2:   
   3:  #region Pripojeni ke zdroji (SQL Server)
   4:  DbServerSyncProvider serverSyncProvider = new DbServerSyncProvider();
   5:  agent.RemoteProvider = serverSyncProvider;
   6:  SqlConnection conn = new SqlConnection(_dbSQL);
   7:  serverSyncProvider.Connection = conn;
   8:   
   9:  // nastaveni horni hranice
  10:  SqlCommand anchorCmd = new SqlCommand();
  11:  anchorCmd.CommandType = CommandType.Text;
  12:  anchorCmd.CommandText = "select @" + SyncSession.SyncNewReceivedAnchor + " = GETUTCDATE()";
  13:  anchorCmd.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.DateTime).Direction = ParameterDirection.Output;
  14:  serverSyncProvider.SelectNewAnchorCommand = anchorCmd;
  15:  anchorCmd.Connection = conn;
  16:   
  17:  // nastaveni ID klienta
  18:  SqlCommand clientIdCmd = new SqlCommand();
  19:  clientIdCmd.CommandType = CommandType.Text;
  20:  clientIdCmd.CommandText = "select @" + SyncSession.SyncOriginatorId + " = 1";
  21:  clientIdCmd.Parameters.Add("@" + SyncSession.SyncOriginatorId, SqlDbType.Int).Direction = ParameterDirection.Output;
  22:  serverSyncProvider.SelectClientIdCommand = clientIdCmd;
  23:  #endregion
  24:   
  25:  #region Lokalni uloziste (SQL Server CE)
  26:  SqlCeClientSyncProvider clientSyncProvider = new SqlCeClientSyncProvider(_dbSQLCE);
  27:  agent.LocalProvider = clientSyncProvider;
  28:  #endregion
  29:   
  30:  #region Tabulky k synchronizaci
  31:  SyncTable tableProdukty = new SyncTable("produkty");
  32:  tableProdukty.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
  33:  tableProdukty.SyncDirection = SyncDirection.Bidirectional;
  34:  agent.Configuration.SyncTables.Add(tableProdukty);
  35:  #endregion
  36:   
  37:  # region SyncAdapterBuilder
  38:  SqlSyncAdapterBuilder syncAdapterBuilder = new SqlSyncAdapterBuilder();
  39:  syncAdapterBuilder.Connection = conn;
  40:  syncAdapterBuilder.SyncDirection = SyncDirection.Bidirectional;
  41:   
  42:  // zakladni tabulky a sloupce
  43:  syncAdapterBuilder.TableName = "produkty";
  44:  syncAdapterBuilder.DataColumns.Add("id");
  45:  syncAdapterBuilder.DataColumns.Add("nazev");
  46:  syncAdapterBuilder.DataColumns.Add("cena");
  47:  syncAdapterBuilder.TombstoneTableName = "produkty_tombstones";
  48:  syncAdapterBuilder.TombstoneDataColumns.Add("id");
  49:  syncAdapterBuilder.TombstoneDataColumns.Add("nazev");
  50:  syncAdapterBuilder.TombstoneDataColumns.Add("cena");
  51:   
  52:  // sloupce pro synchronizaci
  53:  syncAdapterBuilder.CreationOriginatorIdColumn = "insert_orig_id";
  54:  syncAdapterBuilder.CreationTrackingColumn = "insert_stamp";
  55:  syncAdapterBuilder.DeletionOriginatorIdColumn = "delete_orig_id";
  56:  syncAdapterBuilder.DeletionTrackingColumn = "delete_stamp";
  57:  syncAdapterBuilder.UpdateOriginatorIdColumn = "update_orig_id";
  58:  syncAdapterBuilder.UpdateTrackingColumn = "update_stamp";
  59:   
  60:  // pridat
  61:  SyncAdapter produktySyncAdapter = syncAdapterBuilder.ToSyncAdapter();
  62:  serverSyncProvider.SyncAdapters.Add(produktySyncAdapter);
  63:  #endregion
  64:   
  65:  agent.Synchronize();

Pokud si tento kód spustíte, uvidíte, že všechny změny se přenesly na jednu i druhou stranu (pokud nevznikl konflikt). Pokud jste pozorně přečetly předcházející popisy objektů, mělo by výt vše jasno a konkrétní detaily dokreslí kód. Případně můžete využít diskuzi pod článkem.

Ačkoli přípravy byly poněkud obsáhlejší, při dalším přidávání by již měla jít práce více od ruky.

Pozorný čtenář si jistě všiml, že (zatím) např. neřešíme konflikty, více klientů nebo synchronizaci tabulek provázaných referenční integritou. Tyto a další případy si probereme v dalším dílu, kde náš kód vylepšíme.

 


   1:  create table produkty (id int not null primary key, nazev nvarchar(100) not null, cena int not null);
   2:   
   3:  alter table produkty add update_orig_id int default 0;
   4:  alter table produkty add update_stamp datetime default GetUTCDate();
   5:  alter table produkty add insert_orig_id int default 0;
   6:  alter table produkty add insert_stamp datetime default GetUTCDate();
   7:   
   8:  create table produkty_tombstones (id int not null primary key, nazev nvarchar(100) not null, cena int not null, delete_orig_id int, delete_stamp datetime);
   9:   
  10:  create trigger produkty_U on produkty for update
  11:  as
  12:  begin
  13:      if not update(update_orig_id)
  14:      update produkty set update_orig_id = 0 where id in (select id from inserted);
  15:      if not update(update_stamp)
  16:        update produkty set update_stamp = GetUTCDate() where id in (select id from inserted);
  17:  end
  18:  ;
  19:   
  20:  create trigger produkty_D on produkty for delete
  21:  as
  22:  begin
  23:   insert into produkty_tombstones (id, nazev, cena, delete_orig_id, delete_stamp)
  24:      select id, nazev, cena, 0, GetUTCDate() from deleted;
  25:  end
  26:  ;
  27:   
  28:  insert into produkty (id, nazev, cena) values (1, 'televize', 100);
  29:  insert into produkty (id, nazev, cena) values (2, 'krabice', 200);

Jiri Cincura :: 21. prosince 2007 :: 933 shlédnutí :: 0 komentářů
kategorie: Databáze a práce s XML, Microsoft - produkty

Comments

Nyní zde nejsou žádné kometáře. Buďte první!
Musíte být přihlášen pro posílání komentářů. Přihlásit se můžete zde
Přehled posledních diskuzí

Přehled posledních diskuzí

  1. ASP.NET programátor zlín [12.16.2008 10:17 odp.]
    Dobrý den,v současné době hledáme programátora na pozici: ASP.NET / C# programátor - Zlín Požadavky: schopnost sam...
  2. RE: MS Fest video [12.08.2008 6:20 odp.]
    AhojVideo záznam bude, po večerech pilně pracujeme na jeho stříhání. Po dokončení se zde určitě vyskytne link na stažení...
  3. MS Fest video [12.04.2008 4:18 odp.]
    Existuje videozáznam z MS Festu konaneho (koncem listopadu 2008) o vikendu na Male Strane na fakulty MFF? Je nekde mozne...
  4. Práce pro programátora [11.25.2008 10:12 dop.]
    Hledám nadšeného programátora na vytvoření webstránek online tv/stream video. V případě zájmu pište na email: projekt@tr...
Novinky z klubů

Novinky z klubů

  1. Programátorské večery: F# 15. prosince 2008
    Ve čtvrtek 18.12.2008 se v rámci programátorských večerů uskuteční přednáška n...
  2. Programátorské večery: VSTO 10. prosince 2008
    Nad kancelářským balíkem MS Office se dají tvořit rozsáhlé aplikace. Od jednod...
  3. Programátorské večery: .NET 4.0 3. prosince 2008
    Zajímá Vás, na co se můžete těšit v nové verzi .NETu? Pak se přijďte podívat n...
  4. Záznamy z programátorských večerů 3. prosince 2008
    Záznamy z prog. večerů (prezentace+dema, občas i nějaké to video) můžete dočasně najít na http://ci...
Co se píše jinde

Co se píše jinde

Windows 7 Beta k dispozici pro MSDN předplatitele

Windows 7 Beta k dispozici pro MSDN předplatitele

Silverlight 2 úvod

Zajímá vás psaní webových aplikací na straně klienta ve vašem oblíbeném nástroji v .NETu? Slyšeli jste o Silverlightu...

PF 2009

Co tak popřát čtenářům programátorského blogu do nového roku? Snad abyste dostávali rozumná zadání a nemuseli tenkou ...

TypeConvertery

V tomto díle si vysvětlíme, co jsou TypeConvertery a k čemu se používají. To si také ukážeme na příkladu.

Atributy jmenného prostoru System.ComponentModel

V tomto článku si ukážeme jak efektivně využívat atributy z jmenného prostoru System.ComponentModel při vytváření vla...