Comprimere una cartella e tutte le sue sottodirectory
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
Il Domain Model
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.
Riflessioni su NHibernate
Posted by imperugo in Nhibernate on Monday 17 July 2006 at 6:00 PM
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.
Io, il GDI+ e il pocket pc
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. !!!
L'impersonate di asp.net e i thread
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();
Ricerca all'interno dei documenti Parte 2
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.
Ricerca all'interno dei documenti
In questi giorni sono parecchio incasinato con il lavoro e per questo sto tralasciando un po' la comunity, (forum, blog, ecc.) e me ne scuso, d'altro canto il lavoro è sempre il lavoro.
In particolar modo sto lavorando alla realizzazione di un motore di ricerca un po' diverso dal solito, ossia con la possibilità di indicizzare dei documenti presenti all'interno del nostro sito o indicizzare pagine esterne tramite url.
Per capire bene la cosa immaginiamo un sito dove le news, articoli, delibere, ecc, vengono create dinamicamte tramite un CMS o un FTP da parte dell'amministratore.
Nel motore di ricerca del sito dobbiamodo dare la posssibilità di indicizzare anche il testo contenuto in questi file.Operazione solitamente scomoda se il cms non offre questa possibilità o l'amministratore non scrive le keyword del documento distribuito on-line in qualche field che ci andiamo a leggere successivamente.
Partendo da quest'idea la prima cosa fatta è stata una ricerca sul web di prodotti già esistenti e qui sono incappato su DotLucene, motore di ricerca realizzato in .Net, che per lo storage dell'indice utilizza un file e non un database. Da lì sono finito su SeekaFile, che non è altro che un servizio windows che indicizza dei file presenti all'interno di un path impostato da noi, con in più una dependency sul file che, nel caso venga modificato, l'indice è automaticamente aggiornato.Funzione spettacolare, ma non sempre si ha la possibilità di installare dei servizi custom sul server, quindi ho preso il tutto, ho scorporato il servizio, gli ho aggiunto un po' di plug-in per leggere l'html direttamente da un url ripulendolo e indicizzando solo il testo, stessa cosa per i PDF e RTF e ottenendo come risultato di ricerche e indicizzazione delle mie custom entity e collection (ormani le uso da tutte le parti, non mi ricordo neanche come si crea un datatable).
L'aspetto più interessante di questa versione da me creata è che tutto è utilizzabile senza realizzare nessun setup ma soltanto impostando dei valori nel web.config ed effettuando una chiamata quando si crea, upload o modifica un file.
L'indicizzazione avviene tramite thread, con una gestione della coda, a volte per leggere un pdf di diversi mega e ripulirlo la macchina può impiegare diversi secondo, ma la ricerca continua a funzionare e tutti gli altri file da indicizzare sono in attesa che finisca il primo thread.
L'indicizzazione avviene in lowpriority, il che significa che in caso altre applicazioni sul server richiedano risore lui le rilascia, aspettando che la macchina si meno indaffarata :)
Ricapitolando le caratteristiche del motore.
- Indicizzazione su file senza Database;
- Non necessita di installazione;
- Indicizza documenti DOC,PDF,XLS,PPT,RTF,HTML,TXT,XML;
- Indicizza direttamente l'HTML da un url (grabba il codice lo filtra e lo indicizza)
- Possibilità di crearsi plug-in personalizzati e caricarli a runtime;
- Possibilità di indicizzare direttamente delle directory;
- Utilizzo di custom entity e collection;
- Gestione della coda di indicizzazione;
- Low priority per l'indicizzazione.
- e poi non so vediamo un po' l'ispirazione cosa porta :)
Per chi fosse interessato alla cosa non posso che dire Stay Tuned!!.
( frase brutalmente copiata, non dico a chi, ma sono sicuro che lui lo sa )
Ovviamente se avete esperienze, idee o altro sono ben accette.
Chissà che non mi venga in mente di realizzare un desktop search tipo quello di Google o Microsoft, il principio è lo stesso. :D
Un pannello per gestire la cache
In rete ho "scovato" questo interessante link che mostra come realizzare un pannello di controllo per la gestione della cache nelle nostre applicazioni web.
Personalmente ritengo che in fase finale non serva in quanto un'applicazione dovrebbe cancellare e creare la cache automaticamente senza intervento dell'untente, ma in fase di sviluppo può diventare molto utile per capire come muoversi.
Una schermata del pannello:
Tra le caratteristiche più interessanti la possibilità di vedere il valore della cache massima utilizzata e quella attualmente disponibile oltre che la visualizzazione dei dati contenuti in cache per gli oggetti semplici (Datetime. ecc).
CreateDirectory e Windows 2003
Ci sono delle cose che a volte proprio non capisco e che mi fanno imbufalire. Ho pubblicato un lavoro abbastanza semplice realizzato in .net 1.1 su un server web (tra quelli che vanno per la maggiore). Ma quando vado a provare l'applicazione mi trovo di fronte ad un bel errore nel create directory.
Lo stack mi diceva che non avevo l'autorizzazione necessaria per la creazione della cartella; vado a controllare nel mio pannello di controllo ed era tutto ok; provo a resettare i permessi, ma niente. Preso dalla disperazione chiamo il provider chiedo un controllo, ma niente da fare l'errore persiste.
Poi, cerca cerca su google trovo questo articolo che mi risolve il problema, tranne per il fatto che se cerco di creare la cartella C:\Disney\Pluto e la cartella Disney non esiste lui va in errore.
Il problema è facilmente risolvibile con questo codice:
[DllImport("msvcrt.dll", SetLastError = true)]
static extern int _mkdir(string path);
static DirectoryInfo CreateDirectory(string path)
{
int returnCode = _mkdir(path);
if (returnCode == -1)
{
string[] percorsi = path.Split(new Char[] {'\\'} );
if(percorsi.Length > 1)
{
if(Directory.Exists(percorsi[0]))
{
string percorso = percorsi[0] + "\\";
for (int i = 1; i < percorsi.Length; i++)
{
percorso += percorsi[i] + "\\";
if (!Directory.Exists(percorsi[i]))
_mkdir(percorso);
}
}
else
throw new ApplicationException("Drive not found.");
}
else
throw new ApplicationException("String format error!");
}
else if (returnCode != 0)
throw new ApplicationException("Error calling [msvcrt.dll]:_wmkdir(" + path + "), error code: " + returnCode.ToString());
return new DirectoryInfo(path);
}
La sintassi per la creazione della cartella rimane la stessa, ma viene intercettata la funzione CreateDirecotry e sostituita con la nostra.
Un unica precisazione, all'interno del ciclo for utilizzo l'_mkdir per la creazione della cartella e non la mia funzione; faccio ciò per evitare la ricorsione.
Archive