Press "Enter" to skip to content

6 – Classi di uso comune

In questa sesta puntata dedicata alla libreria di base degli Helper, discuteremo una classe che si occupa di fornire delle scorciatoie ad alcune operazioni sui files, che quotidianamente faremo nelle nostre applicazioni.

Introduzione

Quest’ultima piccola classe, raccoglie una serie di metodi per produrre, verificare generare nomi di files e cartelle, FileHelper utilizza le classi di System.IO per il suo lavoro, permettendoci di generare file di log su una cartella su cui siamo certi l’utente ha diritto di scrivere, ci permette di leggere files sulla cartella di installazione del programma e di generare cartelle se ne abbiamo necessità. Inoltre, ci permette di creare il nome dei files per la memorizzazione dei settings utente e dei settings applicazione personalizzati.

La struttura base della classe

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Dnw.Base.Logger;

namespace Dnw.Base
{
    public static class FileHelper
    {

        private readonly static string mClassName =
            System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
        }
}

 

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.IO
Imports Dnw.Base.Logger

Public NotInheritable Class FileHelper

    Private Shared ReadOnly mClassName As String = _
      System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name

    Private Sub New()
    End Sub
End Class

Nulla cambia rispetto a tutte le altre classi helper, anche questa è una classe statica in C# traslata come classe NotInheritable con costruttore privato in VB.

Le costanti

private const string FMP_TmpDateTime = "yyyymmddHHmmssff";
private const string TXT_APPDATA = "APPDATA";
private const string TXT_AssemblyVuoto = "Il Nome dell'assembly non può essere vuoto";
private const string TXT_Backspace = "\\";
private const string TXT_ConfigExtension = ".dnwcfg";
private const string TXT_Dot = ".";
private const string TXT_FilenameVuoto = "Il nome file non può essere vuoto";
private const string TXT_FileNonEsiste = "Il file {0} non esiste";
private const string TXT_FirmBaseDirectory = "DotNetWork";
private const string TXT_LogExtension = ".log";
private const string TXT_PathVuota = "La stringa Path non può essere vuota";
private const string TXT_ProductNameVuoto = "Il Product Name non può essere vuoto";
private const string TXT_USERNAME = "USERNAME";

 

Private Const FMP_TmpDateTime As String = "yyyymmddHHmmssff"
Private Const TXT_APPDATA As String = "APPDATA"
Private Const TXT_AssemblyVuoto As String = "Il Nome dell'assembly non può essere vuoto"
Private Const TXT_Backspace As String = "\"
Private Const TXT_ConfigExtension As String = ".dnwcfg"
Private Const TXT_Dot As String = "."
Private Const TXT_FilenameVuoto As String = "Il nome file non può essere vuoto"
Private Const TXT_FileNonEsiste As String = "Il file {0} non esiste"
Private Const TXT_FirmBaseDirectory As String = "DotNetWork"
Private Const TXT_LogExtension As String = ".log"
Private Const TXT_PathVuota As String = "La stringa Path non può essere vuota"
Private Const TXT_ProductNameVuoto As String = "Il Product Name non può essere vuoto"
Private Const TXT_USERNAME As String = "USERNAME"

Abbiamo utilizzato le costanti sia per i messaggi di errore sia per definire formattazioni e stringhe di base, queste stringhe possono essere trasferite nel file delle risorse della libreria se pensiamo di aver bisogno di tradurle in più lingue.

Usare le costanti è una questione di disciplina, non sempre rende il codice più leggibile per quanto lo renda più pulito, è certo comunque che rende semplice il passaggio a un file di risorse internazionalizzato e soprattutto usandole sui nomi di controlli e nomi di campi delle datatable è utile a evitarci errori di nomenclatura che generano eccezioni.

Il metodo CheckCreateDir

public static void CheckCreateDir(string pDir)
{
    try
    {
        if (!System.IO.Directory.Exists(pDir))
        {
            System.IO.Directory.CreateDirectory(pDir);
        }
    }
    catch (Exception ex)
    {
        EventLogger.SendMsg(mClassName, 
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.Error);
        throw new ApplicationException(" " + mClassName + TXT_Dot
            + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
    }
}

 

Public Shared Sub CheckCreateDir(ByVal pDir As String)
    Try
        If Not System.IO.Directory.Exists(pDir) Then
            System.IO.Directory.CreateDirectory(pDir)
        End If
    Catch ex As Exception
        EventLogger.SendMsg(mClassName, _
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.[Error])
        Throw New ApplicationException(" " _
        + mClassName + TXT_Dot + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
End Sub

Questo metodo, data una stringa contenente il nome di una cartella, utilizza le classi di System.IO per verificarne l’esistenza ed eventualmente generarla. Può tornare utile usarla prima di generare un file di output di qualsiasi tipo.

Il metodo CheckNoFile

public static void CheckNoFile(string pPath)
{
    if (!System.IO.File.Exists(pPath))
    {
        throw new NoFileException(string.Format(TXT_FileNonEsiste, pPath));
    }
}

 

Public Shared Sub CheckNoFile(ByVal pPath As String)
    If Not System.IO.File.Exists(pPath) Then
        Throw New NoFileException(String.Format(TXT_FileNonEsiste, pPath))
    End If
End Sub

Un metodo molto semplice per accertarsi se un file esiste, per esempio quando dobbiamo leggere dei dati dal file system. In questo metodo utilizziamo l’eccezione personalizzata NoFileException che abbiamo visto nella puntata precedente di questa serie.

Il Metodo CreateAppConfigFleName

public static string CreateAppConfigFileName(string pAssemblyName)
{
    try
    {
        if (StringHelper.IsNullOrTrimEmpty( pAssemblyName) )
        {
            throw new EmptyParameterException(TXT_AssemblyVuoto);
        }

        return GetAppDir(String.Concat(pAssemblyName, TXT_ConfigExtension));
    }
    catch (Exception ex)
    {
        EventLogger.SendMsg(mClassName, 
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.Error);
        throw new ApplicationException(" " + mClassName + TXT_Dot
            + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
    }
}

 

Public Shared Function CreateAppConfigFileName(ByVal pAssemblyName As String) As String
    Try
        If (StringHelper.IsNullOrTrimEmpty( pAssemblyName)) Then
            Throw New EmptyParameterException(TXT_AssemblyVuoto)
        End If

        Return GetAppDir([String].Concat(pAssemblyName, TXT_ConfigExtension))
    Catch ex As Exception
        EventLogger.SendMsg(mClassName, _
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.[Error])
        Throw New ApplicationException(" " + mClassName _
        + TXT_Dot + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
End Function

Questo è un metodo da utilizzarsi nella configurazione delle applicazioni, se come accade nelle mie applicazioni, viene utilizzato un file non standard per la memorizzazione di settaggi e di parametri applicativi, è opportuno che il suo nome sia costruito in modo standard. Questo metodo genera un nome di file costruito con la forma:

PathDiInstallazione\Nomeassembly.dnwcfg ad esempio: c:\Programmi\Dotnetwork\HelloWorld.dnwcfg

Ovviamente il modo di costruire questo tipo di nome è ancora una volta una decisione arbitraria, una regola di business che io ho stabilito, non un assoluto, ciascuno può costruire le proprie regole in merito in base alle proprie esigenze.

Il metodo CreateUserLogFileName

public static string CreateUserLogFileName(string pProductName, string pFileName)
{
    string ret = "";
    try
    {
        if (StringHelper.IsNullOrTrimEmpty( pProductName))
        {
            throw new EmptyParameterException(TXT_ProductNameVuoto);
        }
        if (StringHelper.IsNullOrTrimEmpty( pFileName))
        {
            throw new EmptyParameterException(TXT_FilenameVuoto);
        }
        StringBuilder filename = new StringBuilder(Environment.GetEnvironmentVariable(TXT_APPDATA));
        if (!(filename.ToString()).EndsWith(TXT_Backspace))
        {
            filename.Append(TXT_Backspace);
        }
        filename.Append(TXT_FirmBaseDirectory);

        CheckCreateDir(filename.ToString());

        filename.Append(TXT_Backspace);
        filename.Append(pProductName);
        // Determine whether the directory exists.
        CheckCreateDir(filename.ToString());

        filename.Append(TXT_Backspace + pFileName);
        filename.Append(TXT_LogExtension);
        ret = filename.ToString();
    }
    catch (Exception ex)
    {
        EventLogger.SendMsg(mClassName, 
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.Error);
        throw new ApplicationException(" " + mClassName + TXT_Dot
            + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
    }
    return (ret);
}

 

Public Shared Function CreateUserLogFileName(ByVal pProductName  _
	As String, ByVal pFileName As String) As String
    Dim ret As String = ""
    Try
        If (StringHelper.IsNullOrTrimEmpty( pProductName)) Then
            Throw New EmptyParameterException(TXT_ProductNameVuoto)
        End If
        If (StringHelper.IsNullOrTrimEmpty( pFileName )) Then
            Throw New EmptyParameterException(TXT_FilenameVuoto)
        End If
        Dim filename As New StringBuilder(Environment.GetEnvironmentVariable(TXT_APPDATA))
        If Not (filename.ToString()).EndsWith(TXT_Backspace) Then
            filename.Append(TXT_Backspace)
        End If
        filename.Append(TXT_FirmBaseDirectory)

        CheckCreateDir(filename.ToString())

        filename.Append(TXT_Backspace)
        filename.Append(pProductName)
        ' Determine whether the directory exists. 
        CheckCreateDir(filename.ToString())

        filename.Append(TXT_Backspace + pFileName)
        filename.Append(TXT_LogExtension)
        ret = filename.ToString()
    Catch ex As Exception
        EventLogger.SendMsg(mClassName, _
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.[Error])
        Throw New ApplicationException(" " + mClassName _
        + TXT_Dot + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
    Return (ret)
End Function

Questo metodo, genera il nome del file di Log per un utente, viene costruito per essere posizionato nella cartella Application Data (Dati Applicazioni) dell’utente, ove siamo certi di avere il permesso di scrittura e lettura. la stringa viene composta nel seguente modo:

UserApplicationDataDir\CompanyBaseDirName\ProductName\Filename.LogExtension

nel nostro caso, posto che il ProductName sia HelloWorld e il FileName sia HelloLog il risultato sarà:

C:\Documents and settings\username\Dati Applicazioni\DotNetWork\HelloWorld\HelloLog.log (su Win XP)

C:\Users\username\AppData\Roaming\DotnetWork\HelloWorld\HelloLog.log (su Windows Vista)

Non ho ancora esplorato tutte le differenze fra XP e Vista, ma credo potremo verificarle testando le classi con NUnit nel prossimo articolo.

Come per il metodo precedente anche questo usa regole di business arbitrarie, per quanto, l’uso della cartella Application Data per i files di configurazione utente e per gli eventuali file accessori ad una applicazione sia fra le linee guida del team di sviluppo di Microsoft.

Il metodo CreateUserTempFileName

public static string CreateUserTempFileName(string pFolder, string pExtension)
{
    string ret = String.Empty;

    try
    {
        StringBuilder filename = new StringBuilder();

        if (StringHelper.IsNullOrTrimEmpty( pFolder))
        {
            pFolder = Path.GetTempPath();
        }

        filename.Append(TXT_Backspace);

        filename.Append(Environment.GetEnvironmentVariable(TXT_USERNAME));
        filename.Append(DateTime.Now.ToString(FMP_TmpDateTime));
        if (!pExtension.StartsWith(TXT_Dot))
        {
            filename.Append(TXT_Dot);
        }
        filename.Append(pExtension);
        ret = filename.ToString();
    }
    catch (Exception ex)
    {
        EventLogger.SendMsg(mClassName, 
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.Error);
        throw new ApplicationException(" " + mClassName + TXT_Dot
            + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
    }
    return ret;
}

 

Public Shared Function CreateUserTempFileName(_
	ByVal pFolder As String, ByVal pExtension As String) As String
    Dim ret As String = [String].Empty

    Try
        Dim filename  As New StringBuilder()

        If (StringHelper.IsNullOrTrimEmpty( pFolder)) Then
            pFolder = Path.GetTempPath()
        End If

        filename.Append(TXT_Backspace)

        filename.Append(Environment.GetEnvironmentVariable(TXT_USERNAME))
        filename.Append(DateTime.Now.ToString(FMP_TmpDateTime))
        If Not pExtension.StartsWith(TXT_Dot) Then
            filename.Append(TXT_Dot)
        End If
        filename.Append(pExtension)
        ret = filename.ToString()
    Catch ex As Exception
        EventLogger.SendMsg(mClassName, _
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.[Error])
        Throw New ApplicationException(" " + mClassName _
        + TXT_Dot + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
    Return ret
End Function

In questo caso generiamo un file temporaneo con nome univoco che può essere necessario per qualsiasi esigenza, non partiamo da una cartella precisa, perché il file potrebbe essere necessario posizionarlo in una cartella specifica, ad esempio se si tratta di un file dati usato per comunicare in modo asincrono con un servizio di elaborazione a cui semplicemente poniamo il file con i dati da elaborare in una cartella senza comunicazione diretta. L’univocità del nome del file la generiamo assegnando al file dei dati relativi all’utente e un timestamp rilevato dalla macchina su cui il file è generato. Non è ovviamente a prova di bomba, ma ci sono ottime probabilità che non esistano files simili.

Il file generato è nella forma: NomeCartellaDestinazione\NomeutenteAAAAmmgghhmmssff.estensione

ove AAAAmmgg ecc. è il valore di data ed ora corrente al centesimo di secondo.

Se necessitassimo di certezza, possiamo sempre utilizzare un GUID per il nome file, ma questo metodo, ci permette di capire chi e quando a colpo d’occhio e potrebbe tornarci utile in alcuni casi.

Come per i metodi precedenti, qui ciascuno può decidere le proprie regole in base a dove vuole arrivare.

Il Metodo CreateUserConfigFileName

public static string CreateUsrConfigFileName(string pProductName, string pAssemblyName)
{
    try
    {
        if (StringHelper.IsNullOrTrimEmpty( pProductName))
        {
            throw new EmptyParameterException(TXT_ProductNameVuoto);
        }
        if (StringHelper.IsNullOrTrimEmpty( pAssemblyName))
        {
            throw new EmptyParameterException(TXT_AssemblyVuoto);
        }
        StringBuilder sb = new StringBuilder(Environment.GetEnvironmentVariable(TXT_APPDATA));
        if (!(sb.ToString()).EndsWith(TXT_Backspace))
        {
            sb.Append(TXT_Backspace);
        }
        sb.Append(TXT_FirmBaseDirectory);

        CheckCreateDir(sb.ToString());

        sb.Append(TXT_Backspace);
        sb.Append(pProductName);
        // Determine whether the directory exists.
        CheckCreateDir(sb.ToString());

        sb.Append(TXT_Backspace + pAssemblyName);
        sb.Append(TXT_ConfigExtension);
        return (sb.ToString());
    }
    catch (Exception ex)
    {
        EventLogger.SendMsg(mClassName, 
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.Error);
        throw new ApplicationException(" " + mClassName + TXT_Dot
            + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
    }
}

 

Public Shared Function CreateUsrConfigFileName(ByVal pProductName As String, _
    ByVal pAssemblyName As String) As String
    Try
        If(StringHelper.IsNullOrTrimEmpty( pProductName)) Then
            Throw New EmptyParameterException(TXT_ProductNameVuoto)
        End If
        If (StringHelper.IsNullOrTrimEmpty( pAssemblyName)) Then
            Throw New EmptyParameterException(TXT_AssemblyVuoto)
        End If
        Dim sb As New StringBuilder(Environment.GetEnvironmentVariable(TXT_APPDATA))
        If Not (sb.ToString()).EndsWith(TXT_Backspace) Then
            sb.Append(TXT_Backspace)
        End If
        sb.Append(TXT_FirmBaseDirectory)

        CheckCreateDir(sb.ToString())

        sb.Append(TXT_Backspace)
        sb.Append(pProductName)
        ' Determine whether the directory exists. 
        CheckCreateDir(sb.ToString())

        sb.Append(TXT_Backspace + pAssemblyName)
        sb.Append(TXT_ConfigExtension)
        Return (sb.ToString())
    Catch ex As Exception
        EventLogger.SendMsg(mClassName, _
        System.Reflection.MethodBase.GetCurrentMethod(), MessageType.[Error])
        Throw New ApplicationException(" " + mClassName + _
        TXT_Dot + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
End Function

Questo metodo procede in modo simile a quello che genera il file di Log per generare il nome di un file personalizzato per la memorizzazione di parametri di configurazione per uno specifico utente. Anche in questo caso viene utilizzato il percorso Application Data (Dati Applicazioni) per generare un file specifico in una cartella ove l’utente ha certamente i permessi di lettura e scrittura. La forma del nome file è la seguente:

UserApplicationDataDir\CompanyBaseDirName\ProductName\Filename.ConfigExtension

Quindi usando lo stesso esempio che abbiamo utilizzato per il log, otteniamo:

C:\Documents and settings\username\Dati Applicazioni\DotNetWork\HelloWorld\HelloLog.dnwcfg (su Win XP)

C:\Users\username\AppData\Roaming\DotnetWork\HelloWorld\HelloLog.dnwcfg (su Windows Vista)

Il Metodo GetAppDir

public static string GetAppDir()
{
    return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
}

 

Public Shared Function GetAppDir() As String
    Return My.Application.Info.DirectoryPath
    'Return AppDomain.CurrentDomain.SetupInformation.ApplicationBase 
End Function

Questo metodo minuscolo, l’ho generato perché in C# non ho trovato un metodo più semplice per ottenere la cartella di installazione di un programma e ricordarlo a memoria è complicato. Invece in VB come potete vedere c’è un semplice parametro su My. L’uso di questa versione del metodo è più raro, più facilmente si utilizza il suo Overload, per comporre il nome di un file o una sottocartella ove ad esempio abbiamo installato dei file di risorsa che la nostra applicazione utilizza ma non possono essere inclusi direttamente come risorse compilate.

public static string GetAppDir(string pPath)
{
    if (pPath == null) pPath = String.Empty;
    return Path.Combine(GetAppDir(), pPath);
}

 

Public Shared Function GetAppDir(ByVal pPath As String) As String
    If pPath Is Nothing Then
        pPath = [String].Empty
    End If
    Return Path.Combine(GetAppDir(), pPath)
End Function

Questo secondo overload, permette di comporre il nome di un file o di una sottocartella che si trova sotto alla cartella di installazione del programma.

Conclusioni

Anche questa classe Helper è terminata, sicuramente in futuro potremo modificarla aggiungendovi nuovi metodi, ma con essa a disposizione avremo il necessario per iniziare a pensare ad una semplice applicazione funzionante. Nel codice dei metodi di questa classe potete notare che abbiamo utilizzato altre classi spiegate nelle puntate precedenti, quindi abbiamo iniziato ad assemblare fra loro i mattoni di base che costituiscono i pezzi riutilizzabili che OOP ci permette di sviluppare.

Concluderò la presentazione di queste mini classi con un ulteriore articolo in cui utilizzeremo NUnit per testare il funzionamento delle classi sviluppate a cui allegherò il progetto completo delle classi.

Per qualsiasi Feedback, Ulteriore domanda, Chiarimento, oppure se trovate qualche errore usate direttamente il form di contatto con un click sulla bustina in cima alla pagina.

Potete scaricare il progetto esempio dedicato alle classi di uso comune dal link qui sotto indicato.