Fluent NHibernate Step by Step


In questo Post / articolo, cercherò di mostrare passo per passo come creare una prima bozza di DataAccess utilizzando NHibernate con FluentNH per la configurazione e per elminare la produzione di quei “fastidiosi” file xml di configurazione dei mapping.

A seguire altri post sullo stesso argomento che mostreranno via via mapping più complessi.

Partiamo con la modellazione delle classi con cui vogliamo lavorare, partendo con un esempio semplicissimo con una sola tabella “USER”

Come prima cosa vediamo di seguire il pattern repository per l’accesso ai dati sfruttando i Generics, creiamo la nostra entità di dominio

   1: public class User
   2: {
   3:     public virtual int Id { get; set; }
   4:     public virtual string Name { get; set; }
   5:     public virtual string SurName { get; set; }
   6: }

creiamo l’interfaccia generica IRepository<T, idT> nel progetto di DataAccess e l’interfaccia specifica IUserRepository e la classe concreta che andremo (in seguito) ad implementare per interrogare il Database all’interno del progetto contenente le entità.

   1: namespace DataAccess
   2: {
   3:     public interface IRepository<T, idT>
   4:     {
   5:         T GetById(idT id);
   6:         idT Add(T entity);
   7:         void Delete(T entity);
   8:     }
   9: }
   1: namespace Domain.Repositories
   2: {
   3:     public interface IUserRepository : IRepository<User, int>
   4:     {
   5:     }
   6:  
   7:     public class UserRepository : IUserRepository
   8:     {
   9:         private ISession session;
  10:         
  11:         public UserRepository(ISession session)
  12:         {
  13:             this.session = session;
  14:         }
  15:  
  16:         public User GetById(int id)
  17:         {
  18:             throw new System.NotImplementedException();
  19:         }
  20:  
  21:         public int Add(User entity)
  22:         {
  23:             throw new System.NotImplementedException();
  24:         }
  25:  
  26:         public void Delete(User id)
  27:         {
  28:             throw new System.NotImplementedException();
  29:         }
  30:     }
  31: }

Le ultime cose che mancano sono:

  1. Un file di configurazione per dire a NH che driver utilizzare per l’accesso ai dati e alcune informazioni di configurazione: Anche per questo tipo di configurazione ci affideremo a fluent.NHibernate che mette a disposizione una fluent per configurare NH senza troppe linee di codice
  2. Il mapping dell’entità creata con l’entità logica sul database.

Configurazione Database:

   1: var cfg = new SQLiteConfiguration().InMemory().ShowSql();
   2: PersistenceModel model = new PersistenceModel();
   3: model.addMappingsFromAssembly(Assembly.GetAssembly(typeof(User)));
   4: sessionSource = new SessionSource(cfg.ToProperties(), model);

E il mapping dichiarativo utilizzando flluent-Nhibernate:

   1: public class UserMap : ClassMap<User>
   2: {
   3:     public UserMap()
   4:     {
   5:         WithTable("Users");
   6:         Id(x => x.Id);
   7:         Map(x => x.Name);
   8:         Map(x => x.SurName);
   9:     }
  10: }

La classe di mapping è abbastanza semplice, bisogna estendere la classe ClassMap<T> dove T è l’entità che stiamo mappando e nel costruttore, grazie all’uso di un bel po’  di Lambda e fluent interfaces, desciviamo il mapping, dicendogli che:

  1. La tabella su cui viene mappata l’entità è la tabella “users”
  2. La property Id della nostra entità rappresenta la chiave primaria della tabella (per convenzione, se non specifico nulla, il nome dell’entità e il nome della colonna sono gli stessi) salvata in una colonna “Id”.
  3. Le property Name e Surname sono mappate rispettivamente sulle colonne “Name” e “SurName” della tabella.

Faccio qui contento un mio amico / collega (roberto valenti) e procedo con scrivere un po’ di test per controllare che tutto sia predisposto correttamente.

Abbiamo quindi una prima configurazione del nostro layer di persistenza, sa dove dover andare a persistere i nostri dati grazie al file xml, sa che tipi di dati andrò a persistere (le nostre entità) e sa anche, tramite il nostro mapping, come persistere questi dati sul datasource. Vediamo di testare subito se tutto il lavoro di configurazione funziona. In fase di Setup del test, costruiamo un Configuration, che ci servirà per recuperare la ISession per interrogare i dati sulla sorgente dati. Verifichiamo che con la configurazione data, è possibile generare lo schema del database conoscendo i mapping, e se successivamente verifichiamo di essere in grado ad effettuare un inserimeno di uno User e successivamente recuperarlo sempre tramite la session.L’unico test che abbiamo implementato è quello di smoke, trovato girovagando per i blog mi sembrava un buon metodo per controllare che la configurazione sia andata senza nessun problema e lo schema sia stato generato correttamente.

   1: [TestFixture]
   2: public class MappingFixtures
   3: {
   4:     private Configuration config;
   5:  
   6:     [SetUp]
   7:     public void Context()
   8:     {
   9:         var cfg = new SQLiteConfiguration()
  10:             .ShowSql()
  11:             .ConnectionString.Is( "Data Source=nhibernate.db;Version=3");
  12:         PersistenceModel model = new PersistenceModel();
  13:         model.addMappingsFromAssembly(Assembly.GetAssembly(typeof(User)));
  14:         config = cfg.ConfigureProperties(new Configuration());
  15:         model.Configure(config);
  16:         SchemaExport export = new SchemaExport(config);
  17:         export.Execute(false, true, false, false);
  18:     }
  19:  
  20:     [Test]
  21:     public void smoke()
  22:     {
  23:         // pass if config is OK and schema have been created
  24:         Assert.IsTrue(true);
  25:     }
  26: }

Scriviamo ora i test per il salvataggio dell’entità.

   1: [Test]
   2: public void UserShouldBeAddedToDb()
   3: {
   4:     User u = new User();
   5:     u.Name = "Gianluca";
   6:     u.SurName = "Gravina";
   7:     ISessionFactory factory = config.BuildSessionFactory();
   8:     ISession session = factory.OpenSession();
   9:     IUserRepository repo = new UserRepository(session);
  10:     int x = repo.Add(u);
  11:     Assert.IsTrue(x != 0);
  12: }

Come da Red Green, il test non passerà, andiamo quindi a scrivere il metodo per fare passare il test.

   1: public int Add(User entity)
   2: {
   3:     return (int)session.Save(entity);
   4: }

Facciamo la stessa cosa con la GetById e con la Delete, scriviamo i test, ma prima aggiungiamo una GetAll ai repository in modo da recuperare tutte le entità di un certo tipo:

   1: public IList<User> GetAll()
   2: {
   3:     return session.CreateCriteria(typeof (User)).List<User>();
   4: }
ed ecco i test:
   1: [Test]
   2: public void CanRetrieveUsersFromDataBase()
   3: {
   4:     ISession session = config.BuildSessionFactory().OpenSession();
   5:     UserRepository repo = new UserRepository(session);
   6:     User u = repo.GetById(1);
   7:     Assert.IsNotNull(u);
   8:     Debug.WriteLine(u.Name + " " + u.SurName);
   9: }
  10:  
  11: [Test]
  12: public void CanDeleteUsersFromDataBase()
  13: {
  14:     ISession session = config.BuildSessionFactory().OpenSession();
  15:     UserRepository repo = new UserRepository(session);
  16:     int x = repo.GetAll().Count;
  17:     User u = repo.GetById(1);
  18:     repo.Delete(u);
  19:     session.Flush();
  20:     int y = repo.GetAll().Count;
  21:     Assert.IsTrue(x-y==1);
  22: }

e le relative implementazioni:

   1: public User GetById(int id)
   2: {
   3:     return session.Load<User>(id);
   4: }
   5:  
   6: public void Delete(User entity)
   7: {
   8:     session.Delete(entity);
   9: }

e con questo abbiamo (più o meno) finito.

Abbiamo configurato mapping e datasource con FluentInterface di Nhibernate, abbiamo creato le classi Repository per l’interrogazione del datasource e abbiamo testato che tutto funziona.

References:

Technorati Tags: ,

author: Gianluca Gravina | posted @ giovedì 15 gennaio 2009 16.18 | Feedback (0)

Fluent NHibernate


Recentemente mi sono imbattuto in questa fantastica libreria per NHibernate. Tra le varie funzionlità quelle di AutoMapping:James Gregory ha scritto un conciso tutorial per cominciare ad usarle.

Commenti: da approfondire l’uso e  le funzionalità proproste, ma di primo acchito direi eccezionale !

author: Gianluca Gravina | posted @ domenica 11 gennaio 2009 19.29 | Feedback (0)

Windows Live Writer


Dopo aver letto “dal twitter di Simone”, che l’ultima versione di WL non dava problemi con W2008 … ho scaricato e ho provato a installare …

Bhé, non è stato proprio indolore, se qualcuno dovesse avere problemi, soprattutto in presenza di precedenti versioni beta, vi consigli ZapMessenger.

HTH

author: Gianluca Gravina | posted @ martedì 16 dicembre 2008 15.14 | Feedback (0)

ASP.NET Mvc beta


Avevo letto qualcosa in merito ma non l’avevo collegata a questo problema.

E’ da un paio di giorni che apro il mio fidato VS2008 e spesso (troppo spesso) e volentieri (purtroppo) crashava senza motivo … guardo sull’eventlog e trovo una serie di errori di questo tipo:

.NET Runtime version 2.0.50727.3053 - Fatal Execution Engine Error (58450F92)

Googlando in giro trovo la causa: “Power Commands” di visual studio e ASP.NET Mvc beta installati contemporaneamente …

Quale disinstallo ??? Simone non me ne vorrà, ma visto che al momento non sto sviluppando nulla con MVC, I power commands sono ancora presenti e pronti all’uso … e il problema sembra effettivamente scomparso !

author: Gianluca Gravina | posted @ mercoledì 26 novembre 2008 15.05 | Feedback (1)

Visual studio next AND .Net Framework 4


PDC, tante novità … e tra tutte:

Visual studio & NET framework 4

author: Gianluca Gravina | posted @ lunedì 27 ottobre 2008 21.05 | Feedback (0)

Google Chrome


Sarò onesto, ho letto di sfuggita l'articolo sui principali quotidiani online ... ma non pensavo fosse quasi pronto da scaricare ...

Pronti via ... spettacolo !!!!!!!!

image

Prima ottima impressione: IMMEDIATO

Seconda ottima impressione, clic con il tasto destro ... ispeziona elemento ... ed eccolo, un piccolo emulo di Firebug che compare a farci vedere un po' di informazioni dettagliate sul DOM.

Come promesso, non esiste più un solo processo .... ma un processo per ogni Tab di chrome (di recente ho portato avanti una "crociata" per la reintroduzione delle chiamate sincrone per SL, eliminate proprio per evitare di "lockare" il processo di Explorer, o firefox, e ci siamo dovuti inventare un metodo tutto nostro per simulare) e diciamo che questo potrebbe eliminare tale problema ridando un po' di sincronismo che onestamente in alcuni momenti mi manca ...

Adesso aspetto:

1) Plugins vari (senza AdBlock non mi muovo dal mio firefox)

2) Migliori performance (in quanto a Ram i vari processi se ne prendono abbastanza, magari facendo le somme è la stessa quantità di FF ma non ho ancora fatto un controllo accurato).

3) i temi (giusto per fare il pignolo)

4) Silverlight

5) ... basta così, per il resto sembra fatto molto bene !

 

Technorati Tag:

author: Gianluca Gravina | posted @ martedì 2 settembre 2008 22.57 | Feedback (0)

Un piccolo progetto di esempio


Un piccolo progetto di esempio per testare la soluzione proposta in questo post:

http://code.google.com/p/syncsl/

Technorati Tag: ,

author: Gianluca Gravina | posted @ venerdì 29 agosto 2008 11.31 | Feedback (0)

Resyncing the APM model of Silverlight


Riprendo e ripubblico il post che ho scritto sul portale aziendale:

http://blog.avanadeadvisor.com/blogs/grava/archive/2008/08/28/11652.aspx

The Story

Often ideas are more powerful that tools, well my boss Roberto Chinelli (avanade experts page - Linkedin profile) had one of those ideas!

His passion for technology impress me everyday, and here are the results.

 

...

 

Last months have been hard months, smuggling around to find some usuful tip to resync the programming model in silverlight 2b2.
Well, we didn't find yet the motivation that pushed SL team to "remove" synchronous programming model from Silverlight, but we really need it, we're trying to implement an automatic tool that translate an application from anonther language to C# Code behind of Silverlight User Controls. Well it isn't an easy job, we know, but Asynchronous Programming Model (APM from now) wouldn't been good news for us.

The Problem

Let's make a first simple example. For saving data we implemented a WCF service, hosted in the same web app that hosts SL pages, once implemented and deployed, service is available to our Silverlight application, and let's see how we have to use it due to APM.

   1: protected void btn_Click(object sender, RoutedEventArgs e) 
   2: {
   3:     // service proxy initialization
   4:     ServiceApplication client = new ServiceApplication();
   5:     client.OnSaveDataCompleted += OnSaveDataCompleted;
   6:     client.SaveData(data);
   7: }
   8: protected void OnSaveDataCompleted(...)
   9: {
  10:     if (e.Error == null) 
  11:     {
  12:         // Object have been saved
  13:         // show message to user notifying the data saved
  14:     }
  15:     else 
  16:     {
  17:         // Show Exception message to the user (e.g. in a popup canvas)
  18:     }
  19: }

Well, no problem for that, the APM gives the developer the ability to work with time consuming methods witouth hanging the UI ... but let's think to a simple example in which we have to wait for the response, let's think to button that shuold save data to db that executes these operations:
1) Get data from db
2) checks result
3) if result == "OK " saves data into db
this use case should be written with this code snippet with apm:

   1: protected void btn_clic(object sender, RoutedEventArgs e)
   2: {
   3:     // service proxy initialization
   4:     ServiceApplication client = new ServiceApplication();
   5:     client.GetDataComplete = OnGetDataComplete;
   6:     client.GetDataAsync(id);
   7: }
   8: protected void OnDbLookupComplete(...) 
   9: {
  10:     if (e.Error == null)
  11:     {
  12:         // GetData, read data returned from method
  13:         // Check Results
  14:         if (e.Result.Equals("OK"))
  15:         {
  16:             ServiceApplication client = new ServiceApplication();
  17:             client.SaveDataComplete = OnSaveDataComplete;
  18:             client.SaveDataAsync(id,data);
  19:         }
  20:     }
  21:     else
  22:     {
  23:         // getData returned an error
  24:         // notify user with the exception message
  25:         // no save
  26:     }
  27: }
  28: protected void OnSaveDataCompleted(...)
  29: {
  30:     if (e.Error == null) 
  31:     {
  32:         // no Exceptions, saving data was OK !
  33:         // notify user
  34:     }
  35:     else 
  36:     {
  37:         // exception.message shown to the user ...
  38:     }
  39: }

You can easily see that well, everything probably will run without problems but we had to write a callback for each "atomic" Db Statement. In a synchronous environment we probably wrote something like:

   1: protected void btn_clic(object sender, RoutedEventArgs e)
   2: {
   3:     ServiceApplication client = new ServiceApplication();
   4:     string result = client.GetData(id);
   5:     if (id == "OK")
   6:     {
   7:             client.SaveData(id, data);
   8:     } 
   9:     else 
  10:     {
  11:         //notify error
  12:     }
  13: }

Yes, it's a simplified example, without exception handling etc, etc ... and for sure we can (if not handled with care) run into starvation and Blocked UI in our app ...
Well here was our problem, translating something (written in a different programming language) like:
x = getFormDB(data)
if (x)
    result = savedata(data, input)
else
    err
into c# with a Language Parser, should give us some headhache with the APM, but some less with the Synchronous way ...

Our "Sync" Service Proxy Wrapper

Working hard on it we were able to write down a "sync" proxy (notice the ""), it's a wrapper class for the proxy created from visual studio when adding service references. It "simulate" the synchoronous model, it's not sinchronous for real.
First of all we wrote an implementation for a Syncrhonization Object:

   1: public class CallSynchronization<T> where T : System.ComponentModel.AsyncCompletedEventArgs
   2: {
   3:     AutoResetEvent sync = new AutoResetEvent(false);
   4:     T result;
   5:     public void Completed(T result)
   6:     {
   7:         this.result = result;
   8:         sync.Set();
   9:     }
  10:     public bool Wait()
  11:     {
  12:         return sync.WaitOne();
  13:     }
  14:     public T Result { get { return result; } }
  15: }

With a "classic" serviceApplication ("MyServiceClient") automatically generated from the "Add service reference command", let's start with writing down the SyncProxy:

   1: public class MyServiceClientSync
   2: {
   3:     // async proxy
   4:     MyServiceClient client;
   5:     public MyServiceClientSync() 
   6:     {
   7:         client = new MyServiceClient();
   8:         client.GetDataCompleted += OnGetDataCompleted;
   9:         client.SaveDataCompleted += OnSaveDataCompleted;
  10:     }
  11:     void OnGetDataCompleted(object sender, GetDataCompletedEventArgs e)
  12:     {
  13:         CallSynchronization<GetDataCompletedEventArgs> sycnContext = (CallSynchronization<GetDataCompletedEventArgs>)e.UserState;
  14:         sycnContext.Completed(e);
  15:     }
  16:     void OnSaveDataCompleted(object sender, SaveDataCompletedEventArgs e)
  17:     {
  18:         CallSynchronization<GetDataCompletedEventArgs> sycnContext = (CallSynchronization<GetDataCompletedEventArgs>)e.UserState;
  19:         sycnContext.Completed(e);
  20:     }
  21:     public string GetData(int value)
  22:     {
  23:         CallSynchronization<GetDataCompletedEventArgs> syncContext = new CallSynchronization<GetDataCompletedEventArgs>();
  24:         client.GetDataAsync(value, syncContext);
  25:         syncContext.Wait();
  26:         if (sycnContext.Result.Error != null)
  27:             throw sycnContext.Result.Error;
  28:         else
  29:             return sycnContext.Result.Result; 
  30:     }
  31:     public void SaveData(int value, string data)
  32:     {
  33:         CallSynchronization<SaveDataCompletedEventArgs > syncContext = new CallSynchronization<SaveDataCompletedEventArgs >();
  34:         client.SaveDataAsync(value, data, syncContext);
  35:         syncContext.Wait();
  36:         if (sycnContext.Result.Error != null)
  37:             throw sycnContext.Result.Error;
  38:         else
  39:             return sycnContext.Result.Result; 
  40:     }
  41: }

So the call to the sync service will call the async method, setting a ManualResetEvent on the sync Object that will be set back when completed.
With these two simple classes we're now able to call sync methods on wcf services, but we will notice that even if logically correct, the Wait call on the ResetEvent will block the Main UI Thread and from a "wait" state it will pass to a "unreversible Coma state".
The problem as stated, is due to MainDispatcher, Dispatcher is a FrameworkElement property that returns an object used to "resyncing" asynchornous operation in main Silverlight Thread.

The Home-Made "MessageLoop"


So we wrote down a Class that "simulate" a MessageLoop, a Stack of statement called synchronously. First of all let's model a Simple "atomic execution block":

   1: public class ExecutorOperation
   2:     {
   3:         // Fields
   4:         private object[] _args;
   5:         private Delegate _operation;
   6:         private Action _action;
   7:         // Methods
   8:         internal ExecutorOperation(Delegate operation, object[] args)
   9:         {
  10:             this._operation=operation;
  11:             this._args=args;
  12:         }
  13:         internal ExecutorOperation(Action action)
  14:         {
  15:             this._action=action;
  16:             this._args=null;
  17:         }
  18:         internal void Invoke()
  19:         {
  20:             if (_operation!=null)
  21:                 this._operation.DynamicInvoke(this._args);
  22:             else
  23:                 this._action.DynamicInvoke(_args);
  24:         }
  25:         internal string Name
  26:         {
  27:             get
  28:             {
  29:                 if (_action!=null)
  30:                     return _action.Method.Name;
  31:                 else if (_operation!=null)
  32:                     return _operation.Method.Name;
  33:                 else
  34:                     return "(Unknown)";
  35:             }
  36:         }
  37:         internal bool IsEndBlock
  38:         {
  39:             get
  40:             {
  41:                 if (_action!=null&&_action.Method.DeclaringType==typeof(Executor)&&
  42:                     _action.Method.Name=="NotifyEndBlock")
  43:                     return true;
  44:                 else
  45:                     return false;
  46:             }
  47:         }
  48:     }

A simple class that wraps a single operation (invoked with a Delegate or with an action) and with a special property "IsEndBlock" that we will use to instruct the MessageLoop in order to close the queue and to start the execution of every ExecutorOperation instance inside the queue.
It's time to design and implement the Executor class, the main class of entire process.
Well, a first stub is this:
   

   1: public class Executor
   2:     {
   3:         static Queue<ExecutorOperation> executionQueue=new Queue<ExecutorOperation>();
   4:         static Thread worker;
   5:         static ManualResetEvent doLoop;
   6:         static AutoResetEvent stoploop;
   7:         public static bool isInBeginEndBlock=false;
   8:         public static event EventHandler<BeginBlockEventArgs> BeginExecutionBlock;
   9:         public static event EventHandler EndExecutionBlock;
  10:         public static event EventHandler<FailedEventArgs> FailedExecutionBlock;
  11:         public static event EventHandler<PromptEventArgs> OnPrompt;
  12:         static PromptResult result;
  13:         delegate void AskForPromptDelegate(string title, string message, bool inputBox, PromptButton button, PromptIcon icon);
  14:         ...
  15:         static Executor()
  16:         {
  17:             stoploop=new AutoResetEvent(false);
  18:             doLoop=new ManualResetEvent(false);
  19:             worker=new Thread(new ThreadStart(ExecutionLoop));
  20:             worker.Start();
  21:         }
  22:     }

we got a Queue (our messages that have to be dispatched and executed) a Thread, some signals, some delegates and EventHandlers. A static constructor assure us that every needed component is initialized and start our personal implementation of the Message Loop Queue.
In order to give the possibility to our classes to Instruct the message loop we have to tell him when we're starting to enqueuing and when we've finished with the enqueuing, so in our class we will use something like:

   1: Executor.BeginBlock();
   2: Executor.Invoke(... our action);
   3: Executor.EndBlock();


and let's see what Begin and End Block methods contains in our Executor class:

   1: /// <summary>
   2:         /// The first delegate that the executor needs in order to process the queue
   3:         /// </summary>
   4:         public static void BeginBlock()
   5:         {
   6:             BeginBlock(null);
   7:         }
   8:         public static void BeginBlock(string message)
   9:         {
  10:             if (isInBeginEndBlock)
  11:                 return;
  12:             isInBeginEndBlock=true;
  13:             Invoke(new Action<string>(NotifyBeginBlock), message);
  14:         }
  15:         /// <summary>
  16:         ///  Notify the listeners that a BeginBlock have been signaled
  17:         /// </summary>
  18:         private static void NotifyBeginBlock(string msg)
  19:         {
  20:             if (BeginExecutionBlock!=null)
  21:                 MainDispatcher.Current.BeginInvoke(BeginExecutionBlock, null, new BeginBlockEventArgs(msg));
  22:         }
  23:         /// <summary>
  24:         /// End Block is the starter delegate, once setted this delegate the Executor starts with the dequeuing of operations
  25:         /// </summary>
  26:         public static void EndBlock()
  27:         {
  28:             if (!isInBeginEndBlock)
  29:                 return;
  30:             isInBeginEndBlock=false;
  31:             Invoke(NotifyEndBlock);
  32:             doLoop.Set(); // start dequeue block
  33:         }
  34:         /// <summary>
  35:         /// Notidy the queue that an EndBlockStatement have been signaled into executor
  36:         /// </summary>
  37:         private static void NotifyEndBlock()
  38:         {
  39:             if (EndExecutionBlock!=null)
  40:                 MainDispatcher.Current.BeginInvoke(EndExecutionBlock, null, null);
  41:         }

       

For notifying purposes we wrote down two events in order to notify every listeners that Executor have been "somehow" started ... and we will notify back when Executor finished his work. Those events are really useful for usability matters, we remember that isn't possible to set MainDispatcher in a "wait state" so we have to simulate something that exclude every possible interaction between User an User Interface (a modal panel with a "please wait" text should work).
So we have methods to start the enqueuing and notify the Executor that we've done with enqueuing, telling him to start, between those statements we put our method invocation list (often only one method that wraps every statement we need) that run in Synchronous mode. The method used in the executor class is Invoke:

   1: public static ExecutorOperation Invoke(Action a)
   2:        {
   3:            lock (executionQueue)
   4:            {
   5:                ExecutorOperation operation=new ExecutorOperation(a);
   6:                executionQueue.Enqueue(operation);
   7:                if (!isInBeginEndBlock)
   8:                    doLoop.Set();
   9:                return operation;
  10:            }
  11:        }
  12:        public static ExecutorOperation Invoke(Delegate d, params object[] args)
  13:        {
  14:            lock (executionQueue)
  15:            {
  16:                ExecutorOperation operation=new ExecutorOperation(d, args);
  17:                executionQueue.Enqueue(operation);
  18:                if (!isInBeginEndBlock)
  19:                    doLoop.Set();
  20:                return operation;
  21:            }
  22:        }

Let's have a look to the main logic of our class, a simple method that, with particular care to waitsignals and locks, start to dequeuing our operations and process them.

   1: /// <summary>
   2: /// Main logic, dequeue operation when notified from a EndBlock statement and process operations sync
   3: /// </summary>
   4: private static void ExecutionLoop()
   5: {
   6:     WaitHandle[] events=new WaitHandle[] { stoploop, doLoop };
   7:     while (true)
   8:     {
   9:         int eventId=WaitHandle.WaitAny(events);
  10:         if (eventId==0) // stopped
  11:             break;
  12:         else
  13:         {
  14:             lock (executionQueue)
  15:             {
  16:                 while (executionQueue.Count>0)
  17:                 {
  18:                     ExecutorOperation operation=executionQueue.Dequeue();
  19:                     try
  20:                     {
  21:                         Debug.WriteLine("Executor Invoke: {0}", operation.Name);
  22:                         operation.Invoke();
  23:                     }
  24:                     catch (Exception ex)
  25:                     {
  26:                         if (isInBeginEndBlock)
  27:                         {
  28:                             // dequeue until end is reached
  29:                             while (true)
  30:                             {
  31:                                 ExecutorOperation nullOperation=executionQueue.Dequeue();
  32:                                 if (nullOperation.IsEndBlock)
  33:                                 {
  34:                                     MainDispatcher.Current.BeginInvoke(new Action<Exception, ExecutorOperation>(NotifyFailed), ex, operation);
  35:                                     break;
  36:                                 }
  37:                             }
  38:                         }
  39:                         else
  40:                             MainDispatcher.Current.BeginInvoke(new Action<Exception, ExecutorOperation>(NotifyFailed), ex, operation);
  41:                     }
  42:                 }
  43:                 doLoop.Reset();
  44:             }
  45:         }
  46:     }
  47: }

In order to let this up and running you have to create a simple class that returns in every moment the Dispatcher in order to resync with Silverlight Main Thread, every call that involves modifies to User Interface objects have to be called within MainDispatcher Method.
The Simplest way to do it is to set it in the Application_Startup method of our app:

   1: public class App : Application 
   2: {
   3:     private void Application_Startup(object sender, StartupEventArgs e)
   4:     {
   5:         mainDispatcher = this.RootVisual.Dispatcher;
   6:     }
   7:     System.Windows.Threading.Dispatcher mainDispatcher;
   8:     public System.Windows.Threading.Dispatcher MainDispatcher
   9:     {
  10:         get
  11:         {
  12:             return mainDispatcher;
  13:         }
  14:     }
  15: }
  16: [DebuggerStepThrough]
  17: public static class MainDispatcher
  18: {
  19:     public static System.Windows.Threading.Dispatcher Current
  20:     {
  21:         get { return ((App)Application.Current).MainDispatcher; }
  22:     }
  23: }

Let's try it with silverlight


Let's recall what we've tried to do before executor implementation:

   1: protected void btn_clic(object sender, RoutedEventArgs e)
   2: {
   3:     ServiceApplication client = new ServiceApplication();
   4:     string result = client.DbLookUp(data);
   5:     InputBoxCanvas icv = new InputBoxCanvas();
   6:     string userInput = icv.ShowModal();
   7:     client.SaveData(data, userInput);
   8: }


and let's rewrite with our Executor implementation in mind, the odd is that we have to Wrap the method inside another that uses Executor blocks, but no more cascading completed event handler, let's imagine a Silverlight button with a btn_click handler that have to manage the statements we can see in this example, let's write it down:

   1: protected void btn_clic(object sender, RoutedEventArgs e)
   2: {
   3:     Executor.BeginBlock();
   4:     Executor.Invoke(dostuff);
   5:     Executor.EndBlock();
   6: }
   7: protected void doStuff()
   8: {
   9:     MyServiceClientSync client = new MyServiceClientSync();
  10:     string result = client.GetData(this.txtInput1.Text);
  11:     if (result == "OK")
  12:          client.SaveData(this.txtInput1.Text, this.DataToSave.Text);
  13: }


It's clear, it's not as in (for instance) winform, but we're able to call a sequence of statements in synchronous way even if those statements will go to get data from database or open some prompt message and wait for user input.

 

Conclusions:

Our implementation of Executor is extendible, we hooked in it some handlers in order to manage common operations like Showing modal "Wait panels", asking for user prompt, notifying exceptions. It's open to every idea in order to get this component working better.

We're also testing it in order to understand if there is any silent bug behind it (memory leaks or unexpected exceptions).

We're sure this is not the best way to work, perhaps Async is better, but we're also sure that developers, even if often bad developers, have to fail in order to understand, and if fail differs form something that brings to understanding well, they will remain bad developers.

We're sure that silverlight should not be used only for video straming.

We simulate a synchronous way to work with, we don't implemented a synchronous call to wcf services.

Feedback are really appreciated (and needed for improvements) !


Reference:


http://www.rickgaribay.net/archive/2008/03/07/silverlight-2.0-service-integration-three-steps-forward-two-steps-back.aspx

http://neverindoubtnet.blogspot.com/2008/07/big-silverlight-troubles-no-synchronous.html

 

Technorati Tag: ,,

author: Gianluca Gravina | posted @ giovedì 28 agosto 2008 12.47 | Feedback (5)

Questa non è male ...


Approfitto di questa fine di agosto per rimettere il dito nella piaga, la mia non è una crociata contro l'IPhone e chi mi conosce lo sa benissimo ... però leggere notizie del genere (ben inteso ... la Apple non c'entra nulla), mi fa venire in mente, con un sorriso, uno dei film di Fantozzi, tutti a divertirsi e lui a maneggiare carrucole per far muovere i "falsi dipendenti cartonati" ...

 

http://www.repubblica.it/2005/b/sezioni/scienza_e_tecnologia/mondomac/iphone-file-finte/iphone-file-finte.html?ref=hpspr2

 

:)

author: Gianluca Gravina | posted @ sabato 23 agosto 2008 18.58 | Feedback (0)

OT2 - Samsung vs IPhone


Visto che il post su IPhone ha creato un po' di subbuglio ... non potevo non postare alcuni video veramente spassosi che mostrano alcune debolezze del gioiellino (per me rimane un gioiellino per tanti versi) di casa Apple

 

author: Gianluca Gravina | posted @ martedì 29 luglio 2008 9.41 | Feedback (8)