Contesto: devo lanciare un'operazione che può durare molto tempo eseguita da un metodo sincrono che non torna eventi (legacy). Devo fornire un feedback all'utente (una sorta di pagina clessidra) e, a lavoro terminato, mostrare il risultato.
Soluzione adottata: creo una pagina per iniziare l'operazione e configurarne i parametri (pagina Start), una autorefreshante per comunicare che il
lavoro è in corso (pagina Wait) e una pagina per mostrare i risultati (pagina Results).
La pagina Start:
- mette in sessione un flag per indicare che il calcolo è in corso
- istanzia un delegate per richiamare il metodo sincrono con un nuovo thread;
- lancia il nuovo thread passando eventuali parametri del metodo, un riferimento al metodo di callback e un riferimento al caller stesso
- carica la pagina Wait
Il caller del MetodoLungo:
- a lavoro completato richiama il metodo di callback sulla pagina Start
Il metodo di callback (su Start):
- riaggancia il chiamante
- recupera il risultato del calcolo usando EndInvoke sul chiamante
- mette il risultato in sessione
- setta il flag in sessione per indicare che il calcolo è terminato
La pagina Wait:
- ha un tag meta (magari inserito da codice) per fare il refresh automatico
- verifica il flag in sessione per sapere quando passare alla pagina dei risultati
La pagina Results:
- recupera il risultato dalla sessione e lo mostra

Codice:
pagina Start:
   public delegate TipoRisultatoCalcoloLungo MetodoLungoAsyncCaller(TipoParametro1MetodoLungo par1,   TipoParametro2MetodoLungo par2);

    public partial class StartPage
    {
     ...
     void Toolbar1_ButtonClick(object sender, ToolbarEventArgs ea)
        {
             
Session[SessionConstants.SCAN_IN_PROGRESS] = true;

                        // Create the delegate.
                        MetodoLungoAsyncCaller caller = new MetodoLungoAsyncCaller(MetodoLungo);

                        // Initiate the asychronous call.  Include an AsyncCallback
                        // delegate representing the callback method, and the data
                        // needed to call EndInvoke.
                        IAsyncResult result = caller.BeginInvoke(valorePar1, valorePar2,
                            new AsyncCallback(CallbackMethod),
                            caller);

                        // goto to "hourglass" page
                        Response.Redirect(WebFormsConstants.WAIT_ASPX);
        }
        ...
        // Callback method
        public void CallbackMethod(IAsyncResult ar)
        {
            // Retrieve the delegate.
            MetodoLungoAsyncCaller caller = (MetodoLungoAsyncCaller)ar.AsyncState;

            // Call EndInvoke to retrieve the results.
            TipoRisultatoCalcoloLungo risultato = caller.EndInvoke(ar);
            Session[SessionConstants.RISULTATO] = risultato;
            // set flag in session to comunicate scan completed
            Session[SessionConstants.SCAN_IN_PROGRESS] = false;
         }
         ...
}

pagina Wait:
protected void Page_Load(object sender, EventArgs e)
        {
            HtmlMeta meta = new HtmlMeta();
            meta.HttpEquiv = "refresh";
            meta.Content = "10";
            Header.Controls.Add(meta);

            if ((Boolean)Session[SessionConstants.SCAN_IN_PROGRESS] == false)
                Response.Redirect(WebFormsConstants.RESULT_ASPX);

            Message1.Text = "In Progress";
        }

 

Promemoria:          

            System.Web.UI.HtmlControls.HtmlMeta meta = new System.Web.UI.HtmlControls.HtmlMeta();
            meta.HttpEquiv = "refresh";
            meta.Content = "5";  // secondi
            Header.Controls.Add(meta);

Alternativo a UGI, perché no? Non perché UGI non abbia un grandissimo valore per noi, ma perché a volte affrontare gli stessi argomenti con una prospettiva diversa permette di cogliere aspetti non colti prima.
Avete mai sentito parlare di GUISA? Tanti degli argomenti affrontati non esistevano già prima? Non sono stati trattati anche su UGI? Perché creare uno UG separato? Forse per mettere a fuoco certi aspetti in modo più efficace …
E’ giusto interrogarsi se sia più sensato mantenere prospettive diverse “integrate” per non disperdere energie o se sia meglio renderle indipendenti per farle prosperare. E’ una scelta che facciamo tutti i giorni, ad esempio scegliendo se investire tutto presso un’unica banca per avere maggior potere o presso banche diverse per minimizzare il rischio in caso qualcosa vada storto.
Alternativo a UGI anche in senso organizzativo: UGI ha una gerarchia abbastanza precisa mentre UGIALT ha una struttura più “a rete”; perché non sfruttare la peculiarità di entrambi per arrivare più lontano?
La vera alternativa però penso stia nel fatto di lavorare alla “frontiera”, su ciò che non è consolidato. Questo non vuol dire non conoscere o usare ciò che è assodato, ma voler essere un po’ “ricercatori” per passione.
UGI lo ha sempre fatto? Bene, però ritengo che UGI sia il riferimento generale per il mondo .net, per cui ritengo giusto tratti tutti gli argomenti, consolidati o meno e soprattutto con una prospettiva “generalista”.
Ho sempre beneficiato del “mainstream” di Microsoft, perché essendo poco esperto mi è sempre stato comodo avere delle linee guida precise da seguire. Ho sempre faticato ad abbandonare questa prospettiva perché le altre alternative erano moltissime e poco omogenee; mi ha sempre spaventato il mondo open source proprio perché ti ritrovi dinnanzi un oceano di tools di cui è difficile cogliere pregi e difetti. Secondo me l’iniziativa ALT.NET cerca di colmare questa lacuna portando un po’ di “ordine” in questo oceano sterminato, vagliando e facendo poi ricadere nel “mainstream” i tools migliori. Perché privarsene? Solo perché il nome si ricollega alle esperienze che avete di persone alternative a tutti i costi? Se poi sapete che in realtà gli stessi fondatori del movimento ritenevano più adeguato il nome Pragmatic.Net e hanno lasciato Alt.net solo per “questioni storiche”, perché tante polemiche?
Non sarebbe meglio intendere alternativo come la prima o la seconda definizione del garzanti (1. che si alterna; alternato: moto alternativo, che va ora in un verso, ora in quello opposto … 2. che pone un'alternativa, una scelta) piuttosto che come la terza (3. si dice di espressione o manifestazione culturale, politica, sociale che si contrappone a quelle correnti o dominanti)?

Tentando di aggiornare una entità persistita su una tabella di SQL Server dove è presente un trigger ottengo l'eccezione seguente e l'aggiornamento fallisce:

[NHibernate.AdoNet.TooManyRowsAffectedException] {"Unexpected row count: 2; expected: 1"} NHibernate.AdoNet.TooManyRowsAffectedException

A quanto pare il fatto che il trigger aggiorni un altro record fa sì che il db ritorni 2 come numero di record aggiornati, il che fa pensare ad NH che ci sia stato un errore. Non ho trovato opzioni che dicano a NH di non verificare il numero di record aggiornati; sembra che l'unica soluzione consista nel disabilitare il conteggio dei record modificati nel trigger (impostare SET NOCOUNT ON prima del comando di UPDATE nel trigger e impostare SET NOCOUNT OFF dopo il comando di UPDATE). Non ho trovato altre soluzioni.

 - stringhe classiche: myString = "prova=\"aaa\"" (uso \)
 - stringhe con @: myString = @"prova=""aaa""" (raddoppio gli apici)

System.Environment.NewLine OPPURE "/r/n"


Siccome non posso rimuovere elementi da una collection mentre la sto scorrendo con un ciclo foreach, devo salvare gli elementi da eliminare in una lista temporanea e fare poi un altro ciclo, sulla lista temporanea, per rimuoverli:

IList<Child> childrenToDelete = new List<Child>();                   
foreach (Child child in Father.Children)
{
   if (...)
      childrenToDelete.Add(child);
}
foreach (Child child in childrenToDelete)
   Father.Children.Remove(child);
                       

Innanzitutto dichiaro un enum con la lista dei possibili flag; è importante che l'enum abbia l'attributo [Flags] e che gli elementi della lista abbiano associato un valore che sia potenza di 2 (1, 2, 4, ...). Si possono definire fino a 32 elementi.

[Flags] private enum Buttons : int
{
···Ok = 1, Cancel = 2, Retry = 4, Help = 8
}

Tip: è possibile aggiungere alla enumerazione un elemento "All" così: All = Ok | Cancel | Retry | Help

Attivare più flag: concatenali con l'operatore bitwise "|" (or):
Buttons buttons;
buttons = Buttons.Ok | Buttons.Cancel;


Disattivare un flag: usa l'operatore bitwise "~" (not):
buttons &= ~Buttons.Cancel;

Verificare se un flag è attivo:

Testing to see if a certain flag is set: usa l'operatore bitwise "&" (and):
if ((buttons&Buttons.Ok)==Buttons.Ok)
···Console.WriteLine("Ok");

Sono comode per simulare l'ereditarietà multipla.

Esempio: il padre di un container può essere di tipo Grid o item; per poter castare il parent del container a qualcosa e richiamare un metodo uguale su grid e item, ho dovuto definire una interfaccia generica che prevede il metodo, specificare che grid e item la implementano, castare il parent all'interfaccia e richiamare il metodo.

public interface IGenericElement
    {
        GenericGrid Grid();
        IGenericElement Father();
    }


public partial class GenericGrid : System.Web.UI.UserControl, IGenericElement
    {
        ...

        public GenericGrid Grid()
        {
            return this;
        }

        public IGenericElement Father()
        {
            return this;
        }
 ...
    }


    public partial class GenericContainer : System.Web.UI.UserControl, IGenericElement
    {
 ...
        public GenericGrid Grid()
        {
            return ((IGenericElement) Parent).Grid();
        }

        public IGenericElement Father()
        {
            // container father could be a grid or an item
            return (IGenericElement) Parent;
        }
espongo una proprietà della griglia attraverso il container:
        public Boolean IsInList
        {
            get { return Grid().IsList ; }
        }
    }

public abstract partial class GenericItemBase : System.Web.UI.UserControl, IGenericElement
    {
 ...
        public GenericGrid Grid()
        {
            return ((IGenericElement) Parent).Grid();
        }
        public IGenericElement Father()
        {
            // item father is always a container
            return (IGenericElement)Parent;
        }
espongo una property della grid attraverso l'item:
        public Boolean IsInList
        {
            get { return Grid().IsList; }
        }
espongo una property del container padre (item contenuti solo in container) attraverso l'item:
        public Boolean IsInNestedContainer
        {
            get { return ((GenericContainer) Father()).NestChildren; }
        }

NOTA: l'interfaccia specifica metodi, non property.

Per serializzare in JSON un oggetto:

System.Web.Script.Serialization.JavaScriptSerializer serializer = new JavaScriptSerializer();
String result = serializer.Serialize(o);