Fare il mapping di un Dictionary nella configurazione xml di Castle Windsor

Ecco come configurare in Castle Windsor un parametro di tipo Dictionary dove il valore associato alla chiave è un oggetto e non una semplice stringa. Nell'esempio seguente la classe PizzaShop riceve nel costruttore un parametro builders di tipo dictionary avente come chiave il tipo di pizza e come valore l'istanza della classe specializzata nella preparazione di quel tipo di pizza.


 
    
       
        
           ${margherita-pizzabuilder}
           ${veggie-pizzabuilder}
        
       
    
 
 
 
 
 

Ed ecco come è fatta nello specifico la classe PizzaShop dell'esempio:

public class PizzaShop : IShop
{
   private readonly Dictionary _builders;

   public PizzaShop(Dictionary builders)
   {
      this._builders = builders;
   }
}

Impedire l'override di una funzione d'accesso per una property virtual in C#

In C# quando definiamo per una classe base una property virtual possiamo nelle classi derivate eseguire l'override dei metodi d'accesso get e set.

E se non volessimo dare la possibilità alle classi derivate di eseguire l'override di entrambi i metodi? In questo caso è sufficiente dichiarare uno dei due metodi private come nell'esempio seguente:

public abstract class ClassA
{
   public virtual bool PropertyC 
   {
      get;
      private set; // non virtual
   }
}

public class ClassB : ClassA
{
   public override bool PropertyC
   {
      get
      {
         return false;
      }
   }
}

Ajax TreeView con Yahoo! User Interface Library via JSON

E' incredibile la facilità con cui è possibile soddisfare funzionalità applicative anche complesse utilizzando opportunamente le API della libreria Yahoo! User Interface. Ecco ad esempio come possiamo ottenere un controllo TreeView in grado di caricare dinamicamente i nodi tramite Ajax:

YAHOO.widget.AjaxTreeView = function(id, nodes, path, action) {
  if (id) { this.init(id); }
  if (nodes) {
    this.path = path;
    this.action = action;
    if (path && path.length > 0) {
      this.selectedValue = path[path.length - 1];
    }
  this.buildTreeDynamically(nodes);
  }
  else if (YAHOO.lang.trim(this._el.innerHTML)) {
    this.buildTreeFromMarkup(id);
  }
};

Al costruttore passiamo l'id dell'elemento contenitore (tipicamente un div), la lista dei nodi "radice" in formato JSON, il percorso dei nodi da aprire per arrivare al nodo corrente e l'action a cui far puntare il link dell'etichetta di un nodo.
Nel pezzo di codice seguente vediamo come estendere l'oggetto TreeView di Yahoo! per aggiungere il caricamento dinamico dei nodi:

YAHOO.extend(YAHOO.widget.AjaxTreeView, YAHOO.widget.TreeView, {
  path: null,
  action: null,
  selectedValue: null,

  buildTreeDynamically: function(nodes) {
    var roots = [];
    roots = YAHOO.lang.JSON.parse(nodes);
    this.root.setDynamicLoad(loadNodeData);
    for (var i = 0; i < roots.length; i++) {
      var oData = roots[i];
      var aNode = new YAHOO.widget.DynamicNode(oData, this.root);
      if (aNode.isActive) {
        if (this.selectedValue &&
          aNode.contentElId == eval(this.selectedValue)) {
          aNode.labelStyle = 'ygtvlabel-selected';
          aNode.href = null;
        }
        if (this.path && this.path.length > 0) {
          for (var level = 0; level < this.path.length; level++) {
            if (this.path[level] == aNode.contentElId) {
              aNode.expand();
            }
          }
        }
      }
      else {
        aNode.isLeaf = true;
        aNode.href = null;
      }
    }
    this.draw();
  },

  getNodeById: function(id) {
    var node = null;
    for (var i in this._nodes) {
      var n = this._nodes[i];
      if (n.contentElId == id) {
        node = n;
        break;
      }
    }
    return node;
  }
});

Il metodo buildTreeDynamically riceve i nodi radice dal costruttore e li salva in un array locale tramite la funzione parse dell'API JSON (riga 08). Poi imposta la funzione di caricamento dinamico dei nodi (vedi snippet seguente). Successivamente per ogni elemento dell'array controlla se il nodo è attivo (riga 13), se non lo è viene definito come foglia ed impostato il link a null (righe 27-30). Se il nodo è attivo controlla se è quello corrente o se fa parte del percorso per arrivare al nodo corrente, in quest'ultimo caso lo espande (riga 22).

function loadNodeData(node, fnLoadComplete) {
 var path = node.tree.path;
 var selectedValue = node.tree.selectedValue;

 var callback = {
  success: function(oResponse) {
    var childrenNodes = [];
    var;
    try {
      if (oResponse.responseText != "") {
        childrenNodes = YAHOO.lang.JSON.parse(oResponse.responseText);
      }
      else {
        node.isLeaf = true;
      }
    }
    catch (x) {
      alert("Server non raggiungibile.");
      return;
    }
    for (var k = 0; k < childrenNodes.length; k++) {
      var oData = childrenNodes[k];
      var aNode = new YAHOO.widget.DynamicNode(oData, node);
      if (path && path != "") {
        for (var level = 0; level < path.length; level++) {
          if (path[level] == aNode.contentElId) {
            aNode.expand();
          }
        }
      }
      if (selectedValue && aNode.contentElId == eval(selectedValue)) {
        aNode.labelStyle = 'ygtvlabel-selected';
        aNode.href = null;
        selected = true;
      }
    }
    oResponse.argument.fnLoadComplete();
  },

  failure: function(oResponse) {
    oResponse.argument.fnLoadComplete();
  },

  argument: {
    "node": node,
    "fnLoadComplete": fnLoadComplete
  },

  timeout: 8000
 };
 if (node.isActive) {
  YAHOO.util.Connect.asyncRequest('GET', 
            node.tree.action + node.contentElId, callback);
 }
};

La funzione loadNodeData viene chiamata all'espansione di un nodo del TreeView e corrisponde ad una GET (righe 52-53) verso un servizio applicativo che mette nella risposta HTTP la collezione di nodi da caricare in formato JSON. Questi nodi vengono poi interpretati nella funzione di callback in modo analogo a come visto per i nodi radice.

A completamento dell'esempio qui di seguito vediamo il codice che definisce la struttura del DynamicNode ovvero una specializzazione dell'oggetto Node di Yahoo! al quale sono state aggiunte informazioni specifiche come lo stato di inattività.

YAHOO.widget.DynamicNode = function(oData, oParent) {
  if (oData) {
    if (YAHOO.lang.isString(oData)) {
      oData = { label: oData };
    }
    else {
      this.label = oData.Label;
      this.href = oData.Href;
      this.isLeaf = oData.IsLeaf;
      this.isActive = oData.IsActive;
    }
    this.init(oData, oParent, false);
    this.nowrap = true;
    this.contentElId = oData.Id;
    this.setUpLabel(oData);
  }
};

YAHOO.extend(YAHOO.widget.DynamicNode, YAHOO.widget.Node, {

  labelStyle: "ygtvlabel",
  labelElId: null,
  label: null,
  href: null,
  target: '_self',
  isActive: true,

  setUpLabel: function(oData) {
    if (YAHOO.lang.isString(oData)) {
      oData = {
        label: oData
      };
    }
    this.label = oData.Label;
    this.labelElId = "ygtvlabelel" + this.index;
  },

  /**
  * Ritorna la rappresentazione in HTML del nodo eseguendo 
  * un override del metodo della classe base Node per 
  * aggiungere le funzionalità  di attivo/disattivo
  * e la presenza degli attributi target, href e title.
  * @method getContentHtml
  * @return {string} rappresentazione in HTML del nodo
  */
  getContentHtml: function() {
    var sb = [];
    sb[sb.length] = (this.isActive && this.href) ? '' : '';
    return sb.join("");
  }
});

In conclusione questo esempio dimostra ancora una volta come sia semplice estendere le API di Yahoo! integrandone le funzionalità a vantaggio dell'interfaccia utente.


Lucene.Net: indicizzare nullable date con un FieldBridge custom

L'altro giorno mi è capitato di dover effettuare con Lucene delle ricerche per un intervallo di date (data inizio e data fine). Siccome le date in questione potevano essere nulle si poneva il problema di come indicizzarle perchè fossero confrontabili con i valori forniti dalla ricerca (esempio minore di e maggiore di...).

Non potendo rinunciare al valore nullo sul database ho pensato di intervenire al momento dell'indicizzazione dell'entità andando a sostituire il valore nullo con un valore di default che fosse uguale al valore minimo di DateTime per la data di inizio e al valore massimo di DateTime per la data di fine.

Avvalendomi dell'aiuto di NHibernate.Search ho realizzato un FieldBridge custom che potesse intervenire nel momento dell'indicizzazione della mia entità. Un FieldBridge non è altro che un "codec" da object a string che consente di indicizzare il contenuto di una proprietà (Object To String) e di recuperarne il valore originale dall'indice (String To Object). Ecco il codice:

using System;
using NHibernate.Search.Bridge;
using NHibernate.Search.Bridge.Builtin;
using System.Globalization;

public class AdvancedDateBridge : 
   ITwoWayStringBridge, IStringBridge, IParameterizedBridge
{
  private string _dateTimeFormat;
  private string _defaultIfIsNull;

  public string ObjectToString(object obj)
  {
    if (obj == null)
    {
      return _defaultIfIsNull;
    }
    else
    {
      return Convert.ToDateTime(obj).ToString(_dateTimeFormat);
    }
  }

  public object StringToObject(string stringValue)
  {
    if (stringValue.Equals(_defaultIfIsNull))
    {
      return null;
    }
      return DateTime.ParseExact(stringValue, 
        _dateTimeFormat, CultureInfo.CurrentCulture);
  }

  public void SetParameterValues(object[] parameters)
  {
    Object format = parameters[0];
    if (format != null)
      this._dateTimeFormat = (string)format;
    Object defaultValue = parameters[1];
    if (defaultValue != null)
      this._defaultIfIsNull = (string)defaultValue;
  }
}

Per utilizzare questo FieldBridge è sufficiente decorare con un attributo la proprietà di tipo DateTime che si vuole indicizzare come nell'esempio di codice seguente:

[FieldBridge(typeof(AdvancedDateBridge), 
             new object[]{"dd/MM/yyyy",DateTime.MinValue})]
public Nullable StartDate { get; set; }

[FieldBridge(typeof(AdvancedDateBridge), 
             new object[]{"dd/MM/yyyy",DateTime.MaxValue})]
public Nullable EndDate { get; set; }

Il programmatore con solidi principi

Programmare per alcuni è un'arte, un esercizio di creatività che non può essere limitato da regole e schemi, una definizione però che non tiene conto degli obiettivi che un programmatore deve perseguire nello scrivere "buon codice", ovvero mantenibilità, estendibilità, scalabilità, stabilità, etc. Per raggiungere questi obiettivi e non perdere la bussola dobbiamo avere dei solidi principi:

  • S - Single Responsibility Principle
  • O - Open/Closed Principle
  • L - Liskov Substitution Principle
  • I - Interface Segregation Principle
  • D - Dependency Inversion Principle

Ecco i principi che aiutano il programmatore ad ottenere una "buona" programmazione ad oggetti:

Single Responsibility Principle (SRP)

Ogni classe deve essere disegnata per svolgere bene un solo compito, avere una sola responsabilità, in pratica una classe deve avere un solo motivo per cambiare. Seguire questo principio porta automaticamente ad avere un basso accoppiamento.

Open/Closed Principle (OCP)

Una classe deve essere aperta alle estensioni, ma chiusa alle modifiche. Dobbiamo quindi essere in grado di estendere il comportamento di una classe senza modificarne l'implementazione (aka codice sorgente). Come? Facendo uso di classi astratte o interfacce, ovvero sfruttando il polimorfismo.

Liskov Substitution Principle (LSP)

Una funzione che utilizza un riferimento ad una classe base deve poter utilizzare al suo posto una qualsiasi delle classi derivate senza conoscerne l'implementazione. In pratica non dobbiamo modificare in alcun modo il comportamento di una classe base in una derivata. Una via potrebbe essere preferire dove possibile la composizione all'ereditarietà, ponendosi ogni volta la fatidica domanda IS A or HAS A?

Interface Segregation Principle (ISP)

Una classe non deve implementare una interfaccia che non usa o che usa parzialmente. Molte volte si deve preferire più interfacce con una sola funzionalità ad un'unica interfaccia che fa tutto. Il motivo? Una classe è influenzata dal cambiamento di una interfaccia anche se non la usa.  

Dependency Inversion Principle (DIP)

Si basa sul concetto che una classe di un alto livello non deve dipendere dall'implementazione di classi o entità di un livello inferiore. In pratica per raggiungere tale scopo devo progettare le classi pensando alle interfacce e non alle implementazioni. Questo porta ad avere non solo un basso accoppiamento tra i livelli della mia architettura, ma automaticamente a poter sostituire l'implementazione di un livello inferiore senza pregiudicare il funzionamento di quelli superiori.

 

In conclusione questi principi aiutano il programmatore, laddove non arriva l'esperienza, ad ottenere un codice di qualità che mantenga una forte coesione ed un basso accoppiamento tra le entità del sistema. Non devono però diventare un dogma, ma uno strumento da conoscere e da utilizzare a seconda degli obiettivi e dei requisiti applicativi.

Link utili


Autosave del contenuto di una textarea in un cookie con Yahoo! User Interface Library

Può essere utile avere a disposizione nelle applicazioni web un sistema di salvataggio automatico del testo, vediamo come ottenerlo con l'aiuto della Yahoo! User Interface Library.

Utilizziamo per l'occasione il componente Rich Text Editor e l'utility di gestione dei cookie. Con lo snippet seguente dichiariamo l'editor e poi ne salviamo il testo ad intervalli regolari in un cookie:

var editor1 = new YAHOO.widget.Editor('textarea1', {
   toolbar: {
   titlebar: 'Testo:',
   collapse: true,
   buttons: [
    { group: 'style',
    buttons: [
      { type: 'push', label: 'Grassetto', value: 'bold' },
      { type: 'push', label: 'Corsivo', value: 'italic' },
      { type: 'push', label: 'Sottolineato', value: 'underline' },
      { type: 'push', label: 'Ripristina', value: 'recovery' }]
    }
   ]}
});
var started = false;
editor1.on('editorKeyDown', function(ev) {
   if(!started){
      setInterval("saveContentToCookie()",15000);
      started = true;
   }
  }, this, true);
function saveContentToCookie() {
   if(editor1)
   {
      var content = editor1.getEditorHTML();
      if(content.length > 5){ //comprendo anche il br
         YAHOO.util.Cookie.set("recovery", content, { 
           expires: new Date(new Date().getTime() + 2*24*60*60*1000) });
      }
   }
}

Analizziamo il codice passaggio per passaggio: prima creo una istanza del Rich Text Editor di Yahoo! (righe 01-14) poi all'evento KeyDown dell'editor associo un handler che faccia partire ad intervalli regolari la funzione che esegue il salvataggio del testo nel cookie (righe 15-21). Infine nella funzione di salvataggio (righe 22-31) prendo il testo contenuto nell'editor e lo salvo nel cookie attraverso il metodo Cookie.set dell'utility messa a disposizione dall'API Yahoo! YUI.

Per recuperare i dati dal cookie aggiungo un pulsante all'editor (vedi riga 11 snippet precedente) al click del quale visualizzo in un div i dati presenti nel cookie prelevati precedentemente attraverso il metodo Cookie.get.

Ma andiamo con ordine. Prima vediamo la struttura del div utilizzato per visualizzare il contenuto del cookie:

Contenuto salvato
       (Il testo viene salvato ogni 15 secondi)

Il div ha una struttura ben definita che usufruisce degli skin messi a disposizione dall'API di Yahoo! e nel suo corpo accede al DOM per inserire nell'html il contenuto del cookie.

Successivamente da codice javascript istanzio su quel div un oggetto Panel inizialmente non visibile ed al click del pulsante dell'editor definito per l'occasione lo visualizzo attraverso il metodo show():

var cookieContent = new YAHOO.widget.Panel("cookieContent", 
    { xy:[300,220], visible:false, width:"500px" } );
cookieContent.render(); 

editor1.on('toolbarLoaded', function() {
   this.toolbar.on('buttonClick', function(o) {
   switch(o.button.value) {
      case 'recovery':
        onRecoveryClick(o, editor1);
          break;
      }
   });
 }, editor1, true);
var onRecoveryClick = function (e, obj) {
   cookieContent.show();
};

Per rendere del tutto user-friendly il recupero dei dati salvati ho aggiunto una funzione che chiamata da un pulsante all'interno del Panel consente di incollare il contenuto del cookie direttamente nell'area di testo dell'editor:

function pasteContentFromCookie() {
   if(editorRisposta){
      editorRisposta.setEditorHTML(YAHOO.util.Cookie.get("recovery"));
   }
   cookieContent.hide();
}

In conclusione aggiungo che per far funzionare il tutto è necessario linkare nell'head i file sorgenti della libreria Yahoo! YUI e precisamente:







E questo è davvero tutto!


DataGridView e TableAdapter: Update in-line e Performance Tip

Se dobbiamo sviluppare applicazioni Windows Form "quick and dirty" su basi di dati non troppo complesse, niente ereditarietà o self-relationship per intenderci, con il DataSet ed i TableAdapter possiamo soddisfare le nostre esigenze con un po' di drag and drop ed alcuni click del mouse.

Update in-line

Tutto bello se non fosse che per quello che non ci viene dato in automatico dobbiamo sudare le famose 7 camicie. Mi è capitato recentemente di dover applicare ad una DataGridView collegata ad un DataSet la modifica in-line, ovvero il salvataggio dei dati all'uscita dalla cella. Dopo innumerevoli tentativi sono giunto alla seguente conclusione:

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    this.dataGridView1.EndEdit();
    this.customersBindingSource.EndEdit();
    if (this.dataSet1.HasChanges(DataRowState.Modified))
    {
       this.customersTableAdapter.Update(this.dataSet1.Customers);
    }
}

In pratica nell'handler dell'evento CellValueChanged del DataGridView procedo alla chiamata dei metodi EndEdit rispettivamente sulla DataGridView e sul BindingSource collegato in modo da propagare la modifica alla sorgente dati bindata alla griglia. Successivamente controllo se ci sono modifiche (può essere omesso visto lo scopo dell'evento scatenato) ed a quel punto chiamo il metodo Update del TableAdapter che procede alla persistenza dei dati sullo storage.

Performance tip

Lavorando con un insieme maggiore di record il TableAdapter soffre di un po' di lentezza se gli passiamo nel metodo Update tutta la tabella. Per migliorare le performance possiamo utilizzare direttamente il DataAdapter, accessibile tramite la proprietà Adapter, al quale passiamo un dataset con le modifiche da persistere, ecco come fare:

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    this.dataGridView1.EndEdit();
    this.customersBindingSource.EndEdit();
    if (this.dataSet1.HasChanges(DataRowState.Modified))
    {
        DataSet ds = this.dataSet1.GetChanges(DataRowState.Modified);
        this.customersTableAdapter.Adapter.Update(ds);
    }
}

Quando si lavora con il DataSet devono essere gestite opportunamente concorrenza e caricamento dei dati in modo da evitare perdita di informazioni ed un utilizzo pesante delle risorse. Nel primo caso la gestione viene definita tramite opportuna configurazione del TableAdapter, nel secondo caso è compito nostro implementare dei meccanismi di paginazione dei dati, ma questa è un'altra storia...


Aprire un pdf in una determinata pagina

Può succedere a volte di voler linkare un argomento all'interno di un documento pdf. Se il documento ha molte pagine diventa difficile per l'utente trovare da solo le informazioni necessarie. Per questo motivo può essere utile aprire il documento nella pagina che contiene l'argomento interessato.
Per aprire un documento pdf in una determinata pagina dobbiamo aggiungere dopo il nome del file la stringa #page= seguita dal numero della pagina.
Ad esempio:

> HQL: The Hibernate Query Language

Con questo link siamo in grado di aprire la documentazione di NHibernate direttamente nel capitolo che parla del linguaggio HQL.


T-SQL: assegnarsi un ruolo con la stored procedure sp_addrolemember

La stored procedure sp_addrolemember viene utilizzata in SQLServer 2005 per aggiunge un utente del database, un ruolo del database, un account di accesso di Windows o un gruppo di Windows ad un ruolo di database per il database aperto nel contesto corrente.
Peccato che non esista un controllo per impedire al chiamante di eseguire la procedura su se stesso. Per evitare questo spiacevole inconveniente è necessario indicare a SQLServer di considerare attendibile il database in oggetto attraverso il seguente script sql:

ALTER DATABASE dbname SET TRUSTWORTHY ON

Quando il database è impostato su TRUSTWORTHY ON non è però possibile eseguire l'impersonate di un altro utente, se ne abbiamo la necessità dobbiamo prima impostare TRUSTWORTHY ad OFF come nell'esempio seguente:

EXECUTE AS LOGIN = 'login1';
GO
--Set TRUSTWORTHY OFF
ALTER DATABASE dbname SET TRUSTWORTHY OFF
GO
EXECUTE AS USER = 'user2';
--Execution context set to user2
GO
--Set TRUSTWORTHY ON
ALTER DATABASE dbname SET TRUSTWORTHY ON
--Reset the execution context to the previous context
REVERT;
GO

Nota: Per impostare questa opzione, è necessario essere un membro del ruolo sysadmin.


T-SQL: gestire permessi a livello di colonna

Nella gestione della sicurezza di un database può essere necessario in alcuni casi arrivare a definire dei permessi a livello di colonna.
Nell'esempio di codice Transact SQL sottostante vediamo come in SQL Server possiamo negare all'utente di tipo studente la modifica della colonna Matricola all'interno della tabella dbo.Studenti del database Universita:

USE Universita
GO
DENY UPDATE ( Matricola ) ON OBJECT::dbo.Studenti TO studente