asp.net comments edit

È un po' di tempo che non faccio post sul blog, ma oggi con Stefano e GVNN, stavamo cercando di risolvere un problema in una situazione tipo la seguente:

UserControl normalissimo con all'interno un semplice UdatePanel che al click esegue delle semplici operazioni di select ed insert sul database.
E fin qui nulla di particolare se non fosse per il fatto che al primo colpo veniva eseguito il codice perfettamente, mentre al secondo giro la chiamata asincrona non andava più e veniva mostrato un messaggio di errore tipo il seguente:

Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machinekey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

Ovviamente sia EnableViewStateMac che ViewStateEncryptionMode sono impostati su false ed eravamo in locale quindi niente cluster.

Andando a guardare con fiddler le chiamate ci siamo accorti che al primo giro veniva effettuata una chiamata ad un url, mentre al secondo l'url era differente, o meglio non era più il RawUrl ad essere chiamato ma l'url non riscritto da un HttpModule interno.

Indagando un po' più a fondo ci siamo accorti che all'interno dell'evento pageLoading della classe Sys.WebForms.PageRequestManager veniva riscritto l'url da chiamare.

Il problema è risolvibile aggiungedo questo 4 righe di JavaScript:

Sys.Application.add_load(function()
{
var form = Sys.WebForms.PageRequestManager.getInstance()._form;
form._initialAction = form.action = window.location.href;
});

 

Devo dire che ormai non mi stupisco più quando trovo stranezze sul Framework Ajax 1.0 che ritengo più una versione 0.8, e aggiungo questo comportamento all'elenco delle cose che spero Fixate nella prossima Release (Encoding non UTF-8, WebPart, ecc) del Framework ASP.NET AJAX.

Ciauz

 

asp.net comments edit

è un po' che volevo fare questo post, ma causa deadline vicinissima non ho avuto il tempo.

Nell'ultimo periodo ho dovuto sviluppare parecchi Custom Control ed ho fatto uso abbastanza spinto del Framework Ajax di Microsoft riscontrando anche diversi bug (e il romano lo sa bene), ma mi sono imbattuto in una situazione un po' "strana" che non è giusto definirla bug.

Nel realizzare un Custom Control che ereditava da Composite Control (che a sua volta eredita da WebControl) ho avuto la necessità di rimuovere il tag iniziale che questo mi creava, per precisione un span.
Ora tramite la proprietà TagKey del WebControl era possibile specificare un tag di uscita diverso dallo span, ma a me non andava bene e non lo volevo proprio.
Da qui è nata l'idea di effettuare l'override dei metodi RenderBeginTag e RenderEndTag, commetanto la chiamata al base del metodo stesso.
Purtroppo questo ha scatenato un'improvvisa incompatibilità dei miei eventi contenuti all'interno del Composite Control nei confronti del framework ajax, in quanto qualsiasi evento sollevato da un linkbutton (ma è uguale per qualsiasi altro Control) presente all'interno del mio controllo, effettua un PostBack Sincrono ignorando la proprietà ChilderAsTrigger dell'update panel impostata su true, e, anche forzando il trigger a mano il postback rimane sempre asyncrono.
L'unica soluzione che ho trovato è stata quella di recuperare lo ScriptManager della pagina dal controllo e registrare a mano i controlli figli che scatenano l'evento.

Il codice mostra la procedura.

ScriptManage sm = ScriptManager.GetCurrent(Page); 
LinkButton myLinkButton = new LinkButton(); 
if(sm != null) 
{ 
 sm.RegisterAsyncPostBackControl(myLinkButton); 
}

In questo modo le chiamate sono tornate asincrone.
Questa operazione deve essere fatta prima del metodo PreRender, dopo di che non è più possibile registrare il controllo allo ScriptManager.

Ora in quel periodo lavoravo molto di fretta, magari non è la soluzione migliore o il metodo migliore per rimuovere il tag dal Composite Control, ma magari se qualcuno ha avuto lo stesso problema gli faccio risparmiare del tempo :D.

Ciauz

 

 

various comments edit

Questo week-end mi sono deciso, ho fatto il format del pc fisso che ho a casa e ho montato Windows Vista, tutto fantastico, Aero va alla grande, il pc schizza, ma per qualche motivo che solo lui può sapere il messenger non va, mi restituiva sempre errore 81000306.

Per risolvere basta aprire il prompt del DOS e digitare:

netsh int tcp set global autotuninglevel=disabled

fatto ciò il messenger comincia a funzionare :D
Bah, eppure lo ho fatto un sacco di volte, ma questa volta proprio non andava.

BAFANDO !!!

 

.net comments edit

Ormai è un po' di tempo che sto realizzando una piccola applicazione con architettura a tre livelli, e mi si è posto subito un problema, ossia gestire una transazione tra più entity.

Per capirci, io ho la mia entity Fattura che al suo interno ha una collection di righeFattura, la entity Cliente e la entity Fornitore (direi che per l'esempio bastano).
Quando devo persistere la mia entity (con le rispettive sottoentity) devo avere la certezza che tutto sia andato a buon fine e nel caso si verifichi un errore si deve ripristinare la situazione originale e avvertire l'utente che qualcosa è andatao storto.

Per risolvere la il problema ho deciso di implementare questa soluzione:

  1. Ogni classe di persistenza del DAL deve esporre un'interfaccia ITransactionable;
  2. Il DAL deve esporre una classe che implementa un'interfaccia ITransactionManager;

 

I seguenti diagrammi mostrano le interfacce nel dettaglio:

 


 

Ora all'interno delle nostre classi di persistenza dove esponiamo ITransactionable prima della chiamata di update, delete, ecc dobbiamo verificare se l'oggetto transazionale è instanziato oppure no, lo possiamo fare con una semplice if.

public bool Update(Fattura item)
{
int rowsAffected;

if (Transaction == null)
{
rowsAffected = SqlHelper.ExecuteNonQuery(
ProviderHelper.ConnectionString,
CommandType.StoredProcedure,
"sp_Fattura_Update", CreateParameter(true, item));
}
else
{
rowsAffected = SqlHelper.ExecuteNonQuery(
Transaction,
CommandType.StoredProcedure,
"sp_Fattura_Update", CreateParameter(true, item));

}

if (rowsAffected <= 0)
throw new Domain.Exception.ConcurrencyException(Resource.Res.GetString("ConcurrencyException"));
else
return true;
} 

 

 

Transaction non è l'implementazione del'interfaccia ITransactionable ma un suo cast a SqlTransaction.

Il nostro Transaction Manager presente nel DAL sarà pittusto semplice quindi una roba tipo questa: 

private SqlConnection conn;
private SqlTransaction transaction;
private List coll;


public TransactionManager()
{
coll = new List();
}

 

public void Add(ITransactionable transactionObject)
{
coll.Add(transactionObject);
}


public void Remove(ITransactionable transactionObject)
{

transactionObject.ExternalTransaction = null;
coll.Remove(transactionObject);
}


public void StartTransaction()
{
conn = new SqlConnection(ProviderHelper.ConnectionString);
conn.Open();
transaction = conn.BeginTransaction();

for (int i = 0; i < coll.Count; i++)
coll[i].ExternalTransaction = transaction;


}


public void Commit()
{
transaction.Commit();
}


public void Rollback()
{
transaction.Rollback();
}

public void Dispose()
{
if(conn.State == ConnectionState.Open)
conn.Close();
for (int i = 0; i < coll.Count; i++)
{
coll[i].ExternalTransaction = null;
coll.RemoveAt(i);
}
transaction.Dispose();
} 

 

Dal Business Layer vado ad utilizzare il Transaction Manager in questo modo: 

using (ITransactionManager manager = AbstractFactory.ProviderFactory.TransactionManager)
{

IFatturaDataProvider aProvider = AbstractFactory.ProviderFactory.FatturaProvider;
IFornitoreDataProvider fProvider = AbstractFactory.ProviderFactory.FornitoreProvider;
IClienteDataProvider cProvider = AbstractFactory.ProviderFactory.ClienteProvider;

 

manager.Add(provider);
manager.Add(fProvider);
manager.Add(cProvider);

manager.StartTransaction();

try
{
provider.Update(item);
cProvider.Update(item.Cliente);
fProvider.Update(item.Fornitore);

manager.Commit();
}
catch
{
manager.Rollback();
throw;
}

}

 

Secondo voi l'implementazione è quella corretta?

 

 

 

.net comments edit

Volevo farne uno script ma mi sembrava troppo lungo, ma troppo corto per un articolo, insomma non sapevo dove metterlo e alla fine ho deciso di piazzarlo qui, in caso il boss mi dirà.

Cmq sono un'insieme di metodi che comprimono una cartella e tutto il suo contenuto in un unico file zip mantenendo la struttura.

public static void ZipFolder(string path, string path2Save, string name, bool recursive) 
{ 
Crc32 crc = new Crc32(); if (String.IsNullOrEmpty(name)) 
name = new DirectoryInfo(path).Name; if (!name.EndsWith(".zip")) 
name += ".zip" if (!Directory.Exists(path2Save)) 
Directory.CreateDirectory(path2Save); try 
{ 
using (ZipOutputStream s = new ZipOutputStream(File.Create(path2Save + name))) 
{ 
s.SetLevel(6); 
string basepath = path.Replace(new DirectoryInfo(path).Name, String.Empty); if (basepath.EndsWith("\\\\")) 
basepath = basepath.Replace("\\\\", "\\"); 
AddToZip(path, s, crc, basepath, recursive); s.Finish(); 
s.Close(); 
} 
} 
catch (Exception e) 
{ 
File.Delete(path2Save + name); 
throw e; 
} 
} 
 private static void AddToZip(string path, ZipOutputStream s, Crc32 crc, string basepath, bool recursive) 
{ 
foreach (string srcFileName in Directory.GetFiles(path)) 
{ 
string filepath = path + @"\" + new DirectoryInfo(srcFileName).Name; 
AddFile(s, filepath, crc, basepath); 
} foreach (string strSrcSubDirectory in Directory.GetDirectories(path)) 
AddFolder(s, strSrcSubDirectory, crc, basepath); if (recursive) 
foreach (string strSrcSubDirectory in Directory.GetDirectories(path)) 
{ 
path += @"\" + new DirectoryInfo(strSrcSubDirectory).Name; 
AddToZip(strSrcSubDirectory, s, crc, basepath, recursive); 
} 
} private static void AddFile(ZipOutputStream s, string file, Crc32 crc, string basepath) 
{ 
using (FileStream fs = File.OpenRead(file)) 
{ 
byte[] buffer = new byte[fs.Length]; 
fs.Read(buffer, 0, buffer.Length); ZipEntry entry; if (string.IsNullOrEmpty(basepath)) 
entry = new ZipEntry(file.Replace(basepath, String.Empty).ToLower()); 
else 
entry = new ZipEntry(file); entry.DateTime = DateTime.Now; 
entry.Size = fs.Length; crc.Reset(); 
crc.Update(buffer); entry.Crc = crc.Value; 
s.PutNextEntry(entry); 
s.Write(buffer, 0, buffer.Length); 
} 
} private static void AddFolder(ZipOutputStream s, string file, Crc32 crc, string basepath) 
{ 
ZipEntry entry; if (string.IsNullOrEmpty(basepath)) 
entry = new ZipEntry(file.Replace(basepath, String.Empty).ToLower()); 
else 
entry = new ZipEntry(file); entry.DateTime = DateTime.Now; crc.Reset(); entry.Crc = crc.Value; 
s.PutNextEntry(entry); 
}

Va sistemata un po, per gestire bene le eccezzioni e perchè l'ho solo provata al volo ma sicuramente il codice può essere ottimiccato, però in linea di massima funziona

 

orm comments edit

Orami in tutte le mie applicazioni realizzo un Domian Model sia che queste siano complesse che non.

Ora Fowler ci dice:

An object model of the domain that incorporates both behavior and data.

ed è tutto giustissimo il Domain sarà un contenitore di dati disconnesso e, nel caso avere delle implementazioni interne, ma non dovrebbe scostarsi di più da ciò.

Un'ulteriore caratteristica che dobbiamo rispettare è che il Domain deve essere quello a prescindere dal tipo di applicazione che si andrà a realizzare (Web Application, WinForm, Mobile, ecc) e dal tipo di strato dati, che sia NHibernate o un provider custom, xml o quel che volete.

Detto questo mi chiedo:
ma è giusto implementare nel domain l'interfaccia INotifyPropertyChanged dato che un'applicazione ASP.NET non sa che farsene, inoltre è giusto implementare Collection<T> per le nostre collezioni quando per un DataBinding su WinForm sarebbe più giusto utilizzare BindingList<T>???

Ora dato che ogni tipo di applicazione richiede delle caratteristiche diverse per il proprio Domain forse è il caso che queste tipo di implementazioni vengano fatte nella UI dell'applicazione e non nel Domain rischiando di avere implementazioni "pesanti" che non verrano mai utilizzate perchè il tipo di applicazione su cui gira il Domain non le richiede.

Per esempio se andiamo ad analizzare BindingList<T> e Collection<T> entrambe implementano l'interfaccia IList<T>.

Quello che mi chiedo io non è il caso che nel Domain per gestire le collection si utilizzi una sintassi del tipo:

 
 IList roles = new List<T> 

e l'interfaccia INotifyPropertyChanged venga implementata nel UI che la richieda e non nel Domain in modo da avere un domain "perfetto" per ogni tipo di applicazione??

Una parte di questo mio dubbio è risolvibile utilizzando un oggetto per WinForm che come dice il sito stesso:

A library to facilitate DataBinding in .NET Windows.Forms.

dimenticavo il Link.

 

orm comments edit

Oggi, dopo un lungo periodo in cui mi sono sempre rifiutato di studiare Nhibernate, il crad è riuscito a convicermi. Personalmente non mi è mai piaciuta l'idea di far fare tutto ad un framework senza avere il controllo su molte cose, ma davanti a fatto compiuto devo alzare le mani. :D (qui il crad se la ride).

Piccola premessa per chi non sa cos'è Nhibernate:

Nhibernate è un porting di una libreria Java Hibernate in ambiente .net. Questa libreria ha il compito di agevolare l'utente nella memorizzazione di informazioni in un Database, evitandoci il "problema" di dover scrivere query e "limitando" il nostro compito nello gestire le sole entity. Da queste parole sembra che faccia tutto lui , beh quasi :P.

Detto questo passiamo alla mia esperienza:

Il primo impatto è stato molto brutto, il passaggio da un sistema "classico" è parecchio brusco ma una volta capito come lavora non è difficile da usare (da quel poco che ho potuto vedere).

Se andiamo a pensare quanto possa essere difficile creare un buon DAL (Data Access Layer), quindi "Lazy Load", "Unit of Work", ecc, può venir utile tenere in considerazione Nhibernate.

Premetto che praticamente lo uso solo da un giorno, però ad un primo impatto lo potrei descrivere come molto produttivo.

Gestisce lui lo "Unit of Work", "Lazy Load", e inserimenti o modifiche di entity a cascata; basti pensare ad una entity Fattura che ha al suo interno una Entity cliente, passando a Nhibernate l'entity fattura, lui provedderà automaticamente a inserire la fattura e il cliente nel caso non sia presente nel Database mentre in una situazione normale avremmo dovuto creare 2 query, n parametri, interrogare il nostro DataHelper, ecc, ora tutto questo ci viene fatto con un risparmi di risorse incredibile.

Altra bella caratteristica è quella di poter cambiare tipo di database a runtime modificando una righa nel web.config oppure di dire a Nhibernate, "...guarda sono in un'applicazione asp.net usa la cache" e lui lo fa per noi :P

In termini prestazionali non so ancora quanto possa incidere, ma da quanto mi dicono se usato bene è molto performate.

Con questo post non voglio dire che dobbiamo sempre usare Nhibernate o che sia il metodo migliore, ci sono persone più adatte a rispondere a questo tipo di interrogativo (Ricky o Andrea per citarne due :P ) , ma vale la pena di perderci un attimo tempo perchè può venir utile.

Per chi vuole iniziare consiglio il libro in pdf stampabile e scaricabile qui e un tutorial che mostra come iniziare qui.

Successivamente (appena capisco un po' meglio cosa accade in "background") posto un codice che mostra come iniziare ad utilizzarlo.

.net comments edit

Che fanno insieme io, il GDI+ del Compact Framework e un Pocket Pc rinchiusi in una stanza per due giorni??

Beh creiamo componenti !!!!
A parte gli scherzi, come detto in un precedente post, ultimamente mi trovo a dover realizzare un sofware su pocket pc, che esegue dei calcoli e mostra dei dati a video.
Ora il problema principale del software è l'interfaccia grafica che era bruttina, causa i "poveri" componenti per pocket pc presenti nel framework.

Sbirciando in rete, non è che ci siano molte ditte che vendono componenti per pocket pc e quelle poche che lo fanno, non hanno grandi prodotti, almeno per le mie esigenze; di conseguenza mi sono dovuto armare nel crearmi componenti custom per le mie esigenze.

Beh pensavo fosse molto più difficile mentre la parte del framework che riguarda il GDI+ è molto potente e ben sviluppata, per ultimo, ma non meno importante, è facile da usare :-).

I componenti creati sono stati 3 diversi tipi di grafici, i pulsanti sfumati e i panel sfumati.
Per i pulsanti ho creato l'opzione di poter inserire un immagine e di selezionare il colore per "sfondarla" per creargli la trasparenza. Ovviamente per avere un buon risultato bisogna preparare bene le immagini (non come ho fatto io).
Tutti i componenti sono parametrizzati e i loro valori sono quasi tutti gestibili dalla finestra delle proprietà di Visual Studio, senza avere la necessità di far scrivere del codice all'utilizzatore.

I risultati li vedete sotto con delle parti un po' sfocate per tutelare il cliente.

C'è anche con il grafico a barre


Sono tutti in fase di ultimazione per poter supportare le diverse risoluzioni dei palmari disponibili, ma sembra che funzionino :)

Personalmente ritengo che il Compact Framework è ancora un po "povero" (per far certe cose bisogna ingegnarsi un po') però la strada è sicuramente quella buona e di materiale in rete se ne trova parecchio.

Ovviamente consigli e critiche sono ben accette. !!!

 

asp.net comments edit

Ieri notte, insieme al mio amico Fabio, stavamo sviluppando un applicazione web non eccessivamente complessa fino a quando non si è verificato un problema di permessi.
Entro meglio nel dettaglio per far capire la cosa:
La prima caratteristica dell'applicazione è che dovendo accedere a determinate cartelle protette non doveva usare l'utente ASP.NET per eseguire le operazioni, ma bensi un utente membro di un dominio che per comodiftà chiamerò farmuser.
Per far si che l'utente utilizzato non sia asp.net basta andare ad aggiungere nel web.config la riga sull'identity, come la seguente:

<identity impersonate="true" userName="farmuser" password="farmpass" />

Fatto ciò sembra tutto ok, fino a quando non andavamo a utilizzare dei thread in una situazione simile alla seguente:

La pagina aspx lancia un nuovo thread (nel mio caso passando per un metodo statico) ed seguiva un blocco di codice tipo il seguente:

indexerThread = new Thread(new ThreadStart(Run)); 
indexerThread.Priority = ThreadPriority.Lowest; 
indexerThread.Name = "Indexer"; 
indexerThread.Start();

Il metodo statico run eseguiva delle operazioni su file, e qui sono cominciati gli errori. :)

L'inghippo lo abbiamo trovato nei nuovi file creati dal metodo run che avevano come utente ASP.NET, ma poi quando dovevano essere rinominati e/o cancellati dall'utente farmuser, il runtime ci stampava un bell'errore di accesso negato.

Armati di pazienza (molta più Fabio che io) tramite

WindowsIdentity.GetCurrent().Name 

ci siamo messi a cercare dove veniva cambiato l'utente e questo avveniva all'interno del metodo run; quindi se da una pagina asp.net si lancia un thread anche se si ha impostato l'imperosnificazione dell'utente asp.net nel web config, il secondo thread ha sempre come user ASP.NET.

Per risolvere il problema (dopo vari tentativi e ricerche su google) siamo arrivati a questa soluzione:

internal static System.Security.Principal.WindowsIdentity ApplicationIdentity; 

dentro il nostro metodo statico

ApplicationIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
indexerThread = new Thread(new ThreadStart(Run)); 
indexerThread.Priority = ThreadPriority.Lowest; 
indexerThread.Name = "Indexer"; 
indexerThread.Start();

e dentro il run

ApplicationIdentity.Impersonate();

 

 

 

.net comments edit

Per chi si fosse perso il post precedente, sto realizzando un motore di ricerca per indicizzare dei documenti.
Beh rispetto alla versione precedente posso dire di aver fatto dei bei passi in avanti, ora posso paginare (può sembrare una stupidata, ma vi assicuro che con dotLucene non lo è), ed avere ottime performance.
La paginazione è stata dura, in quanto dotLucene non ha una query tipo il COUNT di una SELECT, quindi mi sono dovuto arrangiare un po' creado un sistema a parte per la paginare, effettuando una sola query in modo da non perdere in performance.
Ora con 2200 documenti indicizzati riesco a fare una ricerca avendo 1951 risultati in 0.0625 secondi. :) !!!!Spettacolo direi, anche se spero di migliorare ancora le performance passando alla versione nuova del motore.

Per completare l'operona ora manca veramente poco, inserire i Filtri per la lingua Italiana, in modo da togliere tutti gli articoli (il,del, per,ecc) dall'indicizzazione e poco altro ancora.

Per i test mi sono divertito a fare un interfaccia un po' particolare, mi ricorda qualcosa. Secondo voi che???

Daniele
 quando l'ha vista mi ha detto che sono megalomane.

 Le prestazioni:

 

Un rigranziamento particolare a Fabio (cyber), che mi ha aiutato mentre imprecavo quando il motore non andava.