martedì 23 novembre 2010

LINQ to XML - Recupero di un elemento decimal

C# 4.0
Nel leggere un valore di tipo decimal da un file XML mi si presenta un problema con i decimali in fase di casting. Ovvero il dato letto ha come separatore decimale il “.” mentre nella mia cultura è la “,” il risultato finale è quindi abbastanza evidente:
0.1   diventa 1

Questo è causato da un errato casting dell’elemento appena letto che nello specifico è:
XElement xElement = document.Root.Elements().Where(p => p.Name.LocalName.Equals("Value")).First();

decimal value = (decimal)xElement.Value;

invece si deve fare il casting dell’intero elemento XElement

decimal value = (decimal)xElement;

martedì 26 ottobre 2010

DotNet - CollectionChanged (Workaround)

C# 4.0

Una proprietà di tipo ObservableCollection<T> ( Plus) di un oggetto (Item) per notificare l’aggiunta, la cancellazione o l’aggiornamento di un elemento deve agganciare l’evento “CollectionChanged”, quindi:

_currentItem.Plus.CollectionChanged += CurrentPlusPropertyChanged;

private void CurrentPlusPropertyChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
              
        }

Si è visto però che per quanto riguarda l’aggiornamento questo evento non scatta e ad oggi non c’è una spiegazione ufficiale su questa anomalia. Bisogna quindi adottare questo workaround: agganciare l’evento PropertyChanged di ogni singolo elemento delle collection;

foreach (Plu plu in _currentItemProduct.Plus)
                    {
                        plu.PropertyChanged += CurrentPluPropertyChanged;
                    }

private void CurrentPluPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
             
        }

venerdì 8 ottobre 2010

IIS 6.0 - Page not found (404)

Problema
In una macchina con Windows Server 2003 e IIS 6.0 è stato installato il framework .NET 4.0. A questo punto è stato installato un servizio WCF realizzato con VS2010 (framework 4.0) ma richiamando la pagina Service.svc si aveva sempre l’errore 404 (Page not found).

Soluzione  
Aprire la console di amministrazione di IIS 6.0 e verificare che nelle “Estensioni servizio Web”, ASP.NET 4.0 sia impostato su “Consenti”.

martedì 21 settembre 2010

WPF - Aggiungere una colonna ad una DataGrid

C# 4.0
Definita una DataGrid (grdValues) in una maschera WPF, si vuole aggiungere a run-time un’altra colonna con l’opportuno binding ed assegnando anche uno stile definito sempre nello XAML.
Si può procedere quindi nel seguente modo:
DataGridTextColumn dataGridTextColumnOtherInfo = new DataGridTextColumn();
                       
Binding binding = new Binding("OtherInfo");
dataGridTextColumnOtherInfo.Binding = binding;

// Vengono ridimensionate tutte le colonne.
grdValues.Columns[0].Width = 120;
grdValues.Columns[1].Width = 120;
dataGridTextColumnOtherInfo.Width = 120;

// Stile già definto nello XAML della view.
dataGridTextColumnOtherInfo.EditingElementStyle = (System.Windows.Style)Resources["CellEditStyle"];
dataGridTextColumnOtherInfo.Header =”Other info”;

grdValues.Columns.Add(dataGridTextColumnOtherInfo);

martedì 14 settembre 2010

Serializzare un oggetto che implementa INotifyPropertyChanged

In alcuni contesti c’è la necessità di serializzare un oggetto che implementa l’interfaccia INotifyPropertyChanged. Per fare questo quindi non basta decorare la classe con l’attributo  [Serializable], bisogna anche aggiungere l’attributo  [field: NonSerialized] all’evento PropertyChanged.

A seguire un breve esempio:

    [Serializable]
    public class Department: INotifyPropertyChanged
    {
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Initializes a new instance of the <see cref="Department"/> class.
        /// </summary>
        public Department()
        {
        }

          }   

giovedì 9 settembre 2010

WPF - PropertyChanged is NULL (Part II) -> Implements IEquatable<>

Riprendendo il post “WPF - PropertyChanged is NULL” c’è da aggiungere che se si va in modifica di un oggetto, dopo aver caricato una maschera WPF (binding ad una proprietà di tipo Coupon) con all’interno alcuni controlli tra i quali anche un combo, non si riesce ad impostare il combo con il valore presente nella proprietà dell’oggetto.


In pratica il combo è binding alla proprietà CouponType dell’oggetto Coupon, CouponType a sua volta ha la proprietà impostata a “ValueFree” ma questa non viene visualizzata nel combo.

Il combo ha come ItemSource una lista (CouponTypes) di oggetti di tipo CouponType, il quale oggetto a sua volta implementa l’interfaccia INotifyPropertyChanged, quindi riepilogando:
public class Coupon : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}



private CouponType _couponType;

/// <summary>

/// "CouponType": il tipo di coupon: Unspecified, Free, ValueFree, Fidelity, Serialized;

/// </summary>

public CouponType CouponType

{

get { return _couponType; }

set

{

if (value == _couponType) return;

_couponType = value;

OnPropertyChanged("CouponType");

}

}



}





public class CouponType : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}



/// <summary>

/// Gets or sets the code.

/// </summary>

/// <value>The code.</value>

public CouponTypeEnum Code

{

get { return _code; }

set

{

if (value == _code) return;

_code = value;

OnPropertyChanged("Code");

}

}



/// <summary>

/// Gets or sets the description.

/// </summary>

/// <value>The description.</value>

public string Description

{

get { return _description; }

set

{

if (value == _description) return;

_description = value;

OnPropertyChanged("Description");

}

}



private CouponTypeEnum _code;

private string _description;

}

Bisogna quindi, per risolvere questa anomalia, implementare l’interfaccia IEquatable<CouponType> nell’oggetto CouponType nel seguente modo:
public class CouponType : INotifyPropertyChanged, IEquatable<CouponType>

{

#region IEquatable<CouponType> Membri di

bool IEquatable<CouponType>.Equals(CouponType other)

{

if (ReferenceEquals(null, other)) return false;

if (ReferenceEquals(this, other)) return true;

return Equals(other._code, _code) && Equals(other._description, _description);

}

public static bool operator ==(CouponType first, CouponType second)
{
return Equals(first, second);
}

public static bool operator !=(CouponType first, CouponType second)
{
return !Equals(first, second);
}
#endregion
}

martedì 7 settembre 2010

WPF - Il default di un combo non viene impostato correttamente (PropertyChanged is NULL)

C# 4.0

Si verifica la seguente anomalia: dopo aver caricato una maschera WPF (binding ad una proprietà di tipo Coupon) con all’interno alcuni controlli tra i quali anche un combo, non si riesce ad impostare il combo con un determinato elemento.


Il combo ha come ItemSource una lista (CouponTypes) di oggetti di tipo CouponType, il quale oggetto a sua volta implementa l’interfaccia INotifyPropertyChanged, quindi riepilogando:


public class Coupon : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

private CouponType _couponType;

/// <summary>
/// "CouponType": il tipo di coupon: Unspecified, Free, ValueFree, Fidelity, Serialized;
/// </summary>

public CouponType CouponType

{

get { return _couponType; }

set

{

if (value == _couponType) return;

_couponType = value;

OnPropertyChanged("CouponType");

}

}



}


public class CouponType : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

/// <summary>
/// Gets or sets the code.
/// </summary>
/// <value>The code.</value>

public CouponTypeEnum Code

{

get { return _code; }

set

{

if (value == _code) return;

_code = value;

OnPropertyChanged("Code");

}

}

/// <summary>
/// Gets or sets the description.
/// </summary
/// <value>The description.</value>

public string Description

{

get { return _description; }

set

{

if (value == _description) return;

_description = value;

OnPropertyChanged("Description");

}

}
private CouponTypeEnum _code;

private string _description;

}


Al caricamento della maschera si crea un nuovo oggetto di tipo Coupon e si carica la collection di _couponTypes:

_coupon = new Coupon();
_couponTypes = ContextCommonService.LoadCouponTypes();

Quindi per impostare l’elemento di default si usa la seguente lambda expression :

Coupon.CouponType = CouponTypes.Find(item => item.Code == CouponTypeEnum.ValueFree);

Il risultato è che il combo non viene impostato correttamente perché (in debug) si nota che PropertyChanged è sempre NULL.

Alla fine si è quindi capito che PRIMA bisogna caricare la collection _couponTypes e DOPO inizializzare l’oggetto Coupon:

_couponTypes = ContextCommonService.LoadCouponTypes();
_coupon = new Coupon();

venerdì 3 settembre 2010

LINQ to Entities - "L'espressione LINQ specificata contiene riferimenti a query associate a contesti diversi"

C# 4.0


Una delle possibili cause di questa eccezione è quella di inserire direttamente nella lambda expression della clausola Where un oggetto var. Per ovviare questo quindi basta assegnare il suo valore ad una variabile.


if (!string.IsNullOrEmpty(typeCode))

{

// Recupero del tipo base della promozione.

var queryType = from pt in orangeRepositoryEntities.PromotionType

where pt.Code.Equals(typeCode)

select pt.BasePromotionType;


query = query.Where(p => p.PromotionType.Contains(queryType.First()));

string basePromotionType = queryType.First();
query = query.Where(p => p.PromotionType.Contains(basePromotionType));

}



if (!string.IsNullOrEmpty(code))

query = query.Where(p => p.Code.Contains(code));

if (!string.IsNullOrEmpty(salesDriveCode))

query = query.Where(p => p.SalesDriveCode.Contains(salesDriveCode));



query = query.Where(p => p.StartValidity >= fromStartValidity.Date && p.StartValidity <= toStartDate);

query = query.Where(p => p.StopValidity >= fromStopValidity.Date && p.StopValidity <= toStopDate);



foreach (var promotion in query)

{



}

venerdì 27 agosto 2010

LINQ to Entities - Condizione Where con lambda expressions

C# 4.0


In una query LINQ si presenta talvolta la necessità di aggiungere in modo condizionato uno o più parametri di ricerca in modo tale da creare una clausola Where pertinente.

Si può quindi procedere nel seguente modo utilizzando delle lambda expressions, dove in questo caso i parametri di ricerca sono rappresentati da code e salesDriveCode :

using (OrangeRepositoryEntities dcOrangeRepository = new OrangeRepositoryEntities())

{

var query =

from p in dcOrangeRepository.Promotion

select p;



if (!string.IsNullOrEmpty(code))

query = query.Where(p => p.Code.Contains(code));



if (!string.IsNullOrEmpty(salesDriveCode))

query = query.Where(p => p.SalesDriveCode.Contains(salesDriveCode));



foreach (var promotion in query)

{



}

}

WPF - Larghezza automatica di un TabControl

C#  4.0

In una maschera WPF si vuole inserire un TabControl che abbia la larghezza impostata sulla stessa larghezza della maschera stessa.


Impostare quindi gli attributi Margin e Width dell’elemento XAML TabControl, nel modo seguente:

<TabControl Grid.Row="1" Name="tabMain" Margin="0,0,0,0" Width="Auto">



</TabControl>


Importante poi, NON impostare l’attributo HorizontalAlignment per non compromettere il corretto allineamento del controllo.

giovedì 26 agosto 2010

SQL Server 2005 - TSQL - Aggiornamento di una tabella tramite sub-query

Si deve aggiornare un campo di una tabella prendendo il valore da un’altra, dove il punto di contatto tra le due è rappresentato da un altro campo in comune tra la seconda ed una terza tabella.


In questo esempio il campo AuxiliaryCode della tabella FinalPromotion deve essere aggiornato con il valore del campo Code della DocumentCoupon che non ha FK verso FinalPromotion ma verso Document che a sua volta è legata a FinalPromotion.

Come sempre lo statement T-SQL che segue, potrebbe valere più di mille parole:



UPDATE FP

SET FP.AuxiliaryCode = A.Code

FROM FinalPromotion FP

INNER JOIN Document D

ON FP.DocumentGuid = D.Guid

INNER JOIN

(SELECT D.UpperDocumentGuid AS UDG, DC.Code FROM DocumentCoupon DC

INNER JOIN Document D

ON DC.Id = D.CouponId) A

ON D.UpperDocumentGuid = A.UDG

WHERE D.UpperDocumentGuid = 'b9464a81-b83c-4ce4-8758-b0e632240c2b'

mercoledì 25 agosto 2010

NHibernate - Gestione della concorrenza

C# 2.0

In alcune applicazioni c’è la necessità di gestire un corretto accesso ai dati, evitando eventuali problemi di concorrenza. Tipico esempio potrebbe essere quello delle gestione degli ordini, dove due utenti stanno lavorando contemporaneamente sullo stesso ordine e non si vuole rischiare di sovrascrivere informazioni dell’uno o dell’altro operatore.


Con NHibernate quindi è abbastanza semplice gestire tale esigenza, agendo nel seguente modo:

1. Aggiungere nella tabella degli ordini il campo DataUpdate di tipo datetime.

2. Modificare il file di mapping della tabella interessata, aggiungendo l’elemento timestamp subito dopo quello dell’identificativo della tabella, ed è importante che sia in questa posizione, prima della definizione delle altre proprietà affinché tutto funzioni correttamente.



<id name="Id" type="Guid">

<column name="Id" length="16" sql-type="uniqueidentifier" not-null="true" unique="true" index="PK_Ordine"/>

<generator class="guid" />

</id>

<timestamp column ="DataUpdate" name ="DataUpdate"/>

<property name="IdAssegnazione" type="Guid">

<column name="IdAssegnazione" length="16" sql-type="uniqueidentifier" not-null="false"/>

</property>



3. Gestire l’eccezione di tipo StaleObjectStateException nel metodo di aggiornamento dei dati.

try

{

using (ISession s = SessionController.Instance.NHSession)

{

s.SaveOrUpdate(ordine);

s.Flush();



}

}

}

}

catch (NHibernate.StaleObjectStateException ex)

{

MessageBox.Show("Attenzione! L’ordine è già stato modificato da un altro utente.\rControllare il file di Log. ", "Gestione Ordini", MessageBoxButtons.OK, MessageBoxIcon.Warning);



// Eventuale scrittura su file di log



}

catch (Exception ex)

{



}

finally

{



}

martedì 24 agosto 2010

WPF - Anteprima di stampa con XPSDocument

C# 4.0

Questo metodo esegue l’anteprima di stampa di una stringa o di un oggetto visual, utilizzando l’oggetto XPSDocument.

///
/// Prints the preview.
///

///
The content.
public static void PrintPreview(string content)
{
byte[] bytes = Encoding.UTF8.GetBytes(content);
MemoryStream memoryStream = new MemoryStream(bytes.Length);
Package package = Package.Open(memoryStream, FileMode.Create, FileAccess.ReadWrite);

Uri uri = new Uri("pack://PackTemporaryUri.xps");
if (PackageStore.GetPackage(uri) == null)
PackageStore.AddPackage(uri, package);

XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.NotCompressed, uri.AbsoluteUri);
FixedDocument fixedDocument = new FixedDocument();
PageContent pageContent = new PageContent();

FixedPage fixedPage = new FixedPage { Background = Brushes.White, Width = 96 * 8.5, Height = 96 * 11 };

TextBlock tbTitle = new TextBlock { Text = content };
FixedPage.SetLeft(tbTitle, 96 * 0.75); // left margin
FixedPage.SetTop(tbTitle, 96 * 0.75); // top margin
fixedPage.Children.Add(tbTitle);

((IAddChild)pageContent).AddChild(fixedPage);

fixedDocument.Pages.Add(pageContent);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(fixedDocument);

DocumentViewer documentViewer = new DocumentViewer
{
Document = xpsDocument.GetFixedDocumentSequence()
};
xpsDocument.Close();

// Viene create dinamicamente una window per la visualizzazione dell'anteprima di stampa.
Window window = new Window
{
Title = "Print Preview",
WindowStartupLocation = WindowStartupLocation.CenterScreen
};

Uri uriIcon = new Uri("pack://application:,,,/Admin.Console;component/Content/Print.png");
window.Icon = BitmapFrame.Create(uriIcon);

Grid grid = new Grid();
grid.Children.Add(documentViewer);
window.Content = grid;

window.ShowDialog();
}

///
/// Prints the visual preview.
///

///
The content.
public static void PrintPreview(Visual content)
{
MemoryStream memoryStream = new MemoryStream();
Package package = Package.Open(memoryStream, FileMode.Create, FileAccess.ReadWrite);

Uri uri = new Uri("pack://PackTemporaryUri.xps");
if (PackageStore.GetPackage(uri) == null)
PackageStore.AddPackage(uri, package);

XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.NotCompressed, uri.AbsoluteUri);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(content);

DocumentViewer documentViewer = new DocumentViewer
{
Document = xpsDocument.GetFixedDocumentSequence()
};
xpsDocument.Close();

// Viene create dinamicamente una window per la visualizzazione dell'anteprima di stampa.
Window window = new Window
{
Title = "Print Preview",
WindowStartupLocation = WindowStartupLocation.CenterScreen
};

Uri uriIcon = new Uri("pack://application:,,,/Admin.Console;component/Content/Print.png");
window.Icon = BitmapFrame.Create(uriIcon);

Grid grid = new Grid();
grid.Children.Add(documentViewer);
window.Content = grid;

window.ShowDialog();
}

NHibernate - a different object with the same identifier value was already associated with ...

C# 2.0

Questo messaggio d’errore è molto probabilmente dovuto ad un errore di mappatura, specialmente nella cancellazione a cascata: cascade="all-delete-orphan", nel senso che questo attributo non dovrebbe essere presente.

NHibernate - Combo SelectedItem binding

C# 2.0

Non sempre assegnando direttamente un oggetto alla proprietà binding SelectedItem di un combo, questo viene sincronizzato correttamente: per ovviare a questo inconveniente bisogna esporre l’Id dell’oggetto e valorizzare la proprietà SelectedValue del combo a questo Id.

Inoltre se in un combo, il binding impostato su SelectedItem sembri non sentire il change della base dati agganciata, verificare che la tabella di riferimento nel file di mapping abbia impostato il lazy a false:

<many-to-one name="TipoNota" lazy ="false" class="Pratiche.DataLayer.Database.Entities.TipoNota, Pratiche.DataLayer">
<column name="TipoNotaId" length="4" sql-type="int" not-null="false"/>
</many-to-one>

NHibernate - Oggetti su istanze diverse

C# 2.0

A volte gli oggetti appartengono ad istanze diverse, e pur essendo dello stesso tipo possono risultare diversi, per questo quindi occorre fare l’override dell’oggetto in questione:

public override bool Equals(object obj)
{
if (obj != null && obj.ToString() != string.Empty)
{
return _id == ((TipoNota)obj).Id;
}
else
{

return base.Equals(obj);
}
}

NHibernate - Collezioni di tipo Bag e Set

C# 2.0

Le collezioni di tipo Bag permettono l’inserimento di valori duplicati, questo quindi può comportare dei problemi in fase di Refresh, in pratica vengono duplicati i valori all’interno della collection. A questo punto è consigliabile una collezione di tipo Set, la quale può anche essere ordinata:

<set name="PraNoteScadenzes" order-by="DataScadenza desc" lazy ="false" inverse ="true" cascade ="all-delete-orphan">
<key column="PraId"/>
<one-to-many class="Pratiche.DataLayer.Database.Entities.NoteScadenze, Pratiche.DataLayer"/>
</set>

Nella classe poi, la proprietà deve essere codificata così:

public virtual Iesi.Collections.ISet PraNoteScadenzes
{
get
{
if (_praNoteScadenzes == null)
{
_praNoteScadenzes = new SortedSet();
}
return _praNoteScadenzes;
}
set { _praNoteScadenzes = value; }
}

NHibernate - "Unexpected row count: 0; expected: 1"

C# 2.0

Con questo errore verificare nel file di mapping che:

1. L’attributo “class” dell’elemento “generator”, nel caso di Guid deve essere <generator class="guid" />

2. Togliere anche l’attributo unsaved-value="null".

Inoltre togliere l’eventuale default nel campo chiave del DB SQL.

SQL Server 2005 - Remote connections

Può succedere che da un WebService non si riesca ad accedere a SQL Server.
Controllare quindi, tramite SQL Server 2005 Surface Area Configuration, l'impostazione di "Remote Connections" del gruppo "Database Engine". Le opzioni devono essere impostati su: "Local and remote connections" e "Using TCP/IP only".

Presentazione

Molto spesso nel quotidiano svolgimento della mia professione ho trovato informazioni utilissime in diversi blog sparsi per il mondo, talvolta scovati nei siti più remoti della rete grazie a “potenti motori di ricerca”.


Così mi son posto il quesito, che qualcun altro a sua volta, potesse effettuare lo stesso tipo di ricerca, così ecco questo blog: che non ha nessuna pretesa di insegnare niente a nessuno o distribuire il “verbo”. Ha solamente lo scopo di mettere su “carta” alcune soluzioni pratiche ai problemi che ci affliggono giorno per giorno.

Diego Parolin aka Didacus