DarioSantarelli.Blog("UgiDotNet");

<sharing mode=”On” users=”*” />
posts - 176, comments - 144, trackbacks - 3

My Links

News


This is my personal blog. These postings are provided "AS IS" with no warranties, and confer no rights.

logo linkedIn logo Twitter logo FaceBook logo RSS logo Email

Logo DotNetMarche
Logo XPUG Marche



Tag Cloud

Archives

Post Categories

My English Blog

domenica 25 marzo 2012

Reactive Extensions (Rx) e Weak events

Come molti sanno, il classico uso degli eventi in C# ha un effetto collaterale: se non viene gestita correttamente la strong reference che si forma tra l’oggetto che espone l’evento (EventSource) e gli oggetti che registrano gli event handlers (EventListeners), si rischia di generare dei memory leak. Infatti, se l’ EventSource ha un ciclo di vita più lungo rispetto agli EventListeners, questi ultimi, se non si deregistrano correttamente dall’evento, vengono tenuti in vita in memoria anche quando non ci sono più riferimenti attivi verso di essi e quindi non vengono reclamati dalla garbage collection fintantoché l’EventSource è attivo.
Nella letteratura esistono diverse tecniche per evitare questa tipologia di memory leak: questo articolo (“Weak Events in C#”) in particolare ne parla in maniera molto chiara e presenta vari approcci per implementare degli “weak event” (listener-side o source-side), ovvero degli eventi a cui un EventListener può registrarsi senza incorrere nel rischio di diventare un memory leak. Le soluzioni ormai più diffuse sfruttano la classe WeakReference , che permette di referenziare un oggetto in modo “debole”, ovvero mantenendolo comunque reclamabile dalla garbage collection.
Giusto per citare un caso noto, WPF introduce un design pattern chiamato “Weak Event”. Esso fondalmente si basa sull’implementazione concreta di un WeakEventManager, ovvero un “dispatcher” centralizzato per ogni evento che vogliamo gestire in modalità weak (molti degli aspetti del data binding di WPF sono implementati tramite weak event). In questo pattern, poi, tutti i listener del weak event devono implementare l’interfaccia IWeakEventListener e quindi registrarsi/deregistrarsi all’evento passando per i metodi statici AddListener() e RemoveLister() che il WeakEventManager mette a disposizione. Come si può intuire, questa soluzione introduce dell’infrastruttura che non sempre è semplice gestire, soprattutto in applicazioni esistenti che fanno un uso complesso e massivo di eventi.
Pochi giorni fa ho provato quindi ad affrontare la questione attraverso il Reactive Extensions (Rx) framework e devo dire che le soluzioni che si possono trovare sono alquanto iteressanti e flessibili. Vorrei mostrarne una in particolare.

Supponiamo di avere una classe EventSource che espone un evento pubblico Event.

public class EventSource 
{
public event EventHandler Event;

public void FireEvent()
{
EventHandler handler = Event;
if (handler != null) handler(this, EventArgs.Empty);
}

public int GetEventListenersCount()
{
return Event.GetInvocationList().Count();
}
}

 

Per fini di test esponiamo anche un metodo FireEvent() che scatena l’evento ed un metodo GetEventListenersCount() che ci restituisce il numero di listener correntemente registrati all’evento. Creiamo quindi uno StrongEventListener, ovvero un listener che crea una strong reference all’EventSource tramite la classica sintassi di registrazione ad un evento. Poichè questo oggetto non effettua esplicitamente la deregistrazione, diventa un potenziale memory leak.

public class StrongEventListener 
{
public StrongEventListener(EventSource eventSource)
{
eventSource.Event += HandleEvent;
}

private void HandleEvent(object sender, EventArgs eventArgs)
{
Console.WriteLine("StrongEventListener> Event Received!");
}
}


Per riuscire a implementare la corrispettiva versione weak (WeakEventListener) in modo semplice e veloce è possibile sfruttare le Reactive Extensions (Rx).
Anzitutto partiamo dal risultato che vorremmo ottenere, ovvero un oggetto che possa registrarsi all’evento del nostro EventSource in modalità weak, ovvero senza correre il rischio di diventare un potenziale memory leak:

public class WeakEventListener 
{
public WeakEventListener(EventSource eventSource)
{
eventSource.ObservableEvent()
.WeakSubscribe(this, (me, e) => me.HandleEvent(e.Sender, e.EventArgs));
}

private void HandleEvent(object sender, EventArgs eventArgs)
{
Console.WriteLine("WeakEventListener> Event Received!");
}
}


Per ottenere questo risultato le fasi da seguire sono due:

1) Anzitutto, per leggibilità creiamo un extension method di EventSource che ne converte l’evento in un IObservable grazie al classico metodo Observable.FromEventPattern(...) di Rx. Dietro le quinte viene creato e restituito un oggetto che sarà in grado di gestire la registrazione e la deregistrazione all’evento, aggiungendo e rimuovendo gli event handlers (strong references) verso l’istanza di EventSource in esame. 

public static IObservable<EventPattern<EventArgs>> ObservableEvent(this EventSource eventSource) 
{
return Observable.FromEventPattern<EventHandler, EventArgs>(h => (s, e) => h(s, e),
h => eventSource.Event += h,
h => eventSource.Event -= h);
}


2) Creiamo un extension method di IObservable<T> che chiamiamo WeakSubscribe. Esso ci permette di passare come argomento un oggetto subscriber ed una action da eseguire a fronte di una notifica (onNext) da parte della sequenza osservabile, in modo tale che il subscriber possa essere notificato tramite l’action in modalità “weak”, ovvero senza mantenere degli strong reference con la sequenza osservabile.

public static IDisposable WeakSubscribe<T, TSubscriber>(this IObservable<T> observable,
TSubscriber subscriber, Action<TSubscriber, T> action)
where TSubscriber : class
{
if (action.Target != null) throw new ArgumentException("action target must NOT refer a class instance, " +
"in order to avoid a strong reference to target");
var subscriberWeakReference = new WeakReference(subscriber);
IDisposable subscription = null;
Action<T> onNext = item =>
{
var subscriberObj = subscriberWeakReference.Target as TSubscriber;
if (subscriberObj != null) action(subscriberObj, item);
else subscription.Dispose();
};
subscription = observable.Subscribe(onNext);
return subscription;
}


Qui il gioco si fa interessante ed anche abbastanza complicato: come si osserva, anzitutto il prerequisito è che venga mantenuta una weak reference sul subscriber tramite una WeakReference. Dopodiché, con una lambda definiamo l’onNext di un AnonymousObserver della sequenza osservabile, il quale ad ogni notifica controlla se il subscriber è ancora vivo (subscriberWeakReference.Target != null). In caso positivo, viene eseguita l’action che fa da “passacarte” tra l’observer ed il subscriber, altrimenti viene chiamata la Dispose() sulla subscription dell’observer (il che nel nostro esempio si traduce nella deregistrazione dall’evento dell’EventSource (eventSource.Event -= h)).

Ora arriva il trucco: per evitare che il subscriber diventi un potenziale memory leak, action.Target non deve riferire direttamente l’istanza del subscriber, altrimenti significherebbe che il chiamante del nostro metodo sta passando il puntatore this su action.Target (in altre parole, action.Target == null significa che il delegato action invoca un metodo statico e non di istanza). La action quindi deve essere una lambda che chiama metodi di istanza del subscriber tramite il riferimento che essa stessa fornisce come parametro, piuttosto che catturare il riferimento implicito a this:

.WeakSubscribe(this, (me, e) => this.HandleEvent(e.Sender, e.EventArgs)); // –> FAIL (ArgumentException)
.WeakSubscribe(this, (me, e) => me.HandleEvent(e.Sender, e.EventArgs)); // –> OK

Questo controllo ci garantisce che la lambda che poi specifichiamo sull’onNext non generi una strong reference verso il subscriber tramite l’action. Ciò renderebbe il subscriber un potenziale memory leak. Capiamo meglio il perché: nel nostro esempio, avendo usato un Observable.FromEventPattern, quando chiamiamo la observable.Subscribe(onNext) scateniamo internamente la registrazione all’evento dell’EventSource (h => eventSource.Event += h). h è un EventHandler che tramite una closure interna di Rx crea una strong reference tra l’EventSource e la onNext. La onNext a sua volta mantiene uno strong reference verso la action. Quindi, a cascata, se l’action che usiamo nella lambda fosse un metodo di istanza del subscriber, l’istanza stessa del subscriber diventerebbe un potenziale memory leak poiché manterrebbe uno strong reference attivo nella subscription.

Conclusioni

Il grande vantaggio di questa soluzione è che può introdurre in progetti esistenti una semplice gestione thread-safe di weak events al costo di una banale AddReference a System.Reactive.dll. Infatti, a differenza del “Weak Event Pattern”, questo approccio ha praticamente impatto zero sull’ infrastruttura.
L’unico svantaggio invece è che la subscription.Dispose() avviene eventualmente sull’ onNext della sequenza osservabile, il che tradotto nel nostro esempio significa che la deregistrazione dei delegati dall’evento dell’EventSource avviene eventualmente SOLO a fronte dello scatenare tale evento. Di conseguenza, la onNext che usiamo per fare observable.Subscribe(onNext) potrebbe confluire in dei memory leak fintantoché l’evento non viene scatenato dall’EventSource (come ho provato a dimostrare con lo Unit test qua sotto). I nostri WeakEventListener invece vivono felicemente il loro ciclo di vita sicuri di non diventare dei memory leak, poiché ovviamente non sono loro a registrasi direttamente all’evento dell’EventSource
In conclusione, prima di considerare questa implementazione e comunque in generale, cerchiamo sempre di valutare attentamente qual’è il ciclo di vita degli EventListener rispetto agli EventSource :).

[TestMethod] 
public void WeakEvent_With_Rx()
{
var eventSource = new EventSource();
var strongListener = new StrongEventListener(eventSource);
var weakListener = new WeakEventListener(eventSource);

Assert.AreEqual(2, eventSource.GetEventListenersCount());

eventSource.FireEvent();
// OUTPUT:
// StrongEventListener> EventReceived!
// WeakEventListener> EventReceived!

strongListener = null;
weakListener = null;

GC.Collect();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

 
   Assert.AreEqual(2, eventSource.GetEventListenersCount()); 
// 2 Memory Leak
// - StrongEventListener has NOT been garbage collected (Obviously)
// - WeakEventListener has been garbage collected!
// – onNext action delegate is still a memory leak



eventSource.FireEvent();
// OUTPUT:
// StrongEventListener> EventReceived!

Assert.AreEqual(1, eventSource.GetEventListenersCount());
// 1 Memory Leak (StrongEventListener (Obviously))
}


HTH

posted @ domenica 25 marzo 2012 16.15 | Feedback (0) | Filed Under [ .NET ]

sabato 10 dicembre 2011

Spring.NET REST Client

Sono sempre stato un appassionato di soluzioni REST-ful e la nuova versione di Spring.NET REST Client costituisce a mio avviso un ottimo strumento per implementare il client di un qualunque servizio REST in maniera molto semplice e potente, non solo su framework 2.0/3.5/4.0, ma anche su Silverlight 3 e 4, Compact Framework 2.0/3.5 e Windows Phone 7.
Queste API client fanno capo ad una classe di riferimento chiamata RestTemplate. Si tratta a tutti gli effetti di un helper che astrae in maniera molto semplice ognuno dei sei principali metodi HTTP che possono essere supportati da un servizio REST (GET, POST, HEAD, OPTIONS, PUT, DELETE). I metodi di questa classe infatti seguono una “naming convention” in cui la prima parte del metodo indica il verbo HTTP della richiesta e la seconda parte esplicita la rappresentazione della risposta, la quale può essere ad esempio un oggetto deserializzato o l'Uri di una risorsa.
Ecco un esempio:

 var newCustomer = new Customer { FirstName = "Dario", LastName = "Santarelli" };
 RestTemplate restTemplate = new RestTemplate("http://localhost");
 
 // POST (Creates) the Customer resource  
Uri
newCustomerUri = restTemplate.PostForLocation("/myRestService/Customers", newCustomer);
 
 // GET the Customer resource 
Customer customerObj = restTemplate.GetForObject<Customer>(newCustomerUri);
HttpResponseMessage
<Customer> customerMsg = restTemplate.GetForMessage<Customer>(newCustomerUri);
string customerBodyString = restTemplate.GetForObject<string>(newCustomerUri);
byte[] customerBodyBytes = restTemplate.GetForObject<byte[]>(newCustomerUri);
 
 // DELETE the Customer resource
restTemplate.Delete(newCustomerUri);

 

Anzitutto si chiede al servizio REST di creare una risorsa Customer tramite il metodo PostForLocation(), il quale restituisce la Uri della risorsa appena creata, che possiamo quindi accedere con una GET in varie forme: tramite il metodo GetForObject<T>() potremmo cercare di deserializzare la risposta HTTP direttamente in un oggetto Customer oppure potremmo ottenerne la rappresentazione raw (es. XML, JSON etc.) specificando il tipo T come string. Infine, cancelliamo la risorsa tramite una DELETE. Da notare come GetForObject<T>() e GetForMessage<T>() dietro le quinte eseguano entrambi due GET HTTP ma ritornano nel primo caso un oggetto mentre nel secondo caso un message-wrapper di tipo HttpResponseMessage che espone informazioni aggiuntive come StatusCode e Header. Per questi metodi esistono anche degli overload con il suffisso “Async” per gestire appunto pattern di invocazione asincrona (indispensabili su Silverlight e Windows Phone).

Per completezza, c’è da dire che per effettuare le chiamate REST si può utilizzare anche un metodo alternativo Exchange() che permette di interagire direttamente con i verbi HTTP ed oggetti di tipo HttpEntity (da specifica HTTP 1.1, section 7) eventualmente per impostare degli header custom nella richiesta. Ad esempio...

HttpEntity requestEntity = new HttpEntity(); 
requestEntity.Headers["headerKey"] = "headerValue";
HttpResponseMessage<Customer> response = restTemplate.Exchange<Customer>("/myRestService/Customers/{id}", 
HttpMethod.GET, requestEntity, 1);
string responseHeader = response.Headers["responseHeaderKey"]; 
Customer customer = response.Body;
HttpStatusCode statusCode = response.StatusCode;
string statusDescription = response.StatusDescription;
 

Per deserializzare correttamente il tipo T specificato ad esempio nei metodi GetForObject<T>() e GetForMessage<T>(), il RestTemplate usa internamente una classe chiamata MessageConverterResponseExtractor<T> che è in grado di convertire una risposta HTTP in un'istanza di tipo T tramite dei MessageConverters. A mio avviso la vera potenza di RestTemplate sta proprio in questa parte, ovvero nel fatto che può essere facilmente configurato per supportare diversi MessageConverters, delle classi il cui ruolo è convertire oggetti in messaggi HTTP e viceversa in base al media type. Le strategie di conversione sono veramente tante e possono sfruttare sia serializer “standard” del framework come XmlSerializer, DataContractSerialier, DataContractJsonSerializer piuttosto che altre strategie di conversione custom (es. basate su SyndacationFeed e SyndicationItem per feed ATOM e RSS piuttosto che su Json.NET per accedere ai valori JSON in modo diretto etc.). Ad esempio, possiamo fare in modo che i messaggi HTTP con media type application/json vengano gestiti dal DataContractJsonSerializer del framework semplicemente scrivendo…

RestTemplate restTemplate = new RestTemplate("http://localhost");
restTemplate.MessageConverters.Add(new JsonHttpMessageConverter());
 

N.B.: i MessageConverters caricati per default cambiano in base al target framework di compilazione. Tuttavia, tutti i converter devono implementare l’interfaccia IHttpMessageConverters, quindi possiamo facilmente implementarcene uno custom e renderlo operativo in modo molto semplice ed intuitivo.

Oltre ai MessageConverters , Spring.NET REST Client supporta anche altre features molto interessanti:

  • RestTemplate espone una proprietà RequestFactory che offre la possibilità di personalizzare il meccanismo di generazione delle richieste HTTP. Per default questa proprietà viene valorizzata con un’ istanza della classe WebClientHttpRequestFactory, che a sua volta internamente usa la classe HttpWebRequest del framework. Ciò offre gratuitamente la possibilità di supportare proxy HTTP ad esempio.
  • Altra proprietà interessante di RestTemplate è RequestInterceptors, ovvero una collezione di interceptors che ci permette di inserirci sia in modo sincorno che in modo asincrono in un qualunque punto della pipeline di crezione ed esecuzione della richiesta HTTP, magari per modificare la richiesta al volo piuttosto che per iniettare gestioni custom di error management, logging, etc.
Technorati tags: ,

posted @ sabato 10 dicembre 2011 22.29 | Feedback (1) | Filed Under [ .NET ]

domenica 27 novembre 2011

[Entity Framework v4] Creazione dei tipi proxy

L’approccio POCO di Entity Framework v4 permette di applicare la persistence-ignorance anche su oggetti custom attraverso la creazione a runtime (via Reflection.Emit) di proxy dinamici che li derivano in modo da permettere all’EF di implementarci sopra i suoi meccanismi di change tracking e lazy loading. In particolare, il change tracking è una feature che un oggetto proxy potrebbe continuare a mantenere anche quando attraversa tier diversi, il che ovviamente sposta l’attenzione verso problematiche legate alla serializzazione di oggetti il cui tipo effettivo non è disponibile a compile time.

Ad esempio, se volessimo serializzare e deserializzare su tier diversi un nostro oggetto Customer, il processo che si troverebbe a deserializzare il corrispondente tipo proxy autogenerato dall’EF dovrebbe essere in grado di riconoscere un oggetto di tipo...

System.Data.Entity.DynamicProxies.Customer_1941BFC82D29CB600101CF80564EA2CCAE83E1ACF5EF4B32E482FBDF13337DC9 

definito nell’assembly (fittizio)

EntityFrameworkDynamicProxies-<NostroAssembly>, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

che ovviamente non è un “known type” nel suo AppDomain.

Ora, se utilizzassimo WCF come infrastruttura di comunicazione, non avremmo alcuna feature “gratis” per serializzare e deserializzare direttamente i tipi proxy di EF poiché il DataContractSerializer è in grado di gestire solo “known types” e purtroppo i proxy dinamici non lo sono. Quindi, le principali opzioni per aggirare il problema sono:

  • Disabiltare la creazione dei proxy tramite la proprietà ProxyCreationEnabled (es. context.ContextOptions.ProxyCreationEnabled = false;) in modo da lavorare direttamente sui known types, ma perdendo ovviamente il change tracking automatico di EF.
  • Usare la classe ProxyDataContractResolver per mappare i tipi proxy direttamente sui nostri tipi custom (esempio) che però a quel punto non supporterebbero più né il change tracking né tantomeno il lazy loading di EF. Soluzione sicuramente migliore per continuare a mantenere il change tracking su più tier è far riferimento a “self-tracking entities” ovvero ad oggetti che non hanno nessuna dipendenza con EF e definiscono internamente la loro logica di change tracking (ad esempio implementando l’interfaccia INotifyPropertyChanged).

Senza WCF c’è sempre la serializzazione “old-fashioned”. Ad esempio quella binaria. Affinché i tipi proxy siano resi disponibili alla deserializzazione binaria, EF ci espone un metodo CreateProxyTypes sull’oggetto ObjectContext per assicurare che i tipi proxy relativi ad un dato MetadataWorkspace siano deserializzabili all’interno di un AppDomain diverso da quello che invece li serializza. Occorre tenere a mente che la deserializzazione dei proxy ricostruisce degli oggetti che ovviamente non mantengono il loro comportamento originale (quindi niente change tracking).

Il seguente metodo, invocato ad esempio allo startup di una applicazione, è in grado di scatenare nell'AppDomain corrente la creazione di tutti i tipi proxy corrispondenti alle entity definite negli EDM (Entity Data Model) deployati come risorse all’interno di una lista di assembly, in modo che il BinaryFormatter sia poi in grado di trattare tali tipi come “known types”.

 

public static void CreateProxyTypes(Assembly[] assembliesToConsider) 
{
   var metadataWorkspace = new MetadataWorkspace(new string[] { "res://*/" }, assembliesToConsider);     
using (var entityConnection = new EntityConnection(metadataWorkspace, new SqlConnection()))
{
using (var context = new ObjectContext(entityConnection))
{
foreach (var assembly in assembliesToConsider)
{
context.CreateProxyTypes(assembly.GetTypes());
}
}
}
}

 

Da notare come la classe EntityConnection richieda nel costruttore una connessione ad un data source che ai nostri fini però non è importante specificare. Per questo viene passata una SqlConnection “vuota”. Ciò che conta è referenziare gli EDM deployati come risorse all’interno degli assembly specificati. CreateProxyTypes è un metodo intelligente in quanto è in grado di ignorare tutti i tipi che non sono rappresentati negli EDM, quindi possiamo tranquillamente passargli come argomento tutti i tipi definiti all’interno di un assembly: esso internamente sarà in grado di controllare la sua cache di tipi e di trasformare in known types soltanto i tipi proxy di EF che ancora non sono stati creati. Per avere controprova dei tipi proxy correntemente registrati nel nostro AppDomain, basta chiamare il metodo statico ObjectContext.GetKnownProxyTypes().

posted @ domenica 27 novembre 2011 19.55 | Feedback (0) | Filed Under [ Entity Framework ]

domenica 16 ottobre 2011

[AOP] UI e dintorni con PostSharp

L’AOP ormai sta diventando un paradigma di programmazione sempre più diffuso specialmente nel mondo .NET dove strumenti come PostSharp, DotSpect (Compile-time static injection) e Spring.NET, Castle Windsor, Enterprise Library Policy Injection Application Block (Dynamic Proxy) rendono sempre più semplice la gestione dei “cross-cutting concerns” che provengono dal mondo Object-Oriented. Personalmente sono stato sempre un entusiasta di PostSharp e in questo post vorrei citare un paio di aspect che ho sempre valutato positivamente nella gestione di codice “vicino” alla UI, specialmente nel mondo Windows Forms e WPF.

Il primo aspect ha a che vedere con il DataBinding. Sappiamo tutti come implementare l’interfaccia INotifyPropertyChanged sia noioso ed anche a fronte di snippet per velocizzare il processo di scrittura il risultato che otteniamo si allontana dall’idea di avere codice (almeno) esteticamente “impeccabile”. Per questo l’aspect che PostSharp introduce a riguardo è tanto semplice quanto geniale in quanto permette, tramite un banale attributo, di agganciare l’implementazione dell’interfaccia INotifyPropertyChanged ad un oggetto ed eventualmente alle sue specializzazioni. Ad esempio, se definissimo un oggetto Customer nel seguente modo…

[NotifyPropertyChanged] 
public class Customer
{
   public string Name { get; set; }   
public string Surname { get; set; }
public string Email { get; set; }
...
}


…tramite l’attributo [NotifyPropertyChanged] impostato a livello di classe otteniamo nell’assembly di output una injection della solita implementazione dell’interfaccia INotifyPropertyChanged. L’ implementazione dell’aspect, riportata anche nel sito di PostSharp, è la seguente…

[Serializable] 
[IntroduceInterface(typeof(INotifyPropertyChanged),OverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)]
public class NotifyPropertyChangedAttribute : InstanceLevelAspect, INotifyPropertyChanged
{
[ImportMember("OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;

[IntroduceMember(Visibility = Visibility.Family,
IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this.Instance, new PropertyChangedEventArgs(propertyName));
}
}

[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;

[OnLocationSetValueAdvice]
[MulticastPointcut(Targets = MulticastTargets.Property, Attributes = MulticastAttributes.Instance)]
public void OnPropertySet(LocationInterceptionArgs args)
{
if (args.Value == args.GetCurrentValue()) return;
args.ProceedSetValue();
this.OnPropertyChangedMethod.Invoke(args.Location.Name);
}
}


Il codice è abbastanza autoesplicativo. La feature degna di nota in particolare è la possibilità di estendere automaticamente questo aspect anche alle classi ereditate semplicemente impostando a livello di classe MulticastAttributeUsage.Inheritance = MulticastInheritance.Strict. 

Un altro aspect che ho sempre ritenuto molto utile ha a che vedere con il thread dispatching, ovvero con la gestione dell’esecuzione di un metodo sul thread dell’interfaccia piuttosto che su un worker thread (es. in WPF controllando il thread associato al DispatcherSynchronizationContext corrente). Anche in questo ambito PostSharp permette di definire due attributi [OnGuiThread] e [OnWorkerThread] a livello di metodo, che iniettano la comune logica di controllo del SynchronizationContext in modo da forzare l’esecuzione del metodo rispettivamente sul thread dell’interfaccia o su un thread secondario (link al codice sorgente).
Come risultato, diventa molto semplice applicare questi aspect anche su codice legacy.

[OnWorkerThread] 
void SaveCustomer()
{
Customer.Save();
ShowMessage("Customer Saved!");
}  

[OnGuiThread]
void ShowMessage(string message)
{
MessageBox.Show(message);
}


Il pacchetto di istallazione di PostSharp (disponibile anche su NuGet) aggiunge a Visual Studio due toolbox veramente utili: l’Aspect Browser che ci permette navigare su quelli che sono gli aspect utilizzati nell’intera solutione e l’ Affected code toolbox che ci riassume tutte le classi e i metodi che sono soggetti ad aspects. Inoltre, l’intellisense viene esteso in modo da visualizzare dei summary sulle static injections che gli aspect introducono nell’assembly. Ad esempio, per quanto riguarda il precedente aspect NotifyPropertyChanged, i tooltip che otteniamo sono i seguenti:

 
 

Technorati tags: ,,

posted @ domenica 16 ottobre 2011 20.29 | Feedback (0) | Filed Under [ .NET WPF ]

giovedì 25 agosto 2011

[WPF] Drag and Drop con Rx

Le Reactive Extensions (Rx) del framework .NET sono un set di API dedicate a ciò che nella letteratura prende il nome di “reactive programming”. Rx nasce dalla distinzione di due scenari:
  • pull: un programma agisce estraendo dati dall’ambiente in modo interattivo (es. un’iterazione su una sequenza di dati prelevati dall’ambiente).
  • push: un programma reagisce ai dati che l’ambiente “spinge” verso di esso (es. un event-handler è un esempio di reazione in uno scenario push)

Data questa distinzione, Rx si basa sulla dualità tra il pattern Iterator e il pattern Observer. In breve, il pattern Observer in uno scenario push è analogo al pattern Iterator in uno scenario pull. Nel pattern Iterator, infatti, l’Iterator è l’attore che accede all'insieme degli elementi di un contenitore in modo interattivo. Il pattern Observer può essere interpretato in modo equivalente se si considera che l’ambiente genera spontaneamente i singoli elementi e li spinge verso il programma, ad esempio attraverso eventi. Quando uno stesso evento viene scatenato più volte, di fatto viene prodotta una sequenza di elementi di tipo “evento”, il che rende ovvia a questo punto la relazione con il pattern Iterator. Rx è stato introdotto quindi per mettere a disposizione degli sviluppatori un modello di programmazione push-based, duale rispetto al ben più conosciuto pull-based, pur non aggiungendo nulla di nuovo rispetto a quest’ultimo. Semplicemente, il modello push-based offre modi diversi se non più semplici per approcciare problemi comuni legati alla programmazione asincrona ed alla composizione di eventi.

Rx implementa il pattern Observer tramite le due interfacce IObserver<T> e IObservable<T>, corrispondenti a IEnumerator/IEnumerable del pattern Iterator. IObservable<T> costituisce una sorta di “collezione asincrona” di elementi (generati in momenti diversi quando si parla di eventi). La relazione tra IObservable<T> e IObserver<T> si trova nel fatto che ad un IObservable<T> è possibile registrare uno o più IObserver<T> (tramite il metodo IObservable<T>.Subscribe(IObserver<T> observer) ). IObserver<T> possiede tre metodi: OnNext(…), OnCompleted() e OnError(…). Essi vengono invocati dall’ IObservable<T> rispettivamente quando possiede nuovi dati, completa un’operazione e si verifica un errore.

Drag and Drop e LINQ-to-events

Nella pratica, uno dei (classici) casi in cui Rx mostra la sua potenza è nell’implementazione del Drag and Drop di un oggetto grafico. Infatti, a differenza delle comuni soluzioni event-based che intercettano eventi e sincronizzano variabili di stato con lo scopo di aggiornare correttamente la posizione dell’oggetto grafico, le Reactive Extensions consentono di convertire gli eventi .NET di interesse in sequenze osservabili che possiamo interrogare tramite query LINQ. Vediamo un esempio concreto. Uno dei classici approcci per implementare il drag and drop in WPF (come in Windows Froms o Silverlight) si basa sostanzialmente sulla sincronizzazione di tre eventi logici di un oggetto grafico: MouseDown, MouseUp e MouseMove. Nello specifico, in WPF, supponendo di avere il seguente XAML…

<Window x:Class="WebRequestWithRxSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Loaded="Window_Loaded">
    <Canvas>
        <StackPanel Name="myPanel">
            <Rectangle Fill="Maroon" Width="100" Height="100" />
        </StackPanel>
    </Canvas>
</Window>

… per gestire il drag and drop dello StackPanel “myPanel” potremmo sincronizzarne i tre eventi MouseLeftButtonDown, MouseLeftButtonUp e MouseMove in modo da gestirne il riposizionamento nel Canvas per ogni evento MouseMove compreso temporalmente tra un MouseLeftButtonDown ed un MouseLeftButtonUp. Quindi, ogni volta che si verifica l’evento MouseLeftButtonDown, per ogni successivo evento MouseMove aggiorniamo la posizione dello StackPanel all’interno del Canvas, tenendo conto della posizione del cursore relativa all’angolo in alto a sinistra dello StackPanel. Il tutto finché non viene scatenato l’evento MouseLeftButtonUp.

 

Point relativeMouseDownPosition = new Point();

myPanel.MouseLeftButtonDown += (s, e) =>

{

  if (myPanel.CaptureMouse())

  {

   relativeMouseDownPosition = e.GetPosition(myPanel);

  }

};

myPanel.MouseLeftButtonUp += (s, e) => { myPanel.ReleaseMouseCapture(); };

myPanel.MouseMove += (s, e) =>

{

  if (myPanel.IsMouseCaptured)

  {

    Point absoluteMousePosition = e.GetPosition(this);

    Point newPanelPosition = new Point(absoluteMousePosition.X - relativeMouseDownPosition.X,

                                       absoluteMousePosition.Y - relativeMouseDownPosition.Y);

    Canvas.SetLeft(myPanel, newPanelPosition.X);

    Canvas.SetTop(myPanel, newPanelPosition.Y);

  }

};

Il codice qua sotto implementa la stessa cosa tramite Rx. All’inizio la sintassi può sembrare un po’ meno chiara, ma con il tempo ci si accorge di come in realtà sia molto più leggibile e soprattutto manutenibile rispetto al codice precedente, in quanto si basa su un modello “push” query-based.

 

 var relativeMouseDownPositions = Observable.FromEventPattern<MouseButtonEventArgs>(myPanel, "MouseLeftButtonDown")
                                      .Do(evt => myPanel.CaptureMouse())
                                             .Select(evt => evt.EventArgs.GetPosition(myPanel));
 
 var mouseUpEvents = Observable.FromEventPattern<MouseButtonEventArgs>(myPanel, "MouseLeftButtonUp")
                           .Do(evt => myPanel.ReleaseMouseCapture());
 
 var absoluteMousePositions = Observable.FromEventPattern<MouseEventArgs>(myPanel, "MouseMove") 
.Select(evt => evt.EventArgs.GetPosition(this));
 
 IObservable<Point> dragPoints = from relativeMouseDownPosition in relativeMouseDownPositions
                                 from absoluteMousePosition in absoluteMousePositions.TakeUntil(mouseUpEvents)
                                 select new Point(absoluteMousePosition.X - relativeMouseDownPosition.X,
                                                  absoluteMousePosition.Y - relativeMouseDownPosition.Y); 
 

dragPoints.ObserveOn(
SynchronizationContext.Current).Subscribe(point =>
 {
    Canvas.SetLeft(myPanel, point.X);
    Canvas.SetTop(myPanel, point.Y);
 });  

Alcune considerazioni

  1. La prima cosa che salta all’occhio è come sia possibile convertire un evento .NET in una sequenza osservabile tramite il metodo Observable.FromEventPattern(…) , il quale permette di specificare il tipo di EventArgs dell’evento, l’oggetto che espone l’evento da convertire ed il nome dell’evento. In particolare, la seconda riga di codice denota come sia possibile convertire l’evento MouseLeftButtonUp dello StackPanel in una sequenza osservabile di tipo IObservable<EventPattern<MouseButtonEventArgs>>.
  2. Rx definisce un suo set di extension methods per comporre sequenze osservabili nello stesso modo in cui LINQ to Objects compone sequenze enumerabili. Ad esempio, il metodo Observable.TakeUntil<TSource, TOther>(…) ritorna tutti gli elementi della sequenza osservabile sorgente finché l’altra sequenza osservabile non produce un valore. Nel nostro esempio, questo extension method è stato usato per filtrare gli eventi MouseMove tra un evento MouseLeftButtonDown ed un MouseLeftButtonUp.
  3. Il metodo Observable.ObserveOn<TSource>(…) permette di fare in modo che le notifiche asincrone agli observer vengano effettuate su un thread o uno scheduler specificato. Nel mondo WPF, come in altre piattaforme che supportano un SynchronizationContext, per evitare eccezioni cross-thread quando si interagisce con la UI di solito si interagisce con il contesto di sincronizzazione tramite la chiamata ObserveOn(SynchronizationContext.Current) che in WPF in particolare è costituito da un oggetto di tipo DispatcherSynchronizationContext.

Note di installazione: se stiamo lavorando su Visual Studio 2010, il modo più veloce per integrare Rx nel nostro progetto è NuGet. Digitando “Rx” sulla textbox di ricerca dovremmo ottenere la seguente schermata. In particolare, il package Rx-Main referenzia l’assembly System.Reactive.dll mentre il package Rx-Testing referenzia l’assembly Microsoft.Reactive.Testing.dll.

 

posted @ giovedì 25 agosto 2011 16.27 | Feedback (2) | Filed Under [ .NET WPF ]

domenica 3 luglio 2011

System.Collections.Concurrent: via tutti i lock!

Per garantire la thread-safety nell’accesso a collezioni di oggetti in contesti multi-thread, fino ad oggi siamo stati abituati ad utilizzare i più svariati meccanismi di lock, magari prevedendo diversi livelli di granularità.
In questo ambito il framework 4.0 fornisce il namespace System.Collections.Concurrent che offre un set di classi thread-safe che si possono usare al posto di quelle di System.Collections e System.Collections.Generic.

Anzitutto, salutiamo con una certa soddisfazione la classe ConcurrentDictionary<TKey, TValue>, versione thread-safe della classe Dictionary.

Vengono forniti poi tre oggetti che implementano IProducerConsumerCollection<T>, un’interfaccia che definisce due metodi TryAdd e TryTake per scenari “producer/consumer”, ovvero scenari in cui più thread aggiungono o rimuovono elementi dalla stessa collection in modo concorrente. Gli oggetti in questione sono:

  • ConcurrentBag<T>: implementazione thread-safe di una collezione non ordinata di elementi (eventualmente duplicabili). Questo oggetto è particolarmente ottimizzato per scenari in cui lo stesso thread produce e consuma gli elementi del bag.
  • ConcurrentQueue<T>: versione concorrente di una coda FIFO. A differenza di quanto si sente dire spesso erroneamente, questa classe non costituisce un semplice wrapper della classe Queue ( tipo Queue.Synchronized() ), bensì qualcosa di più potente e performante che internamente fa uso di meccanismi di sincronizzazione e validazione più ottimizzati basati sulle classi SpinWait e Interlocked.
  • ConcurrentStack<T>: versione concorrente di uno stack LIFO. 

Infine abbiamo l’oggetto BlockingCollection<T>, che di fatto costituisce una classe di più alto livello. Una BlockingCollection è a tutti gli effetti un wrapper di un oggetto che implementa l’interfaccia IProducerConsumerCollection<T> (come i sopra citati ConcurrentBag<T>, ConcurrentQueue<T> e ConcurrentStack<T>). Ciò significa che di suo non implementa un meccanismo di storage interno, bensì introduce soltanto la logica di gestione thread-safe per scenari producer/consumer. Infatti, se non forniamo al costruttore un IProducerConsumerCollection, per default viene istanziata internamente una ConcurrentQueue (FIFO). Poiché la BlockingCollection è utile come buffer di scambio condiviso tra più thread, essa fornisce gratis anche dei meccanismi di blocco (metodi Add() e Take()) grazie ai quali possiamo fare in modo che i thread “producer” si blocchino se la collezione è piena (aspettando quindi che almeno un elemento venga rimosso dalla collezione) oppure viceversa che i thread “consumer” si blocchino quando cercano di rimuovere un elemento dalla collection interna vuota.

posted @ domenica 3 luglio 2011 14.31 | Feedback (0) | Filed Under [ .NET ]

lunedì 16 maggio 2011

[Silverlight 5] ICustomTypeProvider

La beta di Silverlight 5 ha introdotto una nuova interfaccia ICustomTypeProvider che permette di effettuare il binding tra elementi della UI ed oggetti la cui struttura non è nota a compile-time. Nello specifico, l’esigenza che questa interfaccia soddisfa è evidente quando si lavora con dati provenienti da fonti eterogenee (database, istanze XML, oggetti JSON, dati binari, csv etc.) che si desiderano presentare senza ricompilare ogni volta che viene aggiunto/rimosso un attributo, una colonna o un campo dalla sorgente dati. In questi contesti si può valutare di rendere flessibile il meccanismo di binding permettendo di aggiungere/rimuovere proprietà a runtime lasciando al motore di databinding il compito di determinarne il tipo senza obbligare lo sviluppatore a ri-compilare/ri-deployare l’applicazione ogni volta che si necessita di una modifica al model. L’interfaccia ICustomTypeProvider è così definita:

public
 interface ICustomTypeProvider
{
    Type GetCustomType();
}

Quando la si implementa, viene chiesto di ritornare un oggetto di tipo System.Type. Oggi l’engine di Silverlight 5 controlla in fase di binding se l’oggetto che stiamo passando implementa o meno l’interfaccia ICustomTypeProvider per capire se usare i metadati del nostro tipo custom piuttosto che del fallback System.Type.
Bisogna dire che l’implementazione di questa interfaccia richiede un certo sforzo iniziale, poiché implica la creazione di un tipo custom in grado di gestire ad esempio un “repository” di proprietà (PropertyInfo) che possiamo aggiungere/rimuovere dinamicamente. Inoltre, non dobbiamo dimenticarci della solita INotifyPropertyChanged utile per il corretto funzionamento del binding e di INotifyDataErrorInfo per la validazione. I benefici che otteniamo sono tuttavia notevoli rispetto alle versioni precedenti di Silverlight, dove non è possibile derivare Type o PropertyInfo per questi scopi.

Esempio di utilizzo
A titolo di esempio, proviamo a definire una classe DictionaryObject che, come il nome fa intendere, espone a mo’ di dictionary l’elenco delle sue proprietà in modo tale che possono essere aggiunte e rimosse a runtime. L’obiettivo è rendere “binding-friendly” queste coppie chiave-valore!

public class DictionaryObject : ICustomTypeProvider, INotifyPropertyChanged
{
    private Dictionary<string, object> _properties = null;

    public event PropertyChangedEventHandler PropertyChanged;

    public object this[string propertyName]
    {
        get { return _properties.ContainsKey(propertyName) ? _properties[propertyName] : null; }
        set
        {
            AddProperty(propertyName);
            if (_properties[propertyName] != value)
            {
                _properties[propertyName] = value;
                OnPropertyChanged(propertyName);
            }
        }
    }

    public DictionaryObject()
    { 
        _properties = new Dictionary<string, object>(); 
    }

    public bool AddProperty(string name) 
    { 
        return AddProperty(name, null); 
    }

    public bool AddProperty(string name, object value)
    {
        if (!_properties.ContainsKey(name))
        {
            _properties.Add(name, value);
            return true;
        }
        else return false;
    }

    public bool RemoveProperty(string name)
    {
        if (_properties.ContainsKey(name))
        {
            _properties.Remove(name);
            return true;
        }
        else return false;
    }

    public Type GetCustomType() 
    { 
        return new DictionaryObjectType(_properties);
    }

    protected void OnPropertyChanged(string key)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(key));
    }
}

Affinché il runtime di Silverlight tratti le coppie chiave-valore alla stregua di proprietà vere e proprie di un generico oggetto del CLR, il trucco sta nel definire un tipo custom in grado di fornire al volo tutti i metadati che servono in fase di binding. Riportiamo solo le righe di codice rilevanti…

public class DictionaryObjectType : Type
{
   private Type _proxyType = typeof(DictionaryObject);
   private Dictionary<string, object> _propertiesDictionary = null;
  
  
   public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
   {
      PropertyInfo[] properties = _proxyType.GetProperties(bindingAttr);

      if (BindingFlags.Instance == (bindingAttr & BindingFlags.Instance) &&
          BindingFlags.Public == (bindingAttr & BindingFlags.Public))
          {
             return GetDynamicProperties().ToArray();
          }

      return properties;
   }
  
  
   private List<PropertyInfo> GetDynamicProperties()
   {
     List<PropertyInfo> properties = new List<PropertyInfo>();

     foreach (string key in _propertiesDictionary.Keys)
     {                
        if (_propertiesDictionary[key] != null
// null values will be ignored!
        {
            properties.Add(new DictionaryObjectPropertyInfo(_propertiesDictionary[key].GetType(), typeof(DictionaryObject), key));
        }
     }

     return properties;
   }
}

Per gli oggetti che implementano l’interfaccia ICustomTypeProvider, abbiamo la possibilità di intercettare le richieste di informazioni di reflection che provengono dal motore di databinding e fornire metadati personalizzati. Nel nostro esempio, per comunicare al runtime come fare la get e la set sulle proprietà di un DictionaryObject, abbiamo bisogno di ereditare anche PropertyInfo.

public class DictionaryObjectPropertyInfo : PropertyInfo
{
    private Type _propertyType;
    private Type _declaringType;
    private string _name;

    public DictionaryObjectPropertyInfo(Type propertyType, Type declaringType, string propertyName)
    {
        _propertyType = propertyType;
        _declaringType = declaringType;
        _name = propertyName;
    }
 
    ...
   
    public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        DictionaryObject dictionaryObject = (DictionaryObject)obj;            
        return dictionaryObject[Name];
    }
      
    public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
    {
        DictionaryObject dictionaryObject = (DictionaryObject)obj;
        dictionaryObject[Name] = value;
    }
}

OK non abbiamo bisogno di altro! Proviamo a utilizzare concretamente la classe DictionaryObject. Creiamo una view con una DataGrid le cui colonne vengono autogenerate. L’obiettivo è di rendere il databinding bidirezionale e consistente rispetto al tipo CLR di ciascuna proprietà esposta.

<UserControl x:Class="ICustomTypeProviderSample.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
   xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <StackPanel x:Name="LayoutRoot" HorizontalAlignment="Center" Margin="20">
        <sdk:DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Items}" />       
    </StackPanel>
</UserControl
>

Nel viewmodel definiamo le proprietà degli items come coppie chiave-valore.

public class MainPageViewModel : INotifyPropertyChanged
{
    ...

    public ObservableCollection<DictionaryObject> Items { get; private set; }        

    public MainPageViewModel()
    {            
        Items = new ObservableCollection<DictionaryObject>();
            
       
DictionaryObject newItem = new DictionaryObject();        
        newItem["ID"] = 1;
        newItem["Name"] = "Item 1";
        newItem["UnitPrice"] = 5.3;
        newItem["Created"] = DateTime.Now;    
    
        Items.Add(newItem);

        ...
   }
}  

La view risultante è coerente rispetto ai tipi delle proprietà. L’oggetto DictionaryObject implementa l’interfaccia INotifyPropertyChanged (e preferibilmente INotifyDataErrorInfo) quindi la nostra interazione con le celle della DataGrid si riflette direttamente sul valore delle proprietà coinvolte nel databinding.

 

L’esempio mostrato è molto banale ma fa intendere quanti miglioramenti possono essere concepiti. Il codice completo di questo esempio è scaricabile al seguente link.

Ma non esisteva qualcosa del genere anche in WPF? 
A chi possiede familiarità con l’interfaccia ICustomTypeDescriptor di WPF (la quale risolve lo stesso problema di ICustomTypeProvider di Silverlight 5… i nomi addirittura sono più o meno gli stessi!) verrà subito in mente la domanda “Perché non hanno aggiunto direttamente l’interfaccia ICustomTypeDescriptor”?
Leggendo la documentazione MSDN non sono riuscito ad estrarre una risposta chiara. Sembrerebbe che il motivo (per ora almeno) sia dovuto al fatto che ICustomTypeDescriptor richiede una sua gerarchia di classi  (TypeDescriptor, EventDescriptor, etc.) che di fatto duplica la gerarchia di reflection, il che incrementerebbe la dimensione del pacchetto Silverlight da installare client-side.

E il DLR?
Questa feature in realtà non ha nulla a che vedere con il DLR. Oggetti del DLR come ExpandoObject o DynamicObject (o qualunque implementazione dell’interfaccia IDynamicMetaObjectProvider) non apportano alcun tipo di informazione sulle loro proprietà. Il motore del databinding ha invece bisogno di queste informazioni per effettuare correttamente conversioni di tipo per tutto ciò che non sia una semplice stringa. Ad esempio, se abbiamo una TextBox in binding con una proprietà DateTime di un oggetto, quando inseriamo un nuovo valore nella TextBox l’engine valuta il tipo della proprietà e converte il testo contenuto nella TextBox da stringa a DateTime e viceversa. Se invece si effettua il binding su una proprietà di un ExpandoObject, il valore della proprietà sarà convertito semplicemente a stringa e non possono essere fatte valutazioni di binding. Insomma, in WPF è possibile associare elementi del DLR agli elementi della UI, ma non avviene nessuna conversione di tipo… tutto ciò su cui possiamo lavorare sono solo stringhe.
In definitiva, mentre il DLR è conveniente quando si interagisce con altri linguaggi o altre piattaforme, non è consigliabile per il databinding e l’interazione con la UI.

Riferimenti
Segnalo i due interessanti articoli da cui ho preso spunto:

posted @ lunedì 16 maggio 2011 23.51 | Feedback (0) | Filed Under [ Silverlight ]

mercoledì 20 aprile 2011

Validare documenti XHTML con XmlPreloadedResolver

Una classe molto interessante che ci fornisce il Framework 4.0 è XmlPreloadedResolver. Come si intuisce dal nome, il suo scopo è quello di risolvere risorse pre-caricate che possono essere referenziate da un’istanza XML. Questa funzionalità è veramente comoda quando non è desiderabile creare chiamate di rete per reperire risorse esterne (es. un DTD) o quando si lavora offline, con evidenti benefici sulle performance. Un esempio concreto di utilizzo di questa classe potrebbe intervenire nella validazione di contenuti XHTML 1.x, magari all’interno di un HttpModule ASP.NET… tipo questo.
Il plus risiede nel fatto che nell’assembly System.Xml.Utils sono già “embeddati” i DTD dello standard XHTML 1.0 Transitional, Strict e Frameset (incluse le entities speciali), nonché dello standard RSS 0.91

Insistendo sul discorso della validazione di contenuti XHTML precaricati, a titolo esemplificativo ho provato a realizzare una semplice classe XhtmlValidator, che espone un metodo Validate() (la validazione del contenuto) ed il relativo Resolver (che deve reperire l’eventuale DOCTYPE esterno da processare nella validazione).
N.B.: Per versioni del Framework precedenti alla 4.0, possiamo ovviamente decidere di svilupparci in casa un Resolver custom (ereditando la classe XmlResolver) che sostanzialmente assolva lo stesso compito.

public class XhtmlValidator

{

   public XmlResolver Resolver { get; set; }

 

   public XhtmlValidator() { }

   public XhtmlValidator(XmlResolver resolver) { Resolver = resolver; }

 

   public bool Validate(string xhtml, out List<XmlValidationError> errors)

   {

      List<XmlValidationError> validationErrors = new List<XmlValidationError>();    

                                                   

      XmlReaderSettings settings = new XmlReaderSettings();

      settings.ValidationType = ValidationType.DTD;

      settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;

      settings.DtdProcessing = DtdProcessing.Parse;

      if (Resolver != null) settings.XmlResolver = Resolver;

      settings.ValidationEventHandler += (s, e) => validationErrors.Add(new XmlValidationError(e.Message,e.Severity));           

 

      Stream xhtmlStream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(xhtml));  

 

      using (XmlReader xmlReader = XmlReader.Create(xhtmlStream, settings))

      {

        try { while (xmlReader.Read()) ; }

        catch (Exception ex) { validationErrors.Add(new XmlValidationError(ex.Message, XmlSeverityType.Error)); }               

      }

 

      errors = validationErrors;

 

      return (validationErrors.Count == 0);

    }     

}

 

public class XmlValidationError

{

   public string Message { get; private set; }

   public XmlSeverityType Severity { get; private set; }

 

   public XmlValidationError(string message, XmlSeverityType severity)

   {

     Message = message;

     Severity = severity;

   }

}

 

La classe XmlValidationError è stata introdotta semplicemente come “astrazione” rispetto a due tipologie di notifiche: quelle derivanti dal parsing della struttura del documento (le Read() dell’XmlReader) e quelle derivanti dal processo di validazione (i ValidationEvent scatenati dalla classe XmlReaderSettings). 
Vediamo quindi come possiamo sfruttare XmlPreloadedResolver in questo contesto tramite una banale applicazione Console…

static void Main(string[] args)

{

   ////GOOD XHTML 1.0 (conformant)

   //string xhtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" +

   //               "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"it\" lang=\"it\">" +

   //               "<head><title>GOOD XHTML 1.0 Document</title></head><body><p>A paragraph here is OK</p></body></html>";

 

   //// BAD XHTML 1.0 (non conformant)

   string xhtml = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" +

                  "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"it\" lang=\"it\">" +

                  "<head><title>BAD XHTML 1.0 Document</title></head><body><span>A span here is NOT OK</span></body></html>";

       

   XmlPreloadedResolver resolver = new XmlPreloadedResolver(XmlKnownDtds.Xhtml10);

   XhtmlValidator validator = new XhtmlValidator(resolver);

 

   List<XmlValidationError> validationErrors = new List<XmlValidationError>();

   bool success = validator.Validate(xhtml, out validationErrors);

 

   if (success) Console.WriteLine("Success");

   else

   {

      Console.WriteLine("Errors: " + validationErrors.Count.ToString());

      foreach (XmlValidationError error in validationErrors) Console.WriteLine(string.Format("- Message: {0} (Severity: {1})", error.Message, error.Severity));               

   }          

}

 

Eseguendo il codice precedente senza essere connessi alla rete, si osserva come il XmlPreloadedResolver faccia il suo lavoro e ci permetta di validare il contenuto XHTML 1.0 Strict di esempio tramite il relativo DTD embeddato nell’assembly System.Xml.Utils.

Domanda: E se volessimo usare XmlPreloadedResolver su documenti XHTML 1.1?
Ebbene, sembra che la strada dello standard XHTML 1.1 non sia stata battuta, forse per la forte modularizzazione della specifica o forse per l'effettiva scarsa adozione dello standard nel web. Fatto sta che in questo contesto non abbiamo "known" DTD pre-caricati (neanche quelli “core” definiti nel DTD principale), quindi dobbiamo pre-caricarceli “a mano” in questo modo…


XmlPreloadedResolver xmlPreloadedResolver = new XmlPreloadedResolver(XmlKnownDtds.Xhtml10);
xmlPreloadedResolver.Add(new Uri("http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"), File.ReadAllBytes("C:\\...\\xhtml11.dtd"));
xmlPreloadedResolver.Add(new Uri("http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-inlstyle-1.mod"), File.ReadAllBytes("C:\\...\\xhtml-inlstyle-1.mod"));
xmlPreloadedResolver.Add(new Uri("http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-text-1.mod"), File.ReadAllBytes("C:\\...\\xhtml-text-1.mod"));
xmlPreloadedResolver.Add(new Uri("http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-hypertext-1.mod"), File.ReadAllBytes("C:\\...\\xhtml-hypertext-1.mod"));
xmlPreloadedResolver.Add(new Uri("http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-list-1.mod"), File.ReadAllBytes("C:\\...\\xhtml-list-1.mod"));
etc...

 

Tag di Technorati: ,,,

posted @ mercoledì 20 aprile 2011 20.23 | Feedback (0) | Filed Under [ .NET ]

sabato 19 febbraio 2011

[WCF 4.0] Default Endpoints

Un’ approccio introdotto in WCF 4.0 è la “Convention Over Configuration”. Sostanzialmente essa permette di assegnare una configurazione predefinita ad un endpoint in assenza di una configurazione esplicita (cosa impossibile in WCF 3.x). Ciò avviene tramite la definizione di un mapping tra lo schema/protocollo (es. http, tcp etc.) e le varie tipologie di binding (es. BasicHttpBinding, NetTcpBinding etc.) all’interno del file:
%WINDIR%\Microsoft.NET\Framework\v4.0.30319\Config\machine.config.comments

<protocolMapping>
    <
clear />
    <
add scheme=
"http" binding="basicHttpBinding" bindingConfiguration="" />
    <
add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration="" />
    <
add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration="" />
    <
add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration="" />
</
protocolMapping>

Volendo verificare la cosa, creiamo un’ interfaccia di prova

[ServiceContract]
public interface IDummyService
{
   [OperationContract(IsOneWay=true)]
   void DoNothing();         
}


Puntualizziamo il fatto che la scelta di impostare l’operation contract come “OneWay” deriva dal fatto che un contratto esposto da un endpoint che utilizza NetMsmqBinding può supportare solo operazioni one-way. Implementiamo quindi l’interfaccia IDummyService...

public class MyDummyService : IDummyService
{
   public void DoNothing() { }       
}


Costruiamo un test per verificare quanto asserito, ponendo attenzione a non specificare alcuna configurazione per gli endpoint:

ServiceHost host = new ServiceHost(typeof(MyDummyService),
                                   new Uri("http://localhost:8080/MyDummyService"),
                                   new Uri("net.tcp://localhost:8081/MyDummyService"),
                                   new Uri("net.pipe://localhost/MyDummyService"),
                                   new Uri("net.msmq://localhost/private/MyDummyService"));                       
host.Open();            
 
Assert.AreEqual(4, host.Description.Endpoints.Count);            
Assert.IsTrue(host.Description.Endpoints[0].Binding is BasicHttpBinding);
Assert.IsTrue(host.Description.Endpoints[1].Binding is NetTcpBinding);
Assert.IsTrue(host.Description.Endpoints[2].Binding is NetNamedPipeBinding);   
Assert.IsTrue(host.Description.Endpoints[3].Binding is NetMsmqBinding);


Verde!
Ovviamente, se ne abbiamo la necessità, possiamo effettuare l’override di questi mappings sia a livello di machine che a livello di application. Ad esempio, se specificassimo la seguente configurazione nell’app.config…

<configuration>
  <system.serviceModel>
    <
protocolMapping
>
      <
add scheme="http" binding="webHttpBinding"
/>
    </
protocolMapping
>
  </
system.serviceModel
>
</
configuration>


…il test precedente fallirebbe poiché lo schema http è stato assegnato al WebHttpBinding e non più al BasicHttpBinding. Invece, il seguente test avrebbe successo

ServiceHost host = new ServiceHost(typeof(MyDummyService),new Uri("http://localhost:8080/MyDummyService"));
host.Open();
Assert.IsTrue(host.Description.Endpoints[0].Binding is WebHttpBinding);


Per avere più informazioni riguardo al funzionamento di questa feature e delle altre novità di WCF 4.0 rimando a questo link MSDN:
http://msdn.microsoft.com/en-us/library/ee354381.aspx (A Developer's Introduction to Windows Communication Foundation 4)

Tag di Technorati:

posted @ sabato 19 febbraio 2011 16.34 | Feedback (0) | Filed Under [ WCF ]

martedì 11 gennaio 2011

Migliorare le performance di un’applicazione ASP.NET MVC

Esistono molti aspetti da tenere in considerazione quando si parla di performance di applicazioni Web. Infatti, le problematiche legate al protocollo HTTP spesso scavalcano il prode tentativo di scrivere codice server-side e client-side ottimizzato, poiché migliorare le performance di un’ applicazione Web significa anche minimizzare il traffico dati tra client e server. Le best practices per velocizzare il caricamento delle pagine web non sono poche, come si può appurare semplicemente consultando l’utilissimo documento di Yahoo Developer Network dal titolo “Best Practices for Speeding Up Your Web Site”  (parliamo di 35 best practices divise in 7 categorie!)
In questo post ho cercato di aggregare una serie di appunti/approfondimenti relativi a tecniche comuni di miglioramento delle performance di un’applicazione ASP.NET MVC (e non solo) in base alla mia esperienza. I punti su cui cercherò di soffermarmi in particolare sono:

  • Output Cache
  • Compressione HTTP 
  • Minimizzazione e combinazione di risorse esterne (JavaScript e Css) 
  • Minimizzazione del markup HTML 
  • Ottimizzazione della generazione degli URL
  • Note di configurazione


Output cache

Un’ applicazione Web dovrebbe sempre prevedere una politica di caching delle risposte HTTP sia client-side (browser + proxy) che server-side, in modo tale che il contenuto di una risposta HTTP non venga rigenerato inutilmente ogni volta che il client ne effettua la richiesta. Il modo più semplice ed immediato per abilitare il caching in un’applicazione MVC è aggiungere l’attributo [OutputCache] a livello di action. Questa funzionalità è praticamente identica a quella che incontriamo nelle Web Forms, ma cambia nel fatto che non deve essere applicata a livello di View (quindi non dovremmo usare la direttiva <%@ OutputCache %>), anche perché non avrebbe molto senso nel pattern MVC, in cui la View dovrebbe essere agnostica rispetto alle politiche di caching. L’unica proprietà che non è supportata nella versione MVC è VaryByControl.
In genere è sempre consigliabile cercare di estrarre le impostazioni di caching nel web.config definendo dei profili, in modo da non dover ricompilare il sorgente ogni volta che cambiamo le nostre strategie di caching. Quindi, nel controller dovremmo scrivere qualcosa del genere:

public class HomeController : Controller
{
[OutputCache(CacheProfile="MyCacheProfile")]
public ActionResult Index()
{                            
return View();
}

}


e nel web.config

<configuration> 
<
system.web>
   <caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="MyCacheProfile" enabled="true" duration="600" varyByParam="None" location="Any"
/>
          ...

</outputCacheProfiles>
</outputCacheSettings>
</caching>
</system.web>
</configuration>


Ovviamente il controllo programmatico della cache di ASP.NET è sempre a disposizione tramite la solita classe System.Web.Caching.Cache. L’unica e non indifferente limitazione di questo approccio risiede nel fatto che la cache è in-process rispetto all’applicazione web e dunque non è distribuita. In applicazioni che richiedono un alto grado di scalabilità, invece, sarebbe preferibile prendere in considerazione architetture di caching distribuito (es. Velocity). A riguardo, consiglio la lettura di questo articolo di Stephen Walther: ASP.NET MVC Tip #39 – Use the Velocity Distributed Cache.


Compressione HTTP

In conformità alla specifica HTTP/1.1, i web server ed i browser moderni supportano l’elaborazione di contenuti compressi secondo standard di compressione come il GZIP ed il (migliore) DEFLATE al fine di ridurre sensibilmente la banda occupata dal traffico HTTP. Lato server, la soluzione più scalabile per comprimere contenuti statici e dinamici prevede lo sfruttamento delle feature del Web Server piuttosto che della web application, ovviamente a fronte di ragionevoli valutazioni sul carico computazionale che il server deve sostenere soprattutto per gestire la compressione di contenuti dinamici. Nelle versioni di IIS precedenti alla 6.0, la funzionalità di compressione HTTP non è built-in e richiede comunque l’utilizzo di strumenti di terze parti (come XCompress).

In IIS6.0, invece, abbiamo l’introduzione di features di compressione statica (con cache su disco) e dinamica (senza cache su disco) che richiedono una modifica al metabase (%WINDIR%\system32\inetsrv\metabase.xml), al fine di abilitare gli schemi di compressione gzip e deflate su contenuti identificabili solamente in base al tipo di estensione (es. .aspx, .html etc). Per questo motivo, le applicazioni ASP.NET MVC deployate su IIS6 vanno incontro ad una serie di problematiche legate al fatto che le URL senza estensione forniscono un bel 404, ed anche se si implementa un URL rewrite o un “wildcard” mapping la compressione built-in di contenuti dinamici di IIS6.0 non ha più effetto poiché non sussiste alcun match con le estensioni specificate nel metabase. Quindi, se vogliamo avere la compressione sotto controllo, dobbiamo ricorrere ad un HttpModule, come viene spiegato molto bene in questo post.

Il problema viene risolto alla radice a partire da IIS7.0 grazie alla nuova sezione di configurazione <httpCompression>, che va a sostituire le precedenti configurazioni del metabase di IIS6.0 e ci permette di abilitare la compressione in base ai tipi MIME a livello di server, web site o web application attraverso diverse strade: IIS Manager, la command line di Appcmd.exe, il web.config e le API managed. Un grande vantaggio di IIS7+ inoltre è la capacità automatica di interrompere/riprendere la compressione a seconda che il carico di CPU superi/scenda sotto una soglia configurabile (vedi attributi dynamicCompressionDisableCpuUsage e dynamicCompressionEnableCpuUsage).

Nel caso in cui non fosse disponibile la possibilità di accedere alla configurazione del web server, siamo costretti a prendere la strada applicativa. Analogamente al classico mondo ASP.NET, anche in ASP.NET MVC la gestione della compressione dei contenuti può essere ottenuta tramite un HttpModule (ad esempio HttpCompress) in grado di applicare la compressione all’output stream della risposta HTTP. In genere, questa è una soluzione semplice e riusabile (forse meno performante) che permette di non intaccare almeno nella teoria una web application preesitente.

Un’ulteriore possibilità totalmente programmatica per ASP.NET MVC è la realizzazione di un filtro custom che a livello di controller o di singola action vada ad intercettare la risposta HTTP ed applicare al volo la compressione (come farebbe un HttpModule) sfruttando le classi del framework GZipStream e DeflateStream. Ecco un esempio di action filter:

public class CompressFilterAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
       HttpRequestBase request = filterContext.HttpContext.Request;
       string acceptEncoding = request.Headers["Accept-Encoding"];
            
       if (string.IsNullOrEmpty(acceptEncoding)) return;
 
       acceptEncoding = acceptEncoding.ToLowerInvariant();

       HttpResponseBase response = filterContext.HttpContext.Response;
 
       if (acceptEncoding.Contains("deflate")) // Priority to DEFLATE compression schema
       {
         response.AppendHeader("Content-Encoding", "deflate");
         response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
       }
       else if (acceptEncoding.Contains("gzip"))
       {
          response.AppendHeader("Content-Encoding", "gzip");
          response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
       }
    }
}

 

Minimizzazione e combinazione di risorse JavaScript e CSS

Un’altra importante tip per ottimizzare il traffico HTTP tra client e server è data dalla minimizzazione e la combinazione delle risorse esterne ad una pagina Web, in modo da ridurre massicciamente il numero complessivo di richieste HTTP generate dal browser. Esiste un progetto molto interessante su CodePlex che affronta questa problematica sia per applicazioni ASP.NET Web Forms che per applicazioni ASP.NET MVC. Si tratta di Combres, una libreria .NET che permette di organizzare JavaScript e CSS in diversi insiemi, ad ognuno dei quali viene associata una sezione di configurazione nel web.config. Le risorse specificate in ciascun insieme vengono minimizzate, combinate, compresse e messe in cache in modo da poter essere trasmesse in un singolo round-trip HTTP. Per informazioni dettagliate sull’utilizzo di questa libreria rimando al completo articolo su CodeProject.


Minimizzazione del markup HTML

Anche la rimozione degli spazi bianchi che si frappongono tra i tag all’interno di un documento HTML può ridurre il tempo di caricamento di una pagina Web, poiché ne riduce le dimensioni nonché ne favorisce il parsing da parte del browser. Anche questa funzionalità può essere ottenuta tramite un modulo HTTP, come questo di Mads Kristensen. In ASP.NET MVC inoltre si può pensare di realizzare un action filter. Molto semplicemente:

public class HtmlWhitespaceFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
HttpResponseBase response = filterContext.HttpContext.Response;                
string contentType = response.ContentType.Trim().ToLowerInvariant();                
if (contentType == "text/html" || contentType == "application/xhtml+xml" ||
contentType == "text/xml" || contentType == "application/xml")
{               
response.Filter = new HtmlWhitespaceFilterStream(response.Filter);
}          
}
}

 

Riporto per completezza l’implementazione della classe HtmlWhitespaceFilterStream, che internamente utilizza delle banali regular expression per eliminare gli spazi vuoti tra i vari tag del documento (X)HTML.

public class HtmlWhitespaceFilterStream : Stream
{
  private Stream _stream;
                              
  public override bool CanRead { get { return true; } }
  public override bool CanSeek { get { return true; } }
  public override bool CanWrite { get { return true; } }
  public override void Flush() { _stream.Flush(); }
  public override long Length { get { return _stream.Length; } } 
  public override long Position
  {
    get { return _stream.Position; }
    set { _stream.Position = value; }
  }
 
  public HtmlWhitespaceFilterStream(Stream stream) { _stream = stream; }
 
  public override int Read(byte[] buffer, int offset, int count)
{ return _stream.Read(buffer, offset, count); }
  public override long Seek(long offset, SeekOrigin origin) { return _stream.Seek(offset, origin); }
  public override void SetLength(long value) { _stream.SetLength(value); }
  public override void Close() { _stream.Close(); }
 
 
public override void Write(byte[] buffer, int offset, int count)
  {           
    string html = System.Text.Encoding.UTF8.GetString(buffer);
   
html = Regex.Replace(html, @"\s+<", "<", RegexOptions.Singleline);
    html = Regex.Replace(html, @">\s+", ">", RegexOptions.Singleline);

    byte[] outdata = System.Text.Encoding.UTF8.GetBytes(html);
    _stream.Write(outdata, 0, outdata.Length);
  }
}

           

Ottimizzazione della generazione degli URL

Un aspetto che può intaccare le performance di applicazioni ASP.NET MVC stressate da migliaia richieste al minuto è il modo in cui vengono generati gli URL virtuali nelle View tramite gli helper Html.ActionLink(), Html.RouteLink(), Url.Action() e Url.RouteUrl(). In questo post di Chad Moran viene mostrato in dettaglio come un performance test sulla generazione massiva di URL ci dovrebbe spingere verso la rinuncia delle comodità che ci vengono offerte dalle lambda expression e dagli anonymous object.
Sostanzialmente, per beneficiare di performance migliori, dovremmo preferire una sintassi di questo tipo…

Html.ActionLink("Link", "Index", "Home", new RouteValueDictionary { { "name", "Mario" }, { "age", 56 } })


…ad una di questo tipo…

Html.ActionLink("Link", "Index", "Home", new { name = "Mario", age = 56 })


…o ancora peggio (per le performance) di questo tipo …

Html.ActionLink<HomeController>(c => c.Index("Mario",56), "Link")

Come è facile intuire, i tempi di compilazione delle lambda expression e di reflection sugli anonymous objects introducono a run-time delle latenze che rallentano sensibilmente la generazione delle URL (fino ad un ordine di grandezza!) rispetto all’esecuzione su creazioni dirette di RouteValueDictionary. Contrariamente, dal punto di vista dello sviluppo, proprio l’ultimo metodo è preferibile per il type checking a compile-time.


Note di configurazione

In quest’ultima parte del post vorrei semplicemente ricordare delle semplici accortezze che però sono fondamentali per rendere più performante un’ applicazione ASP.NET.

  • Impostare <compilation debug="false" /> nel web.config della nostra applicazione nell’ambiente di produzione, in modo tale che il codice venga eseguito più velocemente.
  • Rimuovere i moduli HTTP che non sono utilizzati. Nel machine.config o nel root-level web.config del framework troviamo una configurazione di default che specifica il caricamento di diversi moduli che invece potremmo non usare nella nostra applicazione. Nel web.config possiamo quindi esplicitare l’esclusione dei moduli di cui non abbiamo bisogno dalla pipeline di ASP.NET (sia classica che integrata), facendo attenzione a non rompere le dipendenze tra i vari moduli.

    (Pipeline classica)

    <httpModules>
          <remove name="Session"/>
          <
    remove name="WindowsAuthentication"
    />
          <remove name="PassportAuthentication"/>
          ...     
               
    </
    httpModules>

    (Per la pipeline integrata il discorso è analogo. Basta posizionarsi nella sezione system.webServer\modules).

HTH

Tag di Technorati: ,,

posted @ martedì 11 gennaio 2011 20.31 | Feedback (3) | Filed Under [ ASP.NET MVC ]

lunedì 15 novembre 2010

[WPF 4.0] Esportare dati dal controllo DataGrid

Il controllo DataGrid di WPF 4.0 permette di ottenere “gratis” l’esportazione dei dati associati alla griglia in diversi formati tra cui plain text, CSV e HTML. Tale funzionalità è esposta semplicemente tramite l’invocazione del comando Copy sul controllo, che trasferisce nella Clipboard i dati contenuti nelle celle selezionate, supportando i formati sopra citati (ovviamente i formati CSV e HTML sono i più interessanti nell’ottica dell’esportazione verso altre applicazioni). Nel seguente esempio viene mostrato come generare un file CSV contenente tutte le righe visualizzate in un DataGrid, includendo le intestazioni di colonna sulla prima riga.Il codice è veramente banale:

myDataGrid.SelectAllCells();
myDataGrid.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
ApplicationCommands.Copy.Execute(null, myDataGrid);
myDataGrid.UnselectAllCells();
string csvData = (string)Clipboard.GetData(DataFormats.CommaSeparatedValue);
Clipboard.Clear();
using (StreamWriter writer = new StreamWriter("C:\\test.csv")) { writer.Write(csvData); }

Se invece si sceglie l’HTML come formato di esportazione, ci dobbiamo attendere un simpatico header aggiuntivo collocato fuori dal DOM che indica dove iniziano e dove finiscono le differenti porzioni di dati. Si possono utilizzare tali informazioni per esportare sia una pagina web intera piuttosto che soltanto il frammento relativo alla tabella dati da inserire eventualmente in un’altro documento. In questo caso l’esempio precedente va semplicemente modificato specificando DataFormats.Html come formato di estrazione dati dalla clipboard. Il risultato che otteniamo è il seguente:

Version:1.0
StartHTML:00000097
EndHTML:00000672
StartFragment:00000133
EndFragment:00000636
<HTML>
<BODY>
<!--StartFragment-->
<TABLE>...Dati...</TABLE>
<!--EndFragment-->
</BODY>
</HTML>

Tag di Technorati: ,

posted @ lunedì 15 novembre 2010 23.40 | Feedback (0) | Filed Under [ WPF ]

venerdì 22 ottobre 2010

Dictionary persistenti

In un mio progetto avevo bisogno di rendere persistenti dei Dictionary al fine di recuperarne lo stato al riavvio dell’applicazione. Poiché avevo bisogno di gestire un livello di astrazione rispetto allo storage utilizzato, ho anzitutto definito una classe astratta PersistentDictionary<TKey,TValue> derivando Dictionary<TKey,TValue> nel seguente modo:

public abstract class PersistentDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{  
    public abstract void Load();
   
public abstract void Save();   

}


Come è facile intuire, i metodi Load() e Save() servono per leggere/scrivere i dati del dictionary dallo/sullo storage.
Nella mia soluzione avevo la necessità di persistere su file system le coppie chiave/valore supportando diversi formati. Ho quindi creato un altro livello di astrazione per rappresentare i dictionary persistenti su file system.

public abstract class FileDictionary<TKey, TValue> : PersistentDictionary<TKey, TValue>

{

  public string FileName { get; private set; }

 

   public FileDictionary(string fileName)

  {

    if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("filename can't be null or empty");

    FileName = fileName;       

  }

}


Questo tipo di approccio mi ha permesso di lasciare la porta aperta a diverse personalizzazioni. Ad esempio, la seguente classe XmlFileDictionary<TKey,TValue> può essere utilizzata per serializzare/deserializzare in modo generico le coppie chiave/valore di qualunque tipo su un file XML sfruttando la classe XmlSerializer (scelta per rimanere retrocompatibile con il framework 2.0).

public class XmlFileDictionary<TKey, TValue> : FileDictionary<TKey, TValue>

{

  private XmlSerializer keySerializer;

  private XmlSerializer valueSerializer;

 

  public XmlFileDictionary(string fileName) : base(fileName)

  {

    keySerializer = new XmlSerializer(typeof(TKey));

    valueSerializer = new XmlSerializer(typeof(TValue));           

  }

 

  public override void Load()

  {

      if (File.Exists(this.FileName))

      {

        this.Clear();

 

         using (XmlReader reader = XmlReader.Create(this.FileName))

         {

           reader.ReadToFollowing("dictionary");

           if (!reader.IsEmptyElement)

           {

             reader.ReadToFollowing("item");

 

             while (reader.NodeType != XmlNodeType.EndElement)

             {

               reader.ReadStartElement("item");

 

               reader.ReadStartElement("key");

               TKey key = (TKey)keySerializer.Deserialize(reader);

               reader.ReadEndElement();

 

               reader.ReadStartElement("value");

               TValue value = (TValue)valueSerializer.Deserialize(reader);

               reader.ReadEndElement();

                           

               reader.ReadEndElement();

 

               this.Add(key, value);

             }                       

           }

         }

      }

  }

 

  public override void Save()

  {          

     using (XmlWriter writer = XmlWriter.Create(this.FileName))

     {               

        writer.WriteStartDocument();

        writer.WriteStartElement("dictionary", string.Empty);

 

        foreach (TKey key in this.Keys)

        {

           writer.WriteStartElement("item");

 

           writer.WriteStartElement("key");

           keySerializer.Serialize(writer, key);

           writer.WriteEndElement();

 

           writer.WriteStartElement("value");

           valueSerializer.Serialize(writer, this[key]);

           writer.WriteEndElement();

 

           writer.WriteEndElement();

        }

 

        writer.WriteEndElement();               

     }

  }

}


Nel mio progetto inoltre ho dovuto gestire vari manifest per memorizzare degli indici di cache, sempre su file system. In ciascun file, sia le chiavi che i valori sono sempre di tipo stringa ed ogni coppia è memorizzata su una linea terminata da CRLF. Ho potuto quindi riutilizzare l’astrazione FileDictionary<TKey, TValue> sopra citata per definire una classe concreta che rappresenta un manifest file.

public class ManifestFile : FileDictionary<string, string>

{

  private const char DEFAULT_SEPARATOR = '|';

 

  private char _separator;

  private Encoding _encoding;

 

  public ManifestFile(string fileName) : this(fileName, DEFAULT_SEPARATOR, Encoding.Default) { }

  public ManifestFile(string fileName, char separator) : this(fileName, separator, Encoding.Default) { }

  public ManifestFile(string fileName, char separator, Encoding encoding) : base(fileName)

  {

    if (encoding == null) throw new ArgumentNullException("encoding can't be null");

    _separator = separator;

    _encoding = encoding;

  }

 

  public override void Load()

  {

    if (File.Exists(this.FileName))

    {

      this.Clear();

      string[] lines = File.ReadAllLines(this.FileName, _encoding);

      foreach (string line in lines)

      {

        string[] parts = line.Split(new char[] { _separator }, StringSplitOptions.RemoveEmptyEntries);

        this.Add(parts[0], parts[1]);

      }

    }

  }

 

  public override void Save()

  {

    if (this.Count > 0)

    {

      List<string> lines = new List<string>();

      foreach (string key in this.Keys) lines.Add(key + _separator + this[key]);

      File.WriteAllLines(this.FileName, lines.ToArray(), _encoding);

    }

    else File.WriteAllText(this.FileName, string.Empty, _encoding);

  }

}


Osservazioni e consigli sono ovviamente ben accetti!!! :)

Tag di Technorati: ,

posted @ venerdì 22 ottobre 2010 23.37 | Feedback (0) | Filed Under [ .NET ]

martedì 12 ottobre 2010

[ASP.NET MVC 2] Controller asincroni

Una feature molto importante del framework ASP.NET MVC ha a che vedere con la capacità di gestire operazioni asincrone a livello di controller. Di fatto il movente è lo stesso che si trova dietro le Asynchronous Pages in ASP.NET 2.0, ovvero evitare che si verifichino condizioni di thread starvation nella nostra Web Application per poi vedersi ritornare un simpatico status code 503 (Server too busy). Come sappiamo, quando una richiesta viene ricevuta dal Web Server, il processamento viene affidato ad un thread del threadpool dell’applicazione. In uno scenario sincrono, tale thread rimane in vita fintantoché non vengono effettuate tutte le operazioni previste.
Quindi, potremmo dire a grandi linee che la pipeline asincrona è preferibile in scenari in cui la logica prevede colli di bottiglia che si formano in corrispondenza di operazioni bloccanti, a causa di latenze non legate strettamente al tempo di CPU (es. chiamate ad un WS).
Partendo dal presupposto che una richiesta asincrona possiede lo stesso tempo di esecuzione di una richiesta sincrona, minimizzare il numero di thread in attesa di operazioni bloccanti è una pratica molto apprezzata dal Web Server quando una Web Application viene letteralmente bombardata da centinaia di richieste concorrenti.

Vediamo ora un esempio molto banale di controller asincorno, in cui l’action List è stata creata secondo il pattern asincrono per gestire chiamate “lunghe” ad un Web Service:

public class CustomersController : AsyncController

{

   [AsyncTimeout(10000)]

   public void ListAsync()

   {

     AsyncManager.OutstandingOperations.Increment();

     Task.Factory.StartNew(() =>

     {

       try { AsyncManager.Parameters["result"] = new MyServiceClient().GetCustomers(); }

       catch (Exception ex) { ... }

       finally { AsyncManager.OutstandingOperations.Decrement(); }

     );

   }

 

   public ActionResult ListCompleted(List<Customer> result)

   {

     return View("List", result);

   }

   ...

      

   protected override void OnException(ExceptionContext filterContext)

   {

     if (filterContext.Exception is TimeoutException)

     {

       filterContext.Result = RedirectToAction("TryAgainLater");

       filterContext.ExceptionHandled = true;

     }           

     base.OnException(filterContext);

   }   

}


Alcune importanti osservazioni:

  • Un controller asincrono deve ereditare la classe AsyncController. (Gli action methods sincroni sono comunque supportati)
  • Il nome del metodo del controller che scatena l’operazione asincrona deve essere seguito dal suffisso “Async”.
  • Il nome del metodo del controller che viene invocato dal framework quando l’operazione asincrona viene completata deve essere seguito dal suffisso “Completed
  • Sebbene il pattern asincrono consiste di due metodi, l’invocazione da browser segue la stessa route che si utilizzerebbe per le action sincrone ( …/Customers/List )
  • La proprietà AsyncManager.Parameters è un IDictionary<string,object> che permette di definire i parametri che vengono passati al metodo xxxCompleted, evitando allo sviluppatore l’onere di definire delle variabili a livello di classe che siano visibili da entrambi i metodi xxxAsync e xxxCompleted. Occorre inoltre porre attenzione al nome che si assegna alle key dei parametri. Essi devono infatti corrispondere a quelli dichiarati dal metodo xxxCompleted.
  • I metodi AsyncManager.OutstandingOperations.Increment(…) e AsyncManager.OutstandingOperations.Decrement() sono necessari rispettivamente per determinare il numero di operazioni asincrone che il controller deve ancora completare e che ha completato. Attenzione a non ometterli ed a gestirli in modo consistente!!!
  • L’attributo AsyncTimeout specificato a livello di metodo xxxAsync è utile per definire un timeout per le operazioni a lunga latenza (default: 45 secondi). Se lo si vuole eliminare si può utilizzare il filtro [NoAsyncTimeout] oppure l’equivalente [AsyncTimeout(Timeout.Infinite)]. Se invece lo si vuole gestire per fornire un feedback diverso rispetto al global exception handler di ASP.NET, è possibile svilupparsi un exception filter personalizzato oppure semplicemente fare l’override del metodo OnException(…) del controller, in modo da gestire l’eccezione in modo custom (come in questo caso).

Non è tutto oro ciò che luccica…

  • I controller asincroni sono significativamente più complessi rispetto a quelli sincroni, oltre ad essere difficilmente testabili.
  • Non ha alcun senso utilizzare controller asincroni se si vogliono semplicemente eseguire dei task paralleli legati alle prestazioni della CPU.
  • Occorre assicurarsi che il nostro Web Server sia opportunamente configurato per beneficiare della gestione asincrona delle richieste web: ad esempio, se usiamo IIS7 dobbiamo configurare correttamente il valore della proprietà MaxConcurrentRequestsPerCPU nella sezione <applicationPool> di <system.web>, impostandolo ad un valore sufficientemente alto.
Tag di Technorati: ,,

posted @ martedì 12 ottobre 2010 20.00 | Feedback (0) | Filed Under [ ASP.NET MVC ]

venerdì 10 settembre 2010

Riflessioni sulle basi culturali dei metodi agili

Non posso non condividere questo interessante post di Roberto Lupi circa le basi culturali che si trovano a fondamento dei metodi agili...
Buona lettura :)

posted @ venerdì 10 settembre 2010 14.15 | Feedback (1) |

martedì 7 settembre 2010

[WPF] TextBox e input filtering

Quando si realizza un’interfaccia grafica per l’inserimento dati, è sempre buona prassi prevedere una prima "validazione" dell'input utente. Una tecnica molto semplice per raggiungere tale obiettivo potrebbe basarsi sull’idea di filtrare l’input in modo da non permettere l’inserimento di caratteri non validi. Nel caso della TextBox, in particolare, si potrebbe pensare di estendere il controllo affinché il filtraggio avvenga a fronte di un matching con un generica espressione regolare… una RegexTextBox dunque :).

public class RegexTextBox : System.Windows.Controls.TextBox
{               
  public string Pattern { get; set; }

  public RegexTextBox()
  {
     CommandBinding pasteCommandBinding = new CommandBinding(ApplicationCommands.Paste);
     pasteCommandBinding.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
     {
       e.Handled = !IsValid(Clipboard.GetText());
     };
     this.CommandBindings.Add(pasteCommandBinding);
  }
       
  protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
  {                       
     string newText = Text + e.Text;
     e.Handled = !IsValid(newText);           
     base.OnPreviewTextInput(e);
  }

  private bool IsValid(string text)
  {
     if (string.IsNullOrEmpty(text)) return true;
     return new Regex(Pattern).IsMatch(text);
  }
}


Un esempio di utilizzo potrebbe essere il seguente (ammissione di soli numeri naturali):

<src:RegexTextBox Pattern="^[0-9]+$"></src:RegexTextBox>



Nel codice sopra riportato viene mostrata anche un’ accortezza che può risultare utile, ovvero il filtraggio dell’input in caso di operazioni di Cut/Copy and Paste. Tale controllo avviene agganciando la nostra RegexTextBox all’application command ApplicationCommands.Paste, in modo da poter validare il contenuto della clipboard.

Technorati Tags: ,

posted @ martedì 7 settembre 2010 21.13 | Feedback (0) | Filed Under [ WPF ]

martedì 24 agosto 2010

[WPF] Evidenziare testo in una RichTextBox

Una funzionalità fondamentale per text-editor/reader è l’evidenziazione programmatica di testo, magari utilizzando stili di formattazione diversi. In questo post vorrei mostrare una possibile implementazione di tale funzionalità utilizzando il controllo RichTextBox di WPF. Immaginando un approccio top-down, potremmo partire da un metodo HilightText(…), il cui compito sia proprio quello di evidenziare del testo all’interno di una RichTextBox.

public void HilightText(string pattern)
{             
  List<TextRange> ranges = GetMatchingTextRanges(pattern);
 
foreach (TextRange textRange in ranges) HilightTextRange(textRange, Brushes.Black, Brushes.Yellow);            
}

L’idea è quella di applicare un background giallo ed un foreground nero a tutti gli intervalli di testo (TextRange) che soddisfano un dato pattern-matching.

private void HilightTextRange(TextRange textRange, Brush foregroundBrush, Brush backgroundBrush)
{
 
if (foregroundBrush != null) textRange.ApplyPropertyValue(TextElement.ForegroundProperty, foregroundBrush);
 
if (backgroundBrush != null) textRange.ApplyPropertyValue(TextElement.BackgroundProperty, backgroundBrush);
}

Ora ci troviamo di fronte all’esigenza di determinare gli intervalli di testo coinvolti nel matching. Definiamo dunque un metodo GetMatchingTextRanges(…), il cui compito è di navigare l’intero documento della RichTextBox (un FlowDocument) analizzando esclusivamente gli elementi (inlines) di tipo Run, ovvero quelli che contengono testo.

private List<TextRange> GetMatchingTextRanges(string pattern)
{

   List<TextRange> ranges = new List<TextRange>();

 

   TextPointer textNavigator = richTextBox.Document.ContentStart;

   while (textNavigator.CompareTo(richTextBox.Document.ContentEnd) < 0)

   {

      TextPointerContext context = textNavigator.GetPointerContext(LogicalDirection.Backward);

      if (context == TextPointerContext.ElementStart && textNavigator.Parent is Run)

      {

        List<TextRange> runRanges = GetMatchingTextRanges((Run)textNavigator.Parent, pattern);

        if (runRanges.Count > 0) ranges.AddRange(runRanges);

      }

      textNavigator = textNavigator.GetNextContextPosition(LogicalDirection.Forward);

   }

 

   return ranges;

}

A questo punto, per ogni Run trovato, è possibile controllare se esistono porzioni di testo in esso contenuti che soddisfano il pattern di ricerca. In caso positivo, possiamo aggiungerle alla lista dei TextRange da evidenziare.

private List<TextRange> GetMatchingTextRanges(Run run, string pattern)

{            

   List<TextRange> ranges = new List<TextRange>();

   MatchCollection matches = Regex.Matches(run.Text, pattern);            

 

   foreach (Match match in matches)

   {

     TextPointer startPosition = GetContentTextPointer(run.ContentStart, match.Index - 1);

     TextPointer endPosition = GetContentTextPointer(run.ContentStart, match.Index + match.Length - 1);

     ranges.Add(new TextRange(startPosition, endPosition));

   }
   return ranges;

}

Come si stabiliscono gli estremi dei TextRange da evidenziare all’interno di ciascun Run? Anzitutto, tramite la classe Regex riusciamo a capire per ciascun match quali sono gli indici dei caratteri iniziali all’interno del testo del Run. Poi, ricordando che per calcolare l’offset corretto all’interno di ciascun Run non dobbiamo considerare i caratteri (un Run può contenere altri tipi di simboli) bensì i TextPointer che sono adiacenti al testo di interesse, definiamo il seguente metodo:

 

private TextPointer GetContentTextPointer(TextPointer contentStart, int offset)
{
  TextPointer result = contentStart;

  int i = 0;

  while (i < offset && result != null)

  {

    if (result.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text ||

        result.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.None) i++;

    if
(result.GetPositionAtOffset(1, LogicalDirection.Forward) == null) return result;

 

    result = result.GetPositionAtOffset(1, LogicalDirection.Forward);
  }
  return result;
}

Finalmente il gioco è fatto. Di seguito riportiamo un esempio di output ottenuto invocando HilightText(“labor”);



Technorati Tags: ,

posted @ martedì 24 agosto 2010 22.01 | Feedback (1) | Filed Under [ WPF ]

domenica 30 maggio 2010

[WPF] MVVM e Splash Screen

Durante lo sviluppo di applicazioni WPF di una certa “pesantezza”, può essere utile ricorrere a Splash Screen aggiornabili in tempo reale che notifichino all’utente lo stato di avanzamento del caricamento dei vari moduli, ad esempio tramite elementi di testo piuttosto che barre di progresso. A volte, infatti, può non essere sufficiente una semplice immagine impostabile tramite la Build Action "SplashScreen" di Visual Studio.
In questo post vorrei quindi mostrare una possibile implementazione di uno Splash Screen aggiornabile in tempo reale usando il pattern MVVM. 

Per semplificare, supponiamo di posizionare nel Main la pesantissima sequenza di caricamento della nostra applicazione:

public static class Program
{
 
[STAThread]
 
public static void Main(string[] args)
 
{
   
App app = new App();
   
app.InitializeComponent();  // Load resources...           

    MainWindow mainWindow = null;
   
using (SplashScreenViewModel splashScreenViewModel = SplashScreen.ShowSplashScreen())
   
{
     
splashScreenViewModel.StatusText = "Initializing..."
                 
     // Loading application modules...
      Thread.Sleep(3000); // Simulate delay... 

      mainWindow = new MainWindow();

       splashScreenViewModel.StatusText = "Initialized."
      Thread.Sleep(1000);
  
           

     app.Run(mainWindow);
 
}       
}


Al fine di evitare fastidiosi effetti di freezing della GUI mentre il thread principale sta caricando i vari pezzi della nostra applicazione, la window dello Splash Screen va fatta girare necessariamente su un thread separato. Al termine della sequenza di caricamento, il thread principale deve poter chiudere lo Splash Screen utilizzando il Dispatcher corretto.
Vediamo quindi il codice della view:

public partial class SplashScreen : Window
{
 
private static object splashScreenlockObj = new object();  

  public SplashScreen()
 
{
   
InitializeComponent(); 
    DataContext = new SplashScreenViewModel();
 
}

   public static SplashScreenViewModel ShowSplashScreen()
 
{
   
lock (splashScreenlockObj)
   
{
    
SplashScreenViewModel splashScreenViewModel = null
     ManualResetEvent resetEvent = new ManualResetEvent(false);

     Thread splashScreenThread = new Thread(() =>
    
{
      
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate() 
      
{
        
SplashScreen splashScreenWindow = new SplashScreen();
        
splashScreenViewModel = (SplashScreenViewModel)splashScreenWindow.DataContext;
        
splashScreenViewModel.Dispatcher = Dispatcher.CurrentDispatcher;
        
resetEvent.Set();
        
splashScreenWindow.Show(); 
      
});
      
Dispatcher.Run();

      });

              splashScreenThread.SetApartmentState(ApartmentState.STA);
     
splashScreenThread.IsBackground = true
     
splashScreenThread.Start();

      resetEvent.WaitOne(); // Wait for viewmodel initialization...

     
return splashScreenViewModel;
   
}
 
}
}

Il metodo statico ShowSplashScreen() visualizza lo splash screen e comunica al ViewModel quale Dispatcher utilizzare per la chiusura (InvokeShutdown()).

public class SplashScreenViewModel : ViewModelBase, IDisposable
{
 
private string _statusText = null;
 
public string StatusText
 
{
   
get { return _statusText; }
   
set
   
{
     
if (_statusText == value) return
      _statusText = value;
     
OnPropertyChanged("StatusText");
   
}
  
}

 
public Dispatcher Dispatcher { get; set; }

 
public void Dispose()
 
{
   
if (Dispatcher != null)
   
{
     
Dispatcher.InvokeShutdown();
     
Dispatcher = null;
   
}
 
}
}

 

Technorati Tag: ,

posted @ domenica 30 maggio 2010 21.36 | Feedback (5) | Filed Under [ WPF ]

giovedì 29 aprile 2010

[ASP.NET MVC 2] Localizzazione dell’attributo DisplayName

Per localizzare i metadati delle classi che compongono il model della nostra applicazione MVC 2, principalmente sono due gli aspetti da prendere in considerazione:

  • Localizzare i messaggi di validazione del model. In generale, la localizzazione dei messaggi di errore non è specifica di ASP.NET MVC, bensì è una caratteristica ereditata dai “Data Annotations” di ASP.NET Dynamic Data (namespace System.ComponentModel.DataAnnotations), e si implementa specificando i parametri ErrorMessageResourceName e ErrorMessageResourceType dei vari ValidationAttribute.
  • Localizzare la proprietà DisplayName ove disponibile.

Supponiamo di avere il seguente model:

public class CreateCustomerModel
{
 
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ValidationStrings))]
 
[DisplayName("Name")]
 
public string Name { get; set; }  

  [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ValidationStrings))] 
  [DataType(DataType.EmailAddress)] 
  [DisplayName("Email address")]
 
public string Email { get; set; } 

  [DataType(DataType.Url)]
 
[DisplayName("Web site")]
 
public string WebSiteUrl { get; set; }       
}


La proprietà DisplayName può essere coinvolta sia nella validazione dei messaggi che utilizzano dei parametri per la formattazione, ma anche e soprattutto nel rendering della view: 

<div class="editor-label">
 
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
 
<%: Html.TextBoxFor(model => model.Name) %>
 
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<div class="editor-label">
  
<%: Html.LabelFor(model => model.Email) %>
</div>
<div class="editor-field">
 
<%: Html.TextBoxFor(model => model.Email) %>
 
<%: Html.ValidationMessageFor(model => model.Email) %>
</div>
<div class="editor-label">
 
<%: Html.LabelFor(model => model.WebSiteUrl) %>
</div>
<div class="editor-field">
 
<%: Html.TextBoxFor(model => model.WebSiteUrl) %>
 
<%: Html.ValidationMessageFor(model => model.WebSiteUrl) %>
</div>

Ci troviamo in questa brutta situazione:

immagine1xf[2]


Purtroppo, a differenza dei ValidationAttribute, la proprietà DisplayName non supporta nativamente la localizzazione e quindi non abbiamo gratis la possibilità di specificare una chiave di un file di risorsa (es. ModelStrings.it.resx) dal quale leggere la stringa localizzata.
Possiamo però estendere l’attributo DisplayName in modo da abilitare tale funzionalità tramite reflection. 

 
public class LocalizedDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
 
private PropertyInfo _namePropertyInfo = null;
 
private Type _resourceType = null;

 
public LocalizedDisplayNameAttribute(string displayNameKey) : base(displayNameKey) { }

 
public Type ResourceType
 
{
   
get { return _resourceType; }
   
set
   
{
     
_resourceType = value; 
     
_namePropertyInfo = _resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public);
   
}
 
}

 
public override string DisplayName
 
{
   
get
   
{                
     
if (_namePropertyInfo == null) return base.DisplayName;               
     
return (string)_namePropertyInfo.GetValue(_namePropertyInfo.DeclaringType, null);
   
}
 
}
}

La classe CreateCustomerModel prima descritta diventa quindi…

public
class CreateCustomerModel
{
 
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ValidationStrings))]
 
[LocalizedDisplayName("Name", ResourceType = typeof(ModelStrings))]       
  
public string Name { get; set; }

 
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ValidationStrings))]
 
[DataType(DataType.EmailAddress)]
 
[LocalizedDisplayName("Email", ResourceType = typeof(ModelStrings))]  
  
public string Email { get; set; }

 
[DataType(DataType.Url)]
 
[LocalizedDisplayName("WebSite", ResourceType = typeof(ModelStrings))]  
  
public string WebSiteUrl { get; set; }       
}

…il che ci fa ottenere il risultato desiderato :D

immagine3qg[1] 

Technorati Tag: ,,

posted @ giovedì 29 aprile 2010 20.20 | Feedback (0) |

lunedì 29 marzo 2010

[WPF] Ereditare controlli da classi generiche

In applicazioni WPF di una certa complessità è molto facile trovarsi in situazioni in cui è utile sfruttare classi base per i controlli (es. UserControl e Window). In architetture MVVM, ad esempio, è possibile far ereditare i vari controlli da classi base generiche. Tuttavia, l’ implementazione dell’ereditarietà generica in WPF talvolta non è proprio immediata, soprattutto perché non sempre gli errori di build sono esplicativi. 
In questo semplicissimo esempio di MVVM viene mostrato come si può specificare la classe base generica di una Window direttamente nello XAML.

Partiamo dalla Window:

public partial class CustomersWindow : WindowBase<CustomersViewModel>
{       
 
public CustomersWindow() : base(new CustomersViewModel())
 
{           
   
InitializeComponent();                        
 
}
  ...
}

La classe base generica della Window potrebbe essere qualcosa del genere...

public class WindowBase<T> : Window where T : ViewModelBase
{
 
public T ViewModel { get; set; } 

  public WindowBase() { }

 
public WindowBase(T viewModel)
 
{
   
this.ViewModel = viewModel;
   
base.DataContext = viewModel;
 
}
}

Arriviamo quindi al punto. Lo XAML che permette di specificare l’ereditarietà da classi generiche diventa:

<src:WindowBase
    
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
    
xmlns:src="clr-namespace:MyApplication"
    
x:Class="MyApplication.CustomersWindow"
    
x:TypeArguments="src:CustomersViewModel" ...>   
 
...
</src:WindowBase>

Tramite x:TypeArguments possiamo specificare il tipo generico della classe base. 

ATTENZIONE: questa tecnica è applicabile solo per il nodo root del documento XAML.

Technorati Tag: ,,,

 

posted @ lunedì 29 marzo 2010 13.48 | Feedback (3) | Filed Under [ WPF ]

sabato 30 gennaio 2010

[Entity Framework] Gestione runtime dei metadati

Il comportamento di default del wizard dell’EF accorpa i metadati del modello come risorse del nostro assembly e inserisce una stringa di connessione nell’ app.config o nel web.config in modo da referenziare a runtime tali risorse:

<connectionStrings>
<add name="NorthwindEntities"
    
connectionString="metadata=res://*/NorthwindModel.csdl|res://*/NorthwindModel.ssdl|res://*/NorthwindModel.msl;..."
     providerName="System.Data.EntityClient" />
</connectionStrings>

Tuttavia, “embeddare” i metadati del nostro modello come risorsa dell’assembly può costituire un limite: infatti sorgerebbero dei problemi qualora volessimo

  • esporre i metadati alla modifica di agenti esterni
  • referenziare tramite connection string dei metadati definiti altrove sul file system
  • caricare i metadati on-demand magari leggendoli da stream di rete

Infatti, esistono dei casi in cui può essere utile distinguere tra metadati di test e metadati di produzione o comunque prevedere delle politiche che possano richiedere storage model (SSDL) e mapping model (MSL) diversi, pur mantenendo l’integrità del modello concettuale (CSDL) utilizzato dallo sviluppatore.

Una prassi molto comune è impostare l’opzione 'Metadata Artifact Processing' del designer a 'Copy to Output Directory'. 

immaginejz[1]

In questo modo nella bin del nostro progetto troviamo i metadati SSDL, MSL e CSDL separati fisicamente dalla .dll di output.
Quindi abbiamo già raggiunto l’obiettivo di poter modificare i metadati (solitamente SSDL e MSL) senza dover ricompilare la nostra applicazione. A questo punto possiamo scegliere di modificare la nostra connection string in modo che punti al corretto insieme di file:

string connectionString = @"metadata=.\NorthwindModel.csdl|.\NorthwindModel.ssdl|.\NorthwindModel.msl;
                           
provider=System.Data.SqlClient; provider connection string=""..."" ";

using (NorthwindContext context = new NorthwindContext(connectionString)) { ... }


Uno step ulteriore che si può prevedere è il caricamento dinamico di metadati non soltanto da una locazione fisica su file system, ma anche da un generico stream (es. da un web server via HTTP). A riguardo, il seguente esempio mostra come caricare dinamicamente CSDL, SSDL e MSL in modo da creare un oggetto di tipo EntityConnection da passare al nostro ObjectContext:


// Get CSDL/SSDL/MSL from a generic stream ...
string csdl = "<?xml version="1.0" encoding="utf-8"?><Schema...</Schema>";
string ssdl = "<?xml version="1.0" encoding="utf-8"?><Schema...</Schema>";
string msl = "<?xml version="1.0" encoding="utf-8"?><Mapping...</Mapping>";

XmlTextReader csdlXmlReader = new XmlTextReader(new StringReader(csdl));
XmlTextReader ssdlXmlReader = new XmlTextReader(new StringReader(ssdl));
XmlTextReader mslXmlReader = new XmlTextReader(new StringReader(msl));
EdmItemCollection edmItemCollection = new EdmItemCollection(new[] { csdlXmlReader });
StoreItemCollection storeItemCollection = new StoreItemCollection(new[] { ssdlXmlReader });
StorageMappingItemCollection storageMappingItemCollection = new StorageMappingItemCollection(edmItemCollection, storeItemCollection, new[] { mslXmlReader });


// Create the metadata workspace for the EntityConnection ...
MetadataWorkspace metadataWorkspace = new MetadataWorkspace();
metadataWorkspace.RegisterItemCollection(edmItemCollection);
metadataWorkspace.RegisterItemCollection(storeItemCollection);
metadataWorkspace.RegisterItemCollection(storageMappingItemCollection);

string sqlConnectionString = @"Data Source=.\SQLSRV08;Initial Catalog=Northwind;User ID=XXX;Password=XXX;";

EntityConnection entityConnection = new EntityConnection(metadataWorkspace,new SqlConnection(sqlConnectionString));

using (NorthwindContext context = new NorthwindContext(entityConnection)) { ... }


Technorati Tag: ,,

posted @ sabato 30 gennaio 2010 16.49 | Feedback (2) | Filed Under [ Entity Framework ]

Powered by: