Beren.it

All that you can leave behind

Dynamic Data Entities - Customizzare le Foreign-Key

Personalizzare la visualizzazione delle Foreign Key nei Dynamic Data Entity

Oggi vediamo come customizzare il controllo delle Foreign-Key visualizzato per default nei Dynamic Data Entities. Questo controllo visualizza i dati di una tabella terza rispetto quella che si sta editando e che viene linkata tramite una chiave esterna (Foreign Key appunto). Dato che di default viene semplemente riempito con un field dell'entità scelto in base alla PK della stessa, non sempre può essere immediato da utilizzare. Quello che faremo è visualizzare un composizione di più campi invece che uno singolo ed ordinare gli elemento rispetto ad un field specifico.

1. Visualizzazione di un campo composto: la dropdown usata nel controllo della FK (Foreign Key) può essere customizzata sovrascrivendo la funzione ToString() con un metodo custom come ade esempio illustrato qui sotto:

[C#]

    [MetadataType(typeof(TB_SEASONMetadata))]
    public partial class TB_SEASON
    {         
         public override string ToString()
         {
             return NAME + DESCRIPTION;
         }
    }

 

2. Ordinare i valori nella DropDown: per ordinare rispetto ad un campo gli oggetti all'interno della dropdown di FK occorre usare il decoratore DisplayColumn specificando il nome del field-colonna su cui effetturare l'ordinamento in questa maniera:

[C#]

    [MetadataType(typeof(TB_SEASONMetadata))]
    [DisplayColumn("NAME","NAME", false)]
    public partial class TB_SEASON
    {         
         public override string ToString()
         {
             return NAME + DESCRIPTION;
         }
    }

 

In effetti resta la perplessità sul fatto che il primo parametro di tale decoratore sia un field che a tutti gli effetti poi non è utilizzato: in fatti nell'esempio il field NAME è stato specificato essere la DisplayColumn quando in realtà verrà comunque visualizzato l'override del ToString()... In ogni caso il secondo parametro che è la sortcolumn funziona correttamente e noi otteniamo il risultato sperato.

Dynamic Data Entities - Customization Parte 1 - Name & Scaffolding

In un post di qualche giorno fa illustravo la facilità con cui realizzare una web application per il data entry attraverso l'utilizzo dei Dynamic Data Entities. Ora proseguo in questo argomento illustrando come procedere con la customizzazione di quanto automaticamente prodotto dal template di Visual Studio. Apriamo ad esempio la pagina principale dell'applicazione.

Effettivamente la web application ha l'elenco completo delle tabelle come si vede dall'immagine qui sopra. Però supponiamo che non tutte le tabelle siano da visualizzare. Per nascondere o visualizzare le tabelle bisogna agire sulla proprietà di ScaffolAllTables che abbiamo già visto essere settata all'interno del file Global.asax. Essa infatti se settata a true informa il framework che tutte le tabelle presenti nello schema edmx devono esssere visualizzabili.

[C#]

    DefaultModel.RegisterContext(typeof(FOFEntities), 
    new ContextConfiguration() { ScaffoldAllTables = false });

Settiamo dunque a False tale property, ma questo non basta infatti se lanciamo l'applicazione verrà sollevata un'eccezione. In effetti l'applicazione in questo modo non ha nessuna table da visualizzare, quindi bisogna trovare una maniera per informare il framework quale tabele specifiche mostrare dal momento che non le deve visualizzare tutto. In generale questo tipo di operazioni di customizzazione si applicano all'entity framework decorando le classi con attributi specifici. Ad esempio se volessimo indicare una tabella, o meglio la classe che la rappresenta nel file edmx, come visualizzabile dall'applicazione dovremmo usare l'attributo ScaffoldTable(true) e collocarlo appena prima della dichiarazione. Non è però nelle best practice questa operazione, inquanto dovrebbe andare a modificare un file autogenerato qual'è il Designer del edmx. Infatti se la struttura del database cambiasse per una qualche ragione e volessimo aggiornare il file edmx attraverso l'interfaccia del designer, tutte le modifiche apportate manualmente andrebbero perse...
Che fare dunque? Fortunatamente le Partial Classes di .NET ci vengono in aiuto e ci forniscono un efficiente workaround al problema consentendo di separare in un altro file tutte le personalizzazioni realizzate manualmente. Aggiungendo infatti un file cs al progetto. A questo punto prendiamo il nome della classe che si riferisce alla tabella che vogliamo visualizzare e ne scriviamo la definizione partial. In questa classe però non andremmo a specificare nient'altro ad esccezione di un attributo MetadataType che serve a linkare alla classe stessa una classe di metadata appunto.

[C#]

    [MetadataType(typeof(TB_SEASONMetadata))]
    public partial class TB_SEASON
    {

    }

    [ScaffoldTable(true)]
    [TableName("Stagione")]
    public class TB_SEASONMetadata
    {
        ...
    }

E' in questa classe di metadata che andremo a specificare tutte le customizzazioni che ci interessano. In particolare nel nostro caso sfrutteremo il già sopra citato attributo ScaffoldTable(true) che indica al framework che questa classe è gestibile dal sistema ed un altro attribute di nome TableName che serve a rinominare la tabella e nel nostro caso utilizzeremo per passare dal nome poco user-friendly "TB_SEASON" a "Stagione". Compiliamo, refreshamo la pagina. Ed otteniamo il risultato desiderato. Una sola tabella visualizzata nell'elenco e con un nome maggiormente esplicativo.

Classi Obsolete in C#

Durante il ciclo di vita di un software pò accadere, ed in effetti succede non di rado, che alcune classi vengano migliorate, ampliate o, in genere per motivi di compatibilità con il passato, affiancate da nuove versioni delle stesse. In questo caso, per dare evidenza allo sviluppatore di usare la nuova versione, è possibile in C# contrassegnare con l'attributo Obsolete la vecchia classe.

L'esempio di codice seguente illustra un semplice caso:

[C#]

[Obsolete("This class is obsolete; use class B instead")]
class A
{
   public void F() {}
}

class B
{
   public void F() {}
}

class Test
{
   static void Main() {
      A a = new A();       // Warning
      a.F();
   }
}

La classe A è marcata come obsoleta, questo significa che quando verrà instanziata il compilatore genererà un warning con il testo specificato nel tag obsolete che la decora.

Il costruttore dell'attributo oltre alla stringa che verrà visualizzata con il Warning offre anche un parametro booleano aggiuntivo che impone al compilatore di non effettuare l'escape sui caratteri speciali. In generale questo parametro dovrà essere tenuto a false (che è il default) a meno che non siano necessari comportamenti particolari, che andranno in ognicaso gestiti con molta attenzione.

Throw e Throw ex: le differenze

Nell'ambito della gestione delle eccezioni con il framework .NET è opportuno spendere due parole per capire come, in linea di massima, un'eccezione debba venire gestita e le modalità per farlo. Tutti sanno che per gestire un'eccezione durante l'esecuzione di un frammento di codice occorre inserirlo all'interno di un blocco try-catch. In questo modo al verificarsi un eccezione potremmo eseguire del codice dedicato in base anche alla tipologia dell'eccezione stessa. Calandoci però in un caso particolare voglio parlare di una parte specifica della gestione delle eccezioni definita "exception bubbling".

"Exception bubbling" è un'espressione abbastanza intraducibile che si riferisce alla possibilità di risollevare l'eccezione dopo aver eseguito le operazioni custom nel catch, per consentire di lavorare con l'eccezione anche esternamente al metodo in cui l'eccezione è avvenuta. Ed è proprio a questo proposito che qualora non si tenga in considerazione di alcune accortezze si potrebbe incappare in problematiche non di poco conto in fase di debugging.

Si prenda ad esempio in considerazione il frammento di codice qua sotto:

[C#]

try
{
    // esecuzione di una qualche operazione che fallisce
}
catch (Exception ex)
{
    // effettuare un cleanup
    throw ex;
}

Questo è certamente uno dei casi più comuni nella gestione di un eccezione: c'è un parte dedicata alla gestione dell'eccezione (detta cleanup) e quindi, al termine l'eccezione viene rilanciata.

E' un pò meno comune invece vedere qualcosa di simile:

[C#]

try
{
    // esecuzione di una qualche operazione che fallisce
}
catch (Exception ex)
{
    // effettuare un cleanup
    throw;
}

All'apparenza queste due gestioni sembrano molto simili in realtà nascondono una notevole differenza per quanto riguarda le informazioni dello Stack Trace. In pratica analizzando in debug le due situazioni si scopre che nel primo caso lo stack trace verrà troncato al di sotto del metodo in cui è raccolto il catch con il risultato che sembrerà che l'eccezione sia stata generata proprio dal vostro codice. Infatti in questo modo è come se screaste una vostra propria eccezione.

Utilizzare invece il semplice "throw" vi consentirà di preservare tutte le info dello stack. Tutto ciò è ancora più chiaro se si guardasse il codice IL generato che a fronte di un "throw" del primo caso userà un "rethrow" nel secondo.

Questo però non significa affatto che in generale il "throw ex" non debba mai venire utilizzato. Anzi, qualora si vogliano aggiungere informazioni aggiuntive oppure generare un nuova eccezione personalizzata è opportuno utilizzare questo approcio con l'unica accortezza, onde evitare il problema del troncamento dello stack, di passare l'eccezione originale nel costruttore della nuova in modo da salvare i dati dello stack all'interno dell'inner exception:

[C#]

try
{
    // esecuzione di una qualche operazione che fallisce
}
catch (Exception ex)
{
    // effettuare un cleanup
    throw new ApplicationException("L'operazione è fallita!", ex);
}

A conclusione di questa breve analisi elenco tre punti che possono essere utili nella gestione delle eccezioni:

  1. Catturare solo le eccezioni per le quali si debba effettuare una sorta di cleanup e che abbiano una certa importanza
  2. Per alzare nuovamente l'eccezione usate il throw
  3. Se doveste generare una nuova eccezione personalizzata oppure aggiungere qualche informazione a quella originale ricordatevi sempre di passare anch'essa onde perdere le info dello stack.

 

SQL Cache Dependency con SQL Server, LINQ e ASP.NET 3.5

Riporto qui sotto una libera traduzione di un post molto interessante sul quale mi sono imbattuto qualche giorno fa sul tema del caching in sistemi multiserver. 

ASP.NET fornisce diverse opzioni di caching. In questo caso prenderemo in esame una feature poco conosciuta di ASP.NET 3.5 chiamata SQL Cache Dependency. SQL Cache Dependency nasce in realtà con SQL Server 2000, ma la sua utilità era limitata per via del meccanismo di polling che imponeva un continuo check del DB per individuarne le modifiche, e a modifica individuata, notificava alla web application che la cache andava aggiornata.

Con SQL Server 2005 e 2008, ASP.NET ha adottato finalmente un sistema più performante per la SQL Cache Dependency. Lo sviluppatore può ora infatti delegare direttamente a SQL Server il compito di informarlo quando una modifica ha avuto luogo. In pratica ora la web application non deve continuamente monitorare il DB per accorgersi di eventuali modifiche.

Nell'esempio seguente useremo SQL Server 2005 e il database Pubs che potete scaricare qui. Anzitutto per consentire a SQL Server di inviare le notifiche occorre abilitare il Microsoft SQL Server 2005 Service Broker. Per verificare se questo servizio è già abilitato sul Catalog (Pubs n.d.r.) specificato basta lanciare il seguente script SQL:

[T-SQL]


SELECT name, is_broker_enabled FROM sys.databases

Se la riga con il nome del catalog interessato ha un 1 nella colonna is_broken_enabled, significa che il servizio è già attivo altrimenti bisogna attivarlo. Per farlo occorre lanciare il seguente script che sfrutta il comando ALTER DATABASE:

[T-SQL]


ALTER DATABASE Pubs SET ENABLE_BROKER
GO

Qualora però il database sia in uso il comando precedente sembra non terminare mai, e googlando ho scoperto qui il modo per farlo terminare istantaneamente.

[T-SQL]


ALTER DATABASE Pubs SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE
GO

A questo punto occorre informare SQL Server che l'account con cui gira Internet Information Services (IIS) ha i permessi per sottoscriversi alle notifiche. Lo script seguente spiega esattamente come effettuare questa operazione con l'account ASPNET sul server TESTSERVER:

[T-SQL]


GRANT SUBSCRIBE QUERY NOTIFICATIONS TO 'TESTSERVER\ASPNET'

Ora che il Database è stato correttamente configurato, procediamo con il creare il Website ASP.NET che utilizzi la SQL Cache Dependecy. Apriamo Visual Studio 2008 e clicchiamo su File > New > Web > ASP.NET Web Application.

t;p>Di default, Visual Studio 2008 crea una pagina Default.aspx alla creazione di una nuova Web Application. Useremo questa pagina per interrogare il Database, SQL Cache Dependency per "cachare" i dati ritornatici ed infine visualizzarli.

 

Apriamo la pagina e trasciniamoci dentro un DropDownBox control, infine rinominiamolo cboDiscount.

[ASPX/HTML]

<form id="form1" runat="server">
   <div>
      <asp:DropDownList id="cboDiscount" runat="server" >
      </asp:DropDownList>
   </div>
</form>

In questo caso useremo LINQ to SQL come access data layer. Aggiungiamo quindi un nuovo file di tipo LINQ to SQL Classes al progetto:

Apriamo il file appena creato e trasciniamo attraverso il designer la tabella Discounts al suo interno.

Trascinando la tabella il designer creerà una nuova chiave di tipo Connection String nel web.config simile a questa:

[XML]

<connectionStrings>
   <add name="pubsConnectionString" 
            connectionString="Data Source=dotnet-testbed;Initial Catalog=Pubs;Integrated Security=True"  
            providerName="System.Data.SqlClient" />
</connectionStrings>

Per abilitare la ricezione delle notifiche occorre creare un listener. Il punto migliore per collocarlo è nel file Global.asax che solitamente si trova nel root folder delle applicazioni ASP. Aggiungiamone uno al progetto e quindi andiamo ad inserire nel codice l'attivazione e la disattivazione del listener:

[C#]

protected void Application_Start(object sender,EventArgs  e)
{
   SQLDependency.Start(ConfigurationManager.ConnectionString["pubsConnectionString"].ConnectionString); 
}
protected void Application_End(object sender,EventArgs  e)
{
   SQLDependency.Stop(ConfigurationManager.ConnectionString["pubsConnectionString"].ConnectionString); 
}

Il prossimo passo è scrivere la parte di codice che setti la cache dependency. In questo caso il codice è inserito in un Extension Method introdotti da C# 3.0 and VB.NET 9.0, che consente allo sviluppatore di estendere una classe esistente creando dei metodi che possono essere aggiunti alla classe esistente. Per farlo aggiungiamo una nuovo file di tipo Class e lo nominiamo MyExtensions. Gli Extension Method sono statici e perciò vanno marcati con la keyword static.

[C#]

public static class MyExtensions
{
   public static List<T> LinqCache<T> (this Table<T> query) where T : class
   {
      string tableName = query.Context.Mapping.GetTable(typeof(T)).TableName;
      List<T> result = HttpContext.Current.Cache[tableName] as List<T>;

      if (result == null)
      {
         using(SqlConnection  cn = new SqlConnection(query.Context.Connection.ConnectionString))
         {
            cn.Open();
            SqlCommand  cmd = new SqlCommand(query.Context.GetCommand(query).CommandText, cn);
            cmd.Notification = null;
            cmd.NotificationAutoEnlist = null;
            SqlCacheDependencyAdmin.EnableNotifications(query.Context.Connection.ConnectionString);
            if (!SqlCacheDependencyAdmin.GetTablesEnabledForNotifications(query.Context.Connection.ConnectionString).Contains(tableName))
            {
               SqlCacheDependencyAdmin.EnableTableForNotifications(query.Context.Connection.ConnectionString, tableName);
            }
            SqlCacheDependency dependency = new SqlCacheDependency(cmd);
            cmd.ExecuteNonQuery();

            result = query.ToList();
            HttpContext.Current.Cache.Insert(tableName, result, dependency);
        }
     }
     return result;
   }
}

Questo è a tutti gli effetti un Extension Method per la classe System.Data.Linq.Table<> e può essere utilizza con qualsiasi LINQ query. Il cuore di questo metodo è SqlCacheDependencyAdmin.EnableNotifications che effettua il check per capire se il Service Broker è abilitato. Se così è verifica che la tabella sia già abilitata alla notifica tramite SqlCacheDependencyAdmin.GetTablesEnabledForNotifications. Se così non fosse SqlCacheDependencyAdmin.EnableTableForNotifications abilita le notifiche sulla tabella.

La prima volta che questo Extension Method verrà invocato verrà creata la tabella AspNet_SqlCacheTablesForChangeNotification nel database Pubs.

Aprendola si noterà che è stata aggiunta una riga a testimonianza che il Service Broker è funzionante.

A questo punto, come riprova, cambiando un valore nella tabella vi accorgerete che la cache ASP.NET verrà correttamente pulita.

Sebbene questo sistema non migliori sensibilmente le performance di una web application, certamente può essere di aiuto nel caso di sistemi distribuiti su più server. In particolare con tutte quelle tabelle che non varino in continuazione. Qui potete trovare il codice della solution, sempre riconducibile al post citato in partenza.

Cambiare l'assegnatario di una task in Sharepoint

L&#39;errore &quot;The task is currently locked by a running workflow and cannot be edited&quot; pu&#242; venire alzato quando si cerca di cambiare una task.

Con i Workflow in Sharepoint può capitare di voler modificare l'owner di una task. Cambiare semplicemente il campo "Assigned To" del Task Item però non è sufficiente, infatti così facendo la prossima volta che il workflow accederà alla task, ad esempio quando il nuovo owner effettuerà un accesso ad essa, verrà alzato l'errore "The task is currently locked by a running workflow and cannot be edited". Dopo lungo penare mi è arrivata la soffiata corretta: infatti è sufficiente agire su un campo particolare del task item per violare il lock. Tale campo si chiama "WorkflowVersion": quando esso ha valore diverso da 1 (inteso come intero) l'applicazione alza l'eccezzione e viene settato ad un valore diverso da 1 ogni volta che viene fatto l'update della task. 

Di conseguenza il trucco è:

  1. Verificare se tale campo è diverso da 1, se lo è allora lo si setta ad uno e si fà l'update della task
  2. Si modifica il campo "Assigned To" mettendo il nuovo valore (naturalmente si possono modificare altri campi della task) e si fà l'update. Il framework a questo punto setterà nuovamente il valore di "WorkflowVersion" a qualcosa di diverso da 1.
  3. Per evitare che il prossimo accesso trovi il lock settiamo nuovamente il campo "WorkflowVersion" ad 1 e facciamo l'update

 

Qua sotto riporto un breve esempio sul come eseguire l'operazione:

[C#]

public static void UpdateTask(Guid guid,string _url)
{
   using(SPSite site = new SPSite(_url))
   {
      using(SPWeb web = site.OpenWeb())
      {
         web.AllowUnsafeUpdates = true;

         // TaskList is the list where the task are stored
         SPList lib = web.Lists["TaskList"];

         // retrieve the item
         SPListItem item = lib.Items[guid];

         //if the item is locked-> unlock and update
         if ((int)item["WorkflowVersion"] != 1)
         {
            item["WorkflowVersion"] = 1;
            item.Update();
         }
         
         //update your field, in this case the "assigned to" value :)
         item["Assigned To"] = "Bill Gates";
         item.Update();
         
         item["WorkflowVersion"] = 1;
         item.Update();
         
         web.AllowUnsafeUpdates = false;
      }
   }
}

 

Cambiare il cursore del mouse in una Windows Application

Come cambiare il cursore di una Windows Application

A volte in una Windows Application è utile modificare il cursore del mouse, ad esempio durante un caricamento, per aiutare l'utente a percepire quando l'applicazione sta effettuando delle operazioni e dunque potrebbe non rispondere. Ad esempio un classico è visualizzare la clessidra durante il caricamento. Per farlo è molto semplice, basta agire sulla property Current dell'oggetto Cursor in questo modo:

[C#]

// hourglass cursor
Cursor.Current = Cursors.WaitCursor;
try
{
  Thread.Sleep(5000);  // wait for a while
}
finally
{
  Cursor.Current = Cursors.Default;
}

 

La classe Cursors contiene una serie di cursori tra cui il WaitCursor che è la classica clessidra oppure in Vista e Seven il circolino di attesa. Per tornare al default basta appunto usare il Default che tornerà a visualizzare la classica freccia del mouse. Una cosa a cui prestare attenzione nell'esempio è il fatto che il default del cursore viene resettato nel finally di modo che qualunque cosa succeda all'uscita dal codice in cui si doveva visualizzare un cursore diverso, il default verrà ripristinato, anche in caso eccezioni non gestite.