Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Archive for the '.net' Category

Certificazione MCTS Win verso l’MCPD Enterprise Framework 2.0 – Parte 2/5: 70-526

Posted by Ricibald on 18th February 2009

Questa è la seconda parte (vedi la prima parte) di una serie di pdf che ho scritto per riassumere il contenuto delle certificazioni Microsoft (vedi terza parte) che portano alla certificazione Microsoft MCPD di livello “enterprise” (Distributed).

Logicamente questi pdf li avevo creati per riassumere i concetti che io ritenevo importante riassumere. Potrebbero essere appunti che non vi interessano affatto o che reputate inutili. Viceversa potrebbe essere un’ottima integrazione e un’ottimo punto di partenza per ripassare quanto studiato sul libro ufficiale dell’esame.

TS: Microsoft .NET Framework 2.0 – Windows-Based Client Development

Posted in .net | 1 Comment »

Certificazione MCPD Enterprise Framework 2.0 – Parte 1/5: 70-536

Posted by Ricibald on 17th February 2009

Questa è la prima parte di una serie di pdf che ho scritto per riassumere il contenuto delle certificazioni Microsoft (vedi seconda parte) che portano alla certificazione Microsoft MCPD di livello “enterprise” (Distributed).

Logicamente questi pdf li avevo creati per riassumere i concetti che io ritenevo importante riassumere. Potrebbero essere appunti che non vi interessano affatto o che reputate inutili. Viceversa potrebbe essere un’ottima integrazione e un’ottimo punto di partenza per ripassare quanto studiato sul libro ufficiale dell’esame.

Esame Microsoft 70-536: TS: Microsoft .NET Framework – Application Development Foundation

Posted in .net | No Comments »

WebControl Sempre Abilitati

Posted by Ricibald on 6th February 2009

Il problema è questo: devo creare un LinkButton che sia sempre Enabled. Ma se un suo contenitore (ad esempio un Panel) è disabilitato, allora il LinkButton nel rendering va ad impostare la proprietà disabled navigando tra i padri.

Sarebbe bello se riuscissimo a modificare il comportamento dei WebControl per non rilevare disabled in uno dei padri. La soluzione sta nell’impedire che il nostro controllo figlio (ad esempio il nostro LinkButton) faccia uso di alcune funzionalità ereditate dalla classe base WebControl.

Ricerche su Internet non sono servite. Ho cercato come è implementato il WebControl ispezionando il codice sorgente di WebControl di Mono e il codice sorgente di LinkButton.

Il risultato è di seguito:

    public class LinkButtonSmartAlwaysEnabled : LinkButton
    {
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            //base.AddAttributesToRender(writer);
            if (ID != null)
                writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID);

            if (AccessKey != string.Empty)
                writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, AccessKey);

            //if (!enabled)
            //    writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");

            if (ToolTip != string.Empty)
                writer.AddAttribute(HtmlTextWriterAttribute.Title, ToolTip);

            if (TabIndex != 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, TabIndex.ToString());

            if (Enabled && Page != null)
            {
                string href = Page.ClientScript.GetPostBackClientHyperlink(this, "");
                writer.AddAttribute(HtmlTextWriterAttribute.Href, href);
            }

            if (this.Attributes != null)
                foreach (string s in this.Attributes.Keys)
                    writer.AddAttribute(s, this.Attributes[s]);
        }
    }

Posted in .net | No Comments »

Customizzare Microsoft Dynamics CRM 4.0

Posted by Ricibald on 5th February 2009

Microsoft Dynamics CRM 4.0 nasce come pacchetto immediatamente utilizzabile, ma in realtà deve essere personalizzato per mostrare le cose che effettivamente servono all’utente finale e rinominando concetti che potrebbero non esistere nel dominio applicativo.

Prendiamo il caso di una pubblica amministrazione che voglia avere un CRM. In questo caso il concetto di “cliente” deve essere rinominato in “cittadino”. Alcune proprietà del “cittadino” non avranno più senso di esistere, mentre saranno necessarie altre proprietà completamente nuove.

Microsoft Dynamics CRM 4.0 consente di:

  • aggiungere nuove entità (una nuova struttura dati, come “diritto civile”)
  • aggiungere nuovi attributi (come la proprietà “anni galera” su un entità Contact)
  • cambiare il nome di un’entità (“contact” diventa “citizen”)
  • aggiungere o cambiare viste
  • aggiungere o cambiare form
  • creare relazioni (“diritto civile” ha relazione N:N con “contact”)
  • importare ed esportare le customizzazioni

Dovrebbe essere sempre buona regola verificare attentamente che gli attributi/entità che stiamo aggiungendo non siano già presenti sotto altro nome.
La creazione di un nuovo attributo consente di specificare il tipo del valore:

  • nvarchar: semplice testo fino a 4000 caratteri. Sono personalizzabili modalità di visualizzazione e comportamento del testo
  • ntext: casella di testo fino a 100.000 caratteri
  • int: semplice intero. Si può specificare il format vincolando l’intero ad assumere solo un certo range di valori
  • float: semplice float. Si può impostare la precisione, valori minimi e massimi.
  • money: semplice decimal. Supporta transazioni in più valute, grazie alla memorizzazione del valore in un formato wrappato indipendente dalla valuta (base currency), chiamato base attribute
  • bit: semplice booleano. Si può associare nel caso affermativo/negativo. Nella form si può specificare se mostrarlo come checkbox, 2 radio o dropdown
  • datetime: data, mostrata coin widget calendario. Si può scegliere se mostrare data e ora oppure solo data
  • picklist: dropdown che consente di memorizzare un enumerato, di cui si può specificare l’intero corrispondente
  • lookup: vincoli verso altre entità. Sono attributi creabili tramite relazioni N:1 con un’altra entità
  • partylist: molteplici lookup. Proprietà che rappresentano relazioni N:N ma che non è possibile creare

Ogni attributo può avere associati i seguenti field constraint:

  • Business Required: obbligatorio per il salvataggio. Mostrato con * rosso
  • Business Recommended: consigliato ma non obbligatorio. Mostrato con + blu
  • No Constraint (predefinito): campo normale

Ogni relazione può avere definito il tipo di comportamento a catena (cascade behaviour) nel caso di eventi avvenuti nell’entità collegata (come l’eliminazione). Durante la definizione di una relazione si può specificare il behaviour type:

  • Parental: ogni azione viene anche applicata sulle entità child collegate. Ogni entità può avere al massimo una relazione parental. Infatti, anche se si possono creare tante relazioni N:1 quante se ne vuole, solo una può essere Parental, e tutte le altre Referential.
  • Referential: le azioni nel record padre non impattano nel record figlio
  • Referential, Restrict Delete: le azioni nel record padre non impattano nel record figlio, ma il record padre non può essere eliminato finché esiste il record figlio
  • Configurable Cascading: ogni azione può essere opzionalmente anche applicata sulle entità child. Ma, a prescindere, se si elimina un’entità padre, ogni record dell’entità figlio sono automaticamente eliminate

La Navigation Pane sulla sinistra può essere customizzata e profilata utilizzando il Site Map o il link Personalize Workspace o il link Options dentro Tools. Per customizzare tale file è necessario prima esportarlo e poi reimportarlo una volta modificato.

Esempio di una porzione del sitemap [reference]:

<SiteMap>
  <Area Id="Workplace" ResourceId="Area_Workplace" ShowGroups="true" Icon="/_imgs/workplace_24x24.gif" DescriptionResourceId="Workplace_Description">
    <Group Id="MyWork" ResourceId="Group_MyWork" DescriptionResourceId="My_Work_Description">
      <SubArea Id="nav_activities" Entity="activitypointer" DescriptionResourceId="Activities_SubArea_Description" Url="/Workplace/home_activities.aspx" />
      <SubArea Id="nav_calendar" Icon="/_imgs/area/18_calendar.gif" ResourceId="Homepage_Calendar" Url="/workplace/home_calendar.aspx" Client="Web">
        <Privilege Entity="activitypointer" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_import" Icon="/_imgs/area/18_import.gif" ResourceId="Homepage_Import" Url="/workplace/home_import.aspx" DescriptionResourceId="Imports_Description">
        <Privilege Entity="import" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_duplicatedetectionjobs" Icon="/_imgs/data_management.gif" ResourceId="Homepage_DuplicateDetectionJobs" Url="/Tools/DuplicateDetection/SystemWideDuplicateDetection/home_duplicatedetectionjobs.aspx" DescriptionResourceId="DuplicateDetectionJobs_Description">
        <Privilege Entity="asyncoperation" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_queues" Entity="queue" Url="/workplace/home_workplace.aspx" DescriptionResourceId="Queues_SubArea_Description">
        <Privilege Entity="activitypointer" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_answers" Entity="kbarticle" Url="/workplace/home_answers.aspx" DescriptionResourceId="Article_SubArea_Description">
        <Privilege Entity="subject" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_reports" Entity="report" Url="/CRMReports/home_reports.aspx" DescriptionResourceId="Reports_Description">
        <Privilege Entity="report" Privilege="Read" />
      </SubArea>
      <SubArea Id="nav_news" Entity="businessunitnewsarticle" Url="/home/homepage/home_news.aspx" DescriptionResourceId="News_SubArea_Description" />
    </Group>
    <Group Id="Customers" ResourceId="Group_Customers" DescriptionResourceId="Customers_Description">
      <SubArea Id="nav_accts" Entity="account" DescriptionResourceId="Account_SubArea_Description" />
      <SubArea Id="nav_conts" Entity="contact" DescriptionResourceId="Contact_SubArea_Description" />
    </Group>
    <Group Id="SFA" ResourceId="Area_Sales" IsProfile="true" DescriptionResourceId="Sales_Description">
      <SubArea Id="nav_lists1" Entity="list" DescriptionResourceId="MarketingList_SubArea_Description" />
      <SubArea Id="nav_leads" Entity="lead" DescriptionResourceId="Lead_SubArea_Description" />
      <SubArea Id="nav_oppts" Entity="opportunity" DescriptionResourceId="Opportunity_SubArea_Description" />
      <SubArea Id="nav_quotes" Entity="quote" DescriptionResourceId="Quote_SubArea_Description" />
      <SubArea Id="nav_orders" Entity="salesorder" DescriptionResourceId="Orders_SubArea_Description" />
      <SubArea Id="nav_invoices" Entity="invoice" DescriptionResourceId="Invoice_SubArea_Description" />
    </Group>
    <Group Id="MA" ResourceId="Area_Marketing" IsProfile="true" DescriptionResourceId="Marketing_Description">
      <SubArea Id="nav_lists" Entity="list" DescriptionResourceId="MarketingList_SubArea_Description" />
      <SubArea Id="nav_campaigns" Entity="campaign" DescriptionResourceId="Campaign_SubArea_Description" Url="/MA/home_camps.aspx" />
      <SubArea Id="nav_minicamps" Entity="bulkoperation" DescriptionResourceId="Quick_Campaign_Description" Icon="/_imgs/ico_18_minicamps.gif" OutlookShortcutIcon="/_imgs/olk_4400.ico" Url="/MA/home_minicamps.aspx">
        <Privilege Privilege="AllowQuickCampaign" />
      </SubArea>
    </Group>
    <Group Id="CS" ResourceId="Area_Service" IsProfile="true" DescriptionResourceId="Customer_Service_Description">
      <SubArea Id="nav_cases" Entity="incident" DescriptionResourceId="Cases_SubArea_Description" Url="/CS/home_cases.aspx" />
      <SubArea Id="nav_managekb" ResourceId="Homepage_KBManager" DescriptionResourceId="KBManager_SubArea_Description" Icon="/_imgs/ico_18_126.gif" Url="/cs/home_managekb.aspx" AvailableOffline="false">
        <Privilege Entity="kbarticle" Privilege="Read,Write,Create" />
      </SubArea>
      <SubArea Id="nav_contracts" Entity="contract" DescriptionResourceId="Contract_SubArea_Description" />
    </Group>
    <Group Id="SM" ResourceId="Area_Scheduling" IsProfile="true" DescriptionResourceId="Scheduling_Group_Description">
      <SubArea Id="nav_apptbook" ResourceId="Homepage_AppointmentBook" DescriptionResourceId="AppointmentBook_SubArea_Description" Icon="/_imgs/ico_18_servicecal.gif" Url="/sm/home_apptbook.aspx" AvailableOffline="false">
        <Privilege Entity="activitypointer" Privilege="Read" />
        <Privilege Entity="service" Privilege="Read" />
      </SubArea>
    </Group>
  </Area>
</SiteMap>

La struttura riflette la seguente immagine:

Alcune note:

  • Area: rappresenta il “pulsantone sulla sinistra”, in questo caso il Workplace
  • Group: consente di organizzare i pulsanti di un area, in modo simile agli accordion panel. Se vale IsProfile, allora il gruppo può essere mostrato o meno a seconda della scelta personale dell’utente (tramite “Personalize Workspace…”)
  • SubArea: rappresenta il pulsante di un Area (Activities, Calendar, …). Può essere assegnato in alternativa un URL o un Entity. Quest’ultimo contiene l’identificatore dell’entity da mostrare (di cui verrà visualizzata la “pubic default view”
  • Privilege: visualizza la SubArea solo agli utenti che hanno determinati privilegi nell’Entity specificata

Per creare pulsanti custom, menu e aree di navigazione per form e view si utilizza ISV.Config (ISV = Independent Service Vendor). Ogni item può aprire uno specifico URL o creare del codice jscript che utilizza funzioni jscript documentate nell’sdk. Per modificare l’ISV.Config si segue la stessa procedura del site map.

Esempio di una porzione del ISV.Config [reference]:

<IsvConfig>
  <configuration version="3.0.0000.0">
    <Root>
      <Entities>
        <Entity name="account">
          <!-- The Account Tool Bar -->
          <ToolBar ValidForCreate="0" ValidForUpdate="1">
            <Button Icon="/_imgs/logo_16.gif" PassParams="1" WinMode="0" ValidForCreate="0" ValidForUpdate="1" JavaScript="var ObId=document.getElementById('crmFormSubmitId').value; var ObTp=document.getElementById('crmFormSubmitObjectTypeName').value; window.open('page.aspx?id=' + ObId + '&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;type=' + ObTp);">
              <Titles>
                <Title LCID="1033" Text="Provide" />
              </Titles>
              <ToolTips>
                <ToolTip LCID="1033" Text="Provide" />
              </ToolTips>
            </Button>
          </ToolBar>
        </Entity>
      </Entities>
    </configuration>
</IsvConfig>

L’ISV.Config riportato aggiunge un nuovo pulsante “Provide” all’entità account nella barra degli strumenti, mostrato quando si modifica un account esistente.

Alcune note:

  • la Toolbar rappresenta la barra degli strumenti nella form di dettaglio
  • sia il toolbar che il button possono essere validi per la form di creazione (ValidForCreate) e/o per la form di aggiornamento (ValidForUpdate)
  • PassParams consente di passare dei parametri GET all’url invocato in modo tale che sia possibile ricostruire il contesto del chiamante (entity di lavoro, id dell’organizzazione, …)
  • WinMode consente di configurare il comportamento della finestra (window, modal, modeless)
  • Javascript o Url: uno dei due deve essere specificato e rappresenta il comportamento alla pressione
  • elementId utilizzati nel javascript: “crmFormSubmitId” è l’id dell’entità in editing; “crmFormSubmitObjectTypeName” è il tipo di entità che si stà lavorando (“account”)

Altro ISV.Config di esempio:

<IsvConfig>
  <configuration version="3.0.0000.0">
    <Root>
      <Entities>
        <Entity name="contact">
          <Grid>
            <MenuBar>
              <Buttons>
                <Button Icon="/ISV/VE/img/LiveLocal3D.ico" PassParams="1" WinMode="0" JavaScript="var selected_contacts = getSelected('crmGrid'); window.open('page.aspx?sel=' + selected_contacts);">
                  <Titles>
                    <Title LCID="1033" Text="Visualizza" />
                  </Titles>
                  <ToolTips>
                    <ToolTip LCID="1033" Text="Visualizza" />
                  </ToolTips>
                </Button>
              </Buttons>
            </MenuBar>
          </Grid>
        </Entity>
      </Entities>
    </configuration>
</IsvConfig>

Questo ISV.Config consente di effettuare lavoro sulla selezione di molteplici elementi sulla griglia, utilizzando la funzione javascript getSelected.

Infine, se si vuole interrogare un web service si può utilizzare la funzione javascript RemoteCommand, che consente di inviare un comando remoto (localizzato in Inetpub\wwwroot\_controls\RemoteCommands\RemoteCommand.js). Ad esempio:

var ObId=document.getElementById('crmFormSubmitId').value;
try {
    var oProgressRetrieveCommand = new RemoteCommand('WebServiceName', 'Method', 'Path');
    oProgressRetrieveCommand.ErrorHandler = emptyErrorHandler;
    oProgressRetrieveCommand.SetParameter('objectId', ObId);
    var oResult = oProgressRetrieveCommand.Execute();

    if (oResult.Success)
    {
        var Return = oResult.ReturnValue;
        alert(Return.Success+', '+Return.Value);
    } else {
        alert('Fail')
    }
} catch(e) {
   alert('Error: '+e.message);
}

Molto spesso i javascript customizzati per un certo pulsante devono essere raffinati e testati. Risulta quindi molto scomodo configurare il javascript direttamente dentro il file ISV.config. Per questo conviene all’interno dell’ISV.config richiamare una funzione javascript importata. Risulta quindi necessario dichiarare un nuovo file js da importare nell’header della pagina. Questo non risulta possibile in Microsoft CRM, ma si può procedere andando a gestire l’evento OnLoad della nostra form di interesse andando ad inserire il codice seguente:

var script = document.createElement("script");
script.type = "text/javascript";
script.src= '../../js/myscript.js'
document.getElementsByTagName("head")[0].appendChild(script);

Per aggiungere funzionalità completamente custom si utilizzano gli iframe. In generale non si dovrebbe utilizzare nulla che non sia documentato nel Microsoft Dynamics CRM SDK, poiché si potrebbero avere funzioni “non ufficiali” che cambiano tra una versione e l’altra del crm.

Una piccola nota: l’ISV.Config per funzionare deve essere abilitato. Per questo si devono selezionare tutti gli elementi da Settings, Administration, System Settings, Customization, Custom menus and toolbars.

Un esempio di come utilizzare le API per ottenere le nostre custom properties:


[WebMethod]
public ArrayList Get_Account_Info(string idCompany, string nameCompany)
{
	ArrayList risultato=new ArrayList();
	Guid companyID = new Guid(idCompany);
	CrmServiceWsdl.CrmService service = GetCrmService(nameCompany);
	CrmServiceWsdl.TargetRetrieveDynamic target = new CrmServiceWsdl.TargetRetrieveDynamic();
	target.EntityId = companyID;
	target.EntityName = CrmServiceWsdl.EntityName.account.ToString();
	CrmServiceWsdl.ColumnSet columnSet = new CrmServiceWsdl.ColumnSet();

	// Aggiungere altre proprieta' se necessario
	columnSet.Attributes = new string[] { "name","new_friendlyname","new_ncasellediposta","new_nutenti","new_stato" };
	// Create a retrieve request object.
	CrmServiceWsdl.RetrieveRequest retrieve = new CrmServiceWsdl.RetrieveRequest();
	retrieve.Target = target;
	retrieve.ColumnSet = columnSet;
	retrieve.ReturnDynamicEntities = true;
	// Create a response reference and execute the retrieve request.
	CrmServiceWsdl.RetrieveResponse response;
	CrmServiceWsdl.DynamicEntity retrievedEntity;
	try
	{
		response = (CrmServiceWsdl.RetrieveResponse)service.Execute(retrieve);
		retrievedEntity = (CrmServiceWsdl.DynamicEntity)response.BusinessEntity;
	}
	catch (SoapException ex)
	{
		return new EntityProps[] { new EntityProps("error", ex.Detail.InnerXml) };
	}
	// Access the retrieved properties of the dynamic entity.
	foreach (CrmServiceWsdl.Property prop in retrievedEntity.Properties)
	{
		if (prop is CrmServiceWsdl.PicklistProperty)
		{
			//Un intero rappresentante lo stato
			risultato.Add( new EntityProps( prop.Name,((CrmServiceWsdl.PicklistProperty)prop).Value.Value));
		}
		if(prop is CrmServiceWsdl.CrmNumberProperty)
		{
			//Un valore numerico (int!)
			risultato.Add( new EntityProps(prop.Name,((CrmServiceWsdl.CrmNumberProperty)prop).Value.Value));
		}
		if(prop is CrmServiceWsdl.StringProperty)
		{
			//Una stringa (come ad es nome univoco)
			risultato.Add( new EntityProps(prop.Name, ((CrmServiceWsdl.StringProperty)prop).Value));
		}
	}
	if(risultato.Count==0)
	return new EntityProps[] { new EntityProps("error", "Error: cannot find Company")};
	return res;
}

private CrmServiceWsdl.CrmService GetCrmService(string organizationName)
{
	// Get the CRM Users appointments
	// Setup the Authentication Token
	CrmServiceWsdl.CrmAuthenticationToken token = new CrmServiceWsdl.CrmAuthenticationToken();
	token.OrganizationName = organizationName;
	CrmServiceWsdl.CrmService service = new CrmServiceWsdl.CrmService();
	if (CRMServer != null &amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp; CRMServer.Length > 0)
	{
		service.Url = "http://" + CRMServer + "/MSCRMServices/2007/CrmService.asmx";
	}
	service.Credentials = new NetworkCredential("administrator", "password"); //System.Net.CredentialCache.DefaultCredentials;
	service.CrmAuthenticationTokenValue = token;
	return service;
}

Senza entrare nel dettaglio di ogni singola funzione, si richiede al web service CRM la Execute di una Request (RetrieveRequest), che restituisce una Response (RetrieveResponse). A seconda del tipo di richiesta avremo una UpdateRequest e una corrispondente UpdateResponse e così via. La RetrieveRequest contiene i parametri della query (TargetRetrieveDynamic) e cosa si aspetta in output (ColumnSet). La RetrieveResponse restituisce una BusinessEntity (DynamicEntity) che contiene le varie proprietà da ispezionare che derivano tutte dalla classe base Property e la cui implementazione varia a seconda del tipo.

In realtà questo approccio, anche se generico, non è quello ottimo. Infatti, è possibile richiedere al CRM il WSDL aggiornato, che comprenderà anche l’accesso alle nostre nuove proprietà. Questo consente di richiamare le proprietà customizzate in maniera diretta e fortemente tipizzata attraverso l’esecuzione della Retrieve (esiste in modo analogo il metodo Update). Ad esempio:

[WebMethod]
public ArrayList Get_Account_Info(string idAccount, string nameAccount)
{
	ArrayList risultato=new ArrayList();
	Guid accountID = new Guid(idAccount);
	CrmServiceWsdl.CrmService service = GetCrmService(nameAccount);
	CrmServiceWsdl.EntityName entityName = CrmServiceWsdl.EntityName.account.ToString();
	CrmServiceWsdl.ColumnSet columnSet = new CrmServiceWsdl.ColumnSet();
	columnSet.Attributes = new string[] { "name","new_friendlyname","new_ncasellediposta","new_nutenti","new_stato" };

	// Ottieni account
	account accountRetrieved;
	try
	{
		accountRetrieved = (CrmServiceWsdl.account)service.Retrieve(entityName, accountID, columnSet);
	}
	catch (SoapException ex)
	{
		return new EntityProps[] { new EntityProps("error", ex.Detail.InnerXml) };
	}
	// Access the retrieved properties of the dynamic entity.
	risultato.Add( new EntityProps(prop.Name, accountRetrieved.new_friendlyname.Value.Value));
	risultato.Add( new EntityProps(prop.Name, accountRetrieved.new_ncasellediposta.Value.Value));
	risultato.Add( new EntityProps(prop.Name, accountRetrieved.new_nutenti.Value.Value));
	risultato.Add( new EntityProps(prop.Name, accountRetrieved.new_stato.Value.Value));

	if(risultato.Count==0)
		return new EntityProps[] { new EntityProps("error", "Error: cannot find Company")};
	return res;
}

Molto utile la costante ORG_UNIQUE_NAME che consente tramite javascript di fare riferimento alla company corrente.

Se si vuole infine scegliere più in profondità con la customizzazione si deve procedere in questo modo:

  • Esportare l’xml dell’entità da customizzare
  • Modificare l’xml
  • Importare l’xml modificato per modificare l’entità

In questo modo possiamo accedere a proprietà prima non visibili come ValidForCreateAPI o ValidForUpdateAPI per specificare se un attributo è valido in fase di creazione o update. Inoltre possiamo accedere a fetchXml per costruire query per l’utente o per l’organizzazione (consente anche di creare viste personalizzate), anche creabili con FetchXmlBuilder.

Un ottimo articolo che illustra la customizzazione con CRM è l’articolo di Marcello Fisicaro.

Posted in .net | No Comments »

Progettare un’Applicazione Web 2.0: Yahoo! Design Pattern Library

Posted by Ricibald on 18th November 2008

Le applicazioni vengono progettate seguendo degli approcci di progettazione formalizzati attraverso lo use case analysis e il conseguente utilizzo dei design pattern. La progettazione affronta problemi come la suddivisione delle responsabilità tra le varie classi e la loro conseguente comunicazione. In seguito, le applicazioni vengono scritte utilizzando delle API per la realizzazione di UI complesse, come le swing per Java o le WinForm per .NET.

Nelle applicazioni web viene però trascurata la progettazione legata all’usabilità e al social network, “delegando” in qualche modo quest’aspetto alla bontà di chi scriverà l’HTML, come se ogni volta ci scrivessimo da zero una dialog windows o il layout a grid in una windows application…

Yahoo! è stata la prima ad affrontare in modo rigoroso il problema dando una risposta a problemi comuni della progettazione delle pagine web:

  • Prestazioni delle pagine:  34 best practices e un plugin per firefox per ricevere al volo i consigli su come ottimizzare la pagina corrente…
  • Librerie UI: componenti come modal dialog, layout manager, treeview, … progettati per essere crossbrowser. Molto spesso si abbinano queste librerie a progetti più generici come JQuery per creare i propri personali effetti grafici non presenti nelle librerie di Yahoo!
  • Design Pattern Library: risolvono problemi ricorrenti nelle applicazioni web

Proprio di quest’ultimo faremo una carrellata dei pattern citati:

Search

  • Search Pagination, Item Pagination e Carousel
    • Problema: si vuole visualizzare il risultato di una ricerca e non è possibile mostrarli tutti in una pagina.
    • Soluzione:
      • creare un paginator che contiene gli elementi “Prev <- 2 3 4 5 6 7 8 9 10 11 -> Next”, dove il link corrente (7) non è cliccabile (Search Pagination).
      • creare una semplice navigazione “1-5 of 32 First | < Prev | Next > | Last” per ogni elemento, in modo da far mantenere all’utente il focus sulla navigazione avanti/indietro (Item Pagination)
      • creare una navigazione avanti/indietro molto grafica, da utilizzare in un ambito dove si scorre tra risultati che prevedono grafica (Carousel)
    • Librerie: YUI Paginator e YUI Carousel

Navigation

  • Breadcrumbs.
    • Problema: l’utente si trova in una pagina del sito da cui non è agevole accedere alle altre sezioni e potrebbe provenire da un altro sito e non avere quindi la percezione di dove si trova collocato nel sito che stà visitando.
    • Soluzione: presentare una lista orizzontale di link che rappresenta un “albero compatto” della collocazione del link corrente all’interno della struttura di un sito
    • Librerie: .NET SiteMapPath
  • Alphanumeric Filter Links.
    • Problema: l’utente ricerca un elemento di cui conosce solo la lettera iniziale.
    • Soluzione: consentire una ricerca partendo dalla lettera iniziale
  • Module Tabs.
    • Problema: esistono diversi pannelli che non possono essere visualizzati contemporaneamente e che comunque non ci sarebbe sufficiente spazio e di cui è necessario eseguire uno switch senza cambiare pagina web corrente.
    • Soluzione: creare una lista di tab separati da un ‘|’, il cui click causa l’attivazione lato client solo del pannello interessato, disattivato il precedente. Il tab è una rappresentazione della categorizzazione delle cartelline di lavoro tramite l’uso di linguette (tab).
    • Librerie: YUI TabView
  • Navigation Tabs
    • Problema: esistono 3-10 macrocategorie sufficientemente stabili da rappresentare, le quali devono essere utilizzate per indicare la posizione dell’utente all’interno del sito. Il cambio di categoria deve causare un completo caricamento di una nuova pagina.
    • Soluzione: utilizzare tab in alto, eventualmente con sottocategorie al click. Se le categorie sono troppe per lo spazio orizzontale, creare i tab sulla sinistra della pagina

Browsing

  • Page Grids
    • Problema: si devono creare/gestire molteplici pagine, le quali potrebbero essere costruite da gruppi di lavoro differenti
    • Soluzione: creare un template comune a griglia, in cui ogni cella è destinata a contenere il lavoro di un gruppo. Bisogna inoltre creare un css comune che contenga quanto richiesto da tutti i gruppi di lavoro
    • Librerie: YUI Grids. Consente di creare layout a grid tramite div e css cross browser.

Selection

  • Auto Complete
    • Problema: si deve inserire in un campo di testo un valore difficile da ricordare e che quindi si presta a errori di scrittura
    • Soluzione: fornire una textbox con supporto all’autocompletamento
    • Librerie: YUI Autocomplete
  • Calendar Picker
    • Problema: l’utente deve inserire o trovare un’informazione basata su data o su data range
    • Soluzione: fornire una textbox o due textbox che propongono di compilare automaticamente il campo sulla base della selezione effettuata dall’utente all’interno di un calendario grafico
    • Librerie: YUI Calendar

Rich Interation

  • Drag and Drop Modules
    • Problema: l’utente deve personalizzare il layout della propria pagina senza lasciare la pagina corrente
    • Soluzione: fornisci il drag dei moduli. Suggerisci dove poter effettuare il drop incorniciando le aree “droppabili” (pattern Drop Invitation)
    • Librerie: YUI Drag & Drop
  • Cursor Invitation
    • Problema: l’utente deve capire che può interagire con un oggetto della pagina
    • Soluzione: cambia il puntatore del mouse a seconda dell’operazione da eseguire
  • Tool Tip Invitation
    • Problema: l’utente deve capire cosa succederà al click del mouse su un oggetto che non sia un semplice link
    • Soluzione: fornisci un tooltip come “Clicca per modificare”
  • Hover Invitation
    • Problema: l’utente deve capire quale effetto scatenerà al “semplice” click del mouse su un oggetto che scatena un certo cambio di stato
    • Soluzione: fornisci un feedback immediato al passaggio del mouse sull’elemento mostrando un avvertimento su cosa succederà
  • Animate Transition
    • Problema: l’utente deve percepire gradualmente che un oggetto sta cambiando la sua relazione con lo spazio occupato nella pagina
    • Soluzione: fornisci un’animazione che gradualmente faccia cambiare la relazione con lo spazio
    • Librerie: YUI Animation
  • Dim Transition e Brighten Transition
    • Problema: l’utente deve percepire che un elemento della pagina è diventato di secondaria importanza, non disponibile o non editabile. Questo stato deve poi poter essere ripristinato, facendo segnalare la rinnovata attività dell’elemento
    • Soluzione: definisci lo stato dim e lo stato brighten. Il primo si verifica nel momento in cui diventa di secondaria importanza, mentre il secondo ripristina l’elemento
    • Librerie: YUI Animation
  • Collapse Transition e Expand Transition
    • Problema: l’utente deve poter comprimere un certo elemento che ritiene di secondaria importanza. Viceversa, l’utente deve poter espandere un elemento precedentemente compresso o ottenere dettagli su un certo elemento
    • Soluzione: comprimi o espandi utilizzando una rapida animazione (0.5 secondi)
    • Librerie: YUI Animation
  • Cross Fade Transition
    • Problema: l’utente deve percepire che la vista di un oggetto sta per essere rimpiazzata con una nuova vista
    • Soluzione: esegui il fade out della rimpiazzata, mentre la nuova entrerà in fade in
    • Librerie: YUI Animation
  • Fade In Transition e Fade Out Animation
    • Problema: l’utente deve percepire che un elemento è stato aggiunto o rimosso alla pagina
    • Soluzione: porta l’opacità di un oggetto dal 0% (100%) al 100% (0%) eventualmente attivando (disattivando) il focus sull’elemento
    • Librerie: YUI Animation
  • Self Healing Transition
    • Problema: l’utente deve percepire che un elemento all’interno di una lista è stato rimosso
    • Soluzione: esegui il fade out dell’elemento lasciando un buco sull’elemento rimosso. Successivamente esegui il pattern Slide.
  • Slide Transition
    • Problema: l’utente vuole percepire che un elemento non popup è stato aggiunto o rimosso dalla pagina e vuole percepire la collocazione spaziale del nuovo elemento all’interno della pagina
    • Soluzione: unisci il fade con lo slide
    • Librerie: YUI Animation
  • Spotlight
    • Problema: l’utente vuole percepire che un valore è cambiato nell’interfaccia
    • Soluzione: cambia colore dell’elemento istantaneamente in modo che sia visibile e ripristina al colore del background nel giro di un secondo
    • Librerie: YUI Animation

Social: Rating & Reviews

  • Architecture of a Review
    • Problema: una webapp deve presentare voti e recensioni con una varietà di informazioni aggiuntive
    • Soluzione: gli elementi devono essere raggruppati per Target Element (info sul prodotto da recensire), Review Element (voti + recensioni) e Form Element (giudizio utente). Ogni elemento può essere ulteriormente scomposto secondo la struttura riportata nel pattern
  • Rating an Object
    • Problema: un’utente vuole lasciare la propria opinione su un oggetto, con interruzioni minime a qualsiasi altro task su cui stà lavorando
    • Soluzione: mostra votazione pronta all’uso, combinando la tecnica Hover Invitation e invitando l’utente a votare in presenza di un valore vuoto
  • Vote to Promote
    • Problema: l’utente vuole promuovere un contenuto in una comunity in modo che tale contenuto abbia maggior rank e venga mostrato maggiormente, in una forma democratica
    • Soluzione: fornisci un meccanismo di voto “one-time” per l’utente; evidenzia gli elementi più votati mostrandone il numero di voti; fornisci il voto solo a seguito del consumo dell’elemento: alla fine dell’articolo o all’interno delle pagine esterne tramite snippet esterno da includere
  • Writing a Review
    • Problema: l’utente vuole condividere la propria opinione su un oggetto in modo più accurato di un semplice voto
    • Soluzione: fornisci una form di review che contenga info quantitative (voti vari) più info qualitative (recensione stessa) con associate delle linee guida e varie altre regole

Social: Reputation

Un sistema di reputazione viene definito nel momento in cui si ha necessità di garantire un maggiore utilizzo del servizio o una qualità nell’utilizzo. Il pattern The Competitive Spectrum fornisce delle linee guida per scegliere il tipo di sistema di reputazione da realizzare. Nel pattern, la community viene definita in termini di competitività: la combinazione degli obiettivi individuali, delle azioni che influiscono sui bisogni della comunità e del grado richiesto di confronto tra i membri. Le linee guida da adottare dipendono dal livello di competitività richiesta:

  • Caring
    • Obiettivo: i membri vogliono aiutare altri membri
    • Reputazione: consente di far identificare i membri “senior” all’interno della community
    • Strumenti: fai indossare una o più Identifying Labels al profilo di un utente in modo che i “bollini” guadagnati caratterizzino l’influenza del membro nella community
  • Collaborative
    • Obiettivo: i membri vogliono cooperare per raggiungere un obiettivo in gran parte condiviso
    • Reputazione: consente di far identificare i membri che sono fedeli alla community
    • Strumenti: decora il profilo utente con dei Named Levels che consentano di stabilire facilmente, senza ambiguità e senza alcuna accezione offensiva la fedeltà dei membri
  • Cordial
    • Obiettivo: i membri hanno i loro personali scopi, ma questi non vanno a contrastare con quelli condivisi dalla community
    • Reputazione: consente di far identificare i membri che hanno valori e interessi che la community reputa positivi
    • Strumenti: fornisci dati statistici correlati a un utente. In aggiunta, il pattern Top X consente di identificare facilmente gli utenti che producono interventi con maggiore qualità
  • Competitive
    • Obiettivo: i membri hanno gli stessi obiettivi, ma per raggiungerli devono competere tra di loro
    • Reputazione: consente di far mostrare alla community i progressi raggiunti da un certo utente per suscitare ammirazione (o invidia)
    • Strumenti: fornisci una facile comparazione decorando al profilo un “livello saiyan” agli utenti tramite dei Numbered Levels. Fornisci delle soddisfazioni tramite premi come “luccicanti badge da decorare al profilo” tramite l’uso dei Collectible Achievements, in cui i premi sono legati a diversi obiettivi di difficoltà crescente (da “hai completato il profilo” a “sei il più attivo nella community”)
  • Combative
    • Obiettivo: i membri condividono obiettivi opposti. Il raggiungimento dell’obiettivo di uno nega il raggiungimento dell’obiettivo dell’altro.
    • Reputazione: consente di far mostrare alla community i progressi e i regressi raggiunti da un certo utente per suscitare ammirazione o scherno
    • Strumenti: utilizza il pattern Points per rappresentare la performance di un utente e poterla incrementare o diminuire a seconda dei risultati. Fai un rank dei membri tramite o Top X o una Leaderboard (un Top X più aggressivo, che rappresenta ad esempio le top 3 della settimana)

A queste tecniche spesso si combina quella della Sign-in Continuity, in cui il voto di un utente viene proposto a patto di registrarsi o di autenticarsi, ma la registrazione non deve perdere il contesto (utile una dialog).

Posted in .net | 2 Comments »

xsd.exe: “cannot generate classes because no top-level elements with complex type were found”

Posted by Ricibald on 3rd October 2008

Allora la situazione è la seguente: ho questo xsd:

<xs:schema>
    <xs:element name="Elemento" type="xs:string">
    </xs:element>
</xs:schema>

A cui corrisponde ad esempio l’istanza

<Elemento>ciao</Elemento>

Se eseguo xsd.exe per generare la corrispondente classe ottengo il seguente errore:

Warning: cannot generate classes because no top-level elements with complex type were found.

Ricerche su internet su come risolvere il problema non sono state utili. Per questo scrivo questo post per riportare la mia soluzione al problema.
Ho semplicemente modificato l’xsd non alterandone la struttura in modo tale per cui xsd.exe non si “arrabbi”:

<xs:schema>
    <xs:element name="Elemento">
        <xs:complexType>
            <xs:simpleContent>
                <xs:extension base="xs:string" />
            </xs:simpleContent>
        </xs:complexType>
    </xs:element>
</xs:schema>

Questo è un formato che xsd.exe riconosce e tratta nella conversione.

Posted in .net | 5 Comments »

Sincronizzare Oggetti e Controlli Web/Windows In Un Colpo Solo

Posted by Ricibald on 14th August 2008

L’utilizzo dei controlli Web/Windows prevede due fasi:

  • il trasferimento dei dati dall’oggetto ai controlli corrispondenti
  • il trasferimento dei dati dai controlli all’oggetto corrispondente

Come dire:

void loadPage(TextBox txt_nome, Persona persona) {
    txt_nome.Text = persona.Nome;
}

void submitInfo(TextBox txt_nome, Persona persona) {
    persona.Nome = txt_nome.Text;
}

Questo è il classico approccio, ma, come tutte le cose meccaniche che si fanno in informatica, nasconde vari problemi:

  1. il tempo: scrivo le stesse cose due volte. Prima ad andare e poi a tornare…
  2. la consistenza: qualche proprietà potrei dimenticarla e l’oggetto potrebbe non essere aggiornato o potrebbe essere popolato con il valore sbagliato

La cosa ideale sarebbe avere un controllo Web/Windows che consentisse di realizzare il bind a due vie (le due fasi le fa il controllo per noi):

void bind(TextBoxBinded txt_nome, Persona persona) {
    txt_nome.Bind(persona.Nome);
}

Questa “sarebbe” la cosa ideale, ma non è praticabile: come conosciamo il set della proprietà “Nome”? In questo modo non possiamo:

Come passare per parametro i metodi get/set di una proprietà o di un campo, in modo da ottenere il bind a due vie?

Dopo ricerche su internet, miei ragionamenti e varie ed eventuali, le soluzioni possibili sono le seguenti:

  1. Approccio basato su Reflection: la soluzione più immediata, ma molto fragile: per ottenere un oggetto Property dobbiamo passare il nome della proprietà. Tale nome è una semplice stringa: non è pertanto tipizzato in alcun modo ed eventuali modifiche alla proprietà non ci verrebbero segnalate dal compilatore… Con questo approccio ogni modifica alla struttura ad oggetti diventerebbe letteralmente un incubo…
  2. Ogni proprietà implementa un interfaccia specifica: ad esempio IProperty<T>, dove T è il tipo della proprietà. Tale interfaccia avrà metodi get e set che il controllo Web/Windows riconosce. Il metodo bind accetterà quindi solo oggetti di tipo IProperty. L’approccio ha un ulteriore vantaggio: l’interfaccia può essere pensata come wrapper per contenere metainformazioni. Non solo quindi get o set, ma anche informazioni come editabilità, visibilità, nome, …
    Per abilitarla si devono modificare tutte le proprietà in modo che il tipo non sia ad es. String, ma IProperty<String>: questo però provoca una modifica nell’interfaccia della proprietà, e tutti i metodi che prima utilizzavano ad es. persona.Nome.ToUpper non funzioneranno più.
    Una soluzione eccellente stà nel realizzare degli operatori di conversione impliciti da IProperty<String> a String e nell’implementare le stesse identiche funzioni di String in un suo proxy: PropertyString, che implementa IProperty<String> e dichiara le stesse funzioni di String, rigirando tali funzioni al valore String che contiene.
    Con questo approccio si possono trattare a tutti gli effetti gli IProperty<T> come se fossero effettivamente dei T: i client ne possono essere inconsapevoli…
    Si hanno però svantaggi seguenti:

    • si deve aver accesso alla modifica delle proprietà delle classi. Se della struttura ad oggetti non abbiamo il controllo l’unica soluzione che ci rimane per continuare ad usare il nostro approccio è realizzare un Adapter. Una soluzione piuttosto noiosa…
    • si devono implementare tutte le classi che emulano i tipi primitivi: PropertyString, PropertyInt,  PropertyBool, … è anche vero che una volta fatto diventa patrimonio vostro e rimarrà lì con voi…
  3. Approccio basato su Reflection Tipizzata Fortemente: una splendida soluzione basata su LambdaExpression e Generics proposta da Nick Butler. Il segreto è utilizzare i Generics per dichiarare tramite LambdaExpression la proprietà da “bindare”, più o meno in modo simile alla OrderBy di LINQ:
    PropertyInfo prop = typeof(Persona).GetProperty("Nome"); // VECCHIO APPROCCIO: NON TIPIZZATO
    PropertyInfo prop = Instance<Persona>.GetProperty( o => o.Nome);

    Dove avremo:

    public static class Instance<TClass>
    {
        public static PropertyInfo GetProperty<T>(
                              Expression<Func<TClass, T>> m ) 
        {
            LambdaExpression lambda = m;
            Expression bodyExpr = lambda.Body;
            MemberExpression memberExpr = (MemberExpression)bodyExpr;
            MemberInfo memberInfo = memberExpr.Member;
            PropertyInfo propInfo = (PropertyInfo)memberInfo;
            return propInfo;
        }
    }

    Nonostante l’eleganza dell’approccio (specie se combinato con extension method (si veda l’implementazione di TextBoxFor nell’articolo di MSDN), questo ha un punto in svantaggio rispetto al precedente: consente di inserire solo metadati statici tramite attributi. Viceversa, il vantaggio rispetto alla precedente è che non è necessario modificare nulla del codice preesistente e non bisogna implementare i proxy per ogni tipo primitivo

Insomma, ora che sapete tutto manca “solo” di scrivere le vostre versioni custom dei controlli che consentono il bind (consiglio un Decorator…)

Posted in .net | No Comments »

Metafora Aritmetica per i Design Pattern

Posted by Ricibald on 13th August 2008

Dal blog di Adrian Florea (e quindi dal libro di Uwe Aßmann) mi ha colpito molto questa metafora aritmetica per esprimere il concetto dei design pattern:

The basic solution strategy of a design pattern is factoring:

a * b + a * d = a * (b + d)

Design patterns are the “binomial formulas” of software engineering!

Infatti quello che fanno i design pattern è fattorizzare, mettere in comune aspetti altrimenti ripetuti, come:

  • porzioni di codice in un metodo: Pattern Template
  • espressioni, salti condizionati: Pattern Strategy, State, Template, Factory, … in generale l’utilizzo sensato del polimorfismo (cioè il senso dei pattern)
  • metodi interi: spesso sintomo di cattiva assegnazione di responsabilità. I pattern consentono di individuare le responsabilità in modo da condividere gli stessi metodi nell’ambito della stessa classe padre

Un ottimo articolo che ne parla è quello di UGIdotNET.

Posted in .net | No Comments »

Applicazioni Distribuite: Proprietà Statiche vs Caching. Singleton rivisitato

Posted by Ricibald on 13th August 2008

Normalmente quando scriviamo codice capita di scrivere proprietà statiche: si pensi al pattern Singleton. Lo scopo di questo pattern è proprio assicurare la condivisione degli elementi, per ragioni di efficienza o di consistenza.

Il pattern funziona bene in contesti “normali”: è implementato mediante un campo/proprietà statico e quindi condiviso nell’ambito dell’applicazione Console/Windows che sviluppiamo. Se però ci spostiamo in contesti distribuiti il pattern non è in realtà così efficace:

In contesti distribuiti, i campi statici sono condivisi solo nell’ambito della stessa richiesta

Quindi in una nuova richiesta viene “dimenticato” il valore assunto degli stessi campi statici in altre richieste. I campi statici nascono invece con lo scopo di avere memoria globale di un’informazione. Quindi, come regola generale:

Il concetto di “campi static” dovrebbe sempre essere virtualizzato

Questo significa che, come possibile soluzione, la proprietà statica deve essere acceduta mediante una classe astratta CacheManager, che:

  1. è ottenibile come Singleton “puro”, cioè come proprietà statica a cui siamo tanto abituati. Avremo quindi un metodo CacheManager.GetInstance che restituisce tale istanza, condivisa solo nell’ambito della richiesta corrente
  2. consente metodi come SetPrinterSingleton o GetPrinterSingeton che restituiscono tali valori in cache
  3. l’implementazione di tale metodi varia a seconda del tipo concreto in uso

Il terzo punto significa che avremo diverse implementazioni, come StaticCacheManager o WebApplicationCacheManager, in cui la prima prenderà valori nel modo classico, tramite proprietà di istanza, mentre la seconda implementazione li prenderà dalle variabili “Application” del nostro web site.

La scelta di quale implementazione concreta utilizzare è demandata al momento in cui viene creato internamente l’istanza unica di CacheManager, che ci restituirà l’istanza corretta sulla base del principio “Dependency Injection: viene specificato cosa creare mediante file di configurazione e/o Reflection.

Molto comodo risulta infine utilizzare un Singleton con “shortcut”: invece di scrivere

public abstract class CacheManager {
    public static CacheManager GetInstance() {
        return ServiceLocator.GetCacheManager(); // usa reflection
    }
    public abstract Printer GetPrinterSingleton();
}

scriveremo una forma molto più compatta da utilizzare per il “client” della classe:

public abstract class CacheManager {
    public static CacheManager GetInstance() {
        return ServiceLocator.GetCacheManager(); // usa reflection
    }
    public static Printer GetPrinterSingeton() {
        return GetInstance().GetPrinterSingleton();
    }
    protected abstract Printer GetPrinterSingleton();
}

Che verrà quindi utilizzato in questo modo:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        // lock regione critica omessi per semplicita'
        Printer printer = CacheManager.GetPrinterSingleton();
        if(printer == null) {
            // il set restituisce la stessa istanza passata per parametro
            printer = CacheManager.SetPrinterSingleton(new Printer());
        }
        return printer;
    }
}

Si può anche esprimere in una forma succinta, utilizzando l’operatore di null coalescing di C#:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        return CacheManager.GetPrinterSingleton() ?? CacheManager.SetPrinterSingleton(new Printer());
    }
}

Si può infine completare l’implementazione garantendo la mutua esclusione utilizzando l’approccio di William Pugh:

public sealed class Printer {
    private Printer() {
    }
    public static Printer GetInstance() {
        return SingletonHolder.GetInstance();
    }

    class SingletonHolder {
        static SingletonHolder() { }
        static Printer GetInstance() {
            return CacheManager.GetPrinterSingleton() ?? CacheManager.SetPrinterSingleton(new Printer());
        }
    }
}

Attenzione: si potrebbe pensare che lo stesso concetto si applichi anche al pattern Flyweight. In realtà questo pattern (a differenza dell’esempio riportato nell’articolo in Wikipedia) non dovrebbe prevedere una mappa statica, ma a livello istanza, quindi la regola descritta è comunque soddisfatta.

In pratica la regola da seguire è questa:

Campi/proprietà statiche hanno senso solo nel contesto del pattern Singleton, e in tal caso il concetto di “campi static” deve essere virtualizzato tramite un CacheManager, a sua volta creabile tramite la versione “standard” del pattern Singleton

Posted in .net | No Comments »

La Precisione dei tipi “double”

Posted by Ricibald on 26th June 2008

I double vengono memorizzati in binario (in Java, C#, C++, C…). Questo potrebbe provocare problemi di precisione nei calcoli.

Si consideri infatti:

double d = 0D;
for (int i = 0; i < 10; i++)
{
   d += 0.10D;
} // d = 0.9999999999...

Il double d non vale 1, ma vale 0.9999… Perché? Consideriamo lo stesso calcolo riscritto in questo modo:

double d2 = 0D;
for (int j = 0; j < 8; j++)
{
   d2 += 0.125D;
} // d2 = 1.0

Stavolta d vale 1. Questo perché 0.125 vale 2-3: una potenza di 2. Significa quindi che è esprimibile come binario, e mantiene la precisione.

In generale questo non è un problema poiché la stampa di 0.9999999 produce a video 1. Ma si consideri il seguente codice:

double d3 = 1.0675;
d3 = d3 * 100;                              // d3 = 106.74999999....
Console.WriteLine(d3);                      // output = 106.75
double d4 = Double.Parse(d3.ToString());    // d4 = 106.75
d5 = 106.75;
d5 == d4;                                   // true
d5 == d3;                                   // false

Se ad esempio vogliamo testare se il nostro valore e’ pari a 2.0, questo semplice confronto potrebbe dar risultato negativo.
In questi casi la soluzione consiste nell’utilizzare il tipo decimal o creare una classe Currency che internamente usa il tipo long.

Posted in .net | 2 Comments »