Memorizzazione dei dati nel codice

16

Alcune volte nel mio passato ho voluto memorizzare i dati nel codice. Si tratta di dati che cambiano raramente e vengono utilizzati in luoghi in cui l'accesso a un database non è possibile, pratico o auspicabile. Un piccolo esempio potrebbe essere la memorizzazione di un elenco di paesi. Per quello potresti fare qualcosa del tipo:

public class Country
{
    public string Code { get; set; }
    public string EnglishName {get;set;}
}

public static class CountryHelper
{
    public static List<Country> Countries = new List<Country>
        {
            new Country {Code = "AU", EnglishName = "Australia"},
            ...
            new Country {Code = "SE", EnglishName = "Sweden"},
            ...
        };

    public static Country GetByCode(string code)
    {
        return Countries.Single(c => c.Code == code);
    }
}

In passato sono riuscito a farlo in passato perché i set di dati erano relativamente piccoli e gli oggetti piuttosto semplici. Ora sto lavorando con qualcosa che avrà oggetti più complessi (5 - 10 proprietà ciascuno, alcune proprietà sono dizionari) e circa 200 oggetti totali.

I dati stessi cambiano molto raramente e quando cambia non è nemmeno così importante. Quindi il passaggio alla versione successiva è perfetto.

Sto pensando di utilizzare T4 o ERB o qualche altra soluzione di template per trasformare la mia fonte di dati in qualcosa che è staticamente immagazzinato nell'assembly.

Sembra che le mie opzioni siano

  1. Archivia i dati in XML. Compilare il file XML come risorsa di assemblaggio. Caricare i dati secondo necessità, memorizzare i dati caricati in un dizionario per prestazioni ripetute.
  2. Genera qualche tipo di oggetto statico o oggetti inizializzati all'avvio.

Sono abbastanza sicuro di aver compreso le implicazioni sulle prestazioni dell'opzione 1. Almeno, la mia impressione è che non avrebbe prestazioni stellari.

Per quanto riguarda l'opzione 2, non so cosa fare. Non conosco abbastanza i componenti interni del framework .NET per conoscere il modo migliore per archiviare questi dati nel codice C # e i modi migliori per inizializzarlo. Mi sono messo in giro usando .NET Reflector per vedere come funziona System.Globalization.CultureInfo.GetCulture(name) , poiché questo è in realtà un flusso di lavoro molto simile a quello che voglio. Sfortunatamente quella traccia si è conclusa con un extern , quindi non ci sono suggerimenti. Sta inizializzando una proprietà statica con tutti i dati, come nel mio esempio, la strada da percorrere? O sarebbe meglio creare gli oggetti su richiesta e poi metterli nella cache, in questo modo?

    private static readonly Dictionary<string, Country> Cache = new Dictionary<string,Country>(); 

    public static Country GetByCode(string code)
    {
        if (!Cache.ContainsKey(code))
            return Cache[code];

        return (Cache[code] = CreateCountry(code));
    }

    internal static Country CreateCountry(string code)
    {
        if (code == "AU")
            return new Country {Code = "AU", EnglishName = "Australia"};
        ...
        if (code == "SE")
            return new Country {Code = "SE", EnglishName = "Sweden"};
        ...
        throw new CountryNotFoundException();
    }

Il vantaggio di crearli contemporaneamente in un membro statico è che puoi usare LINQ o qualsiasi altra cosa per guardare tutti gli oggetti e interrogarli se vuoi. Anche se ho il sospetto che fare questo ha una penalizzazione delle prestazioni di avvio. Spero che qualcuno abbia esperienza con questo e possa condividere le loro opinioni!

    
posta mroach 30.04.2013 - 00:32
fonte

5 risposte

11

Vorrei andare con l'opzione uno. È semplice e amp; facile da leggere. Qualcun altro che osserva il tuo codice lo capirà immediatamente. Sarà anche più semplice aggiornare i tuoi dati XML, se necessario. (In più puoi lasciare che l'utente li aggiorni se hai un bel front-end e memorizzi i file separatamente)

Ottimizzalo solo se necessario - l'ottimizzazione prematura è malvagia:)

    
risposta data 30.04.2013 - 06:12
fonte
7

Dato che si tratta essenzialmente di coppie chiave-valore, ti consiglio di archiviare queste stringhe come file di risorse incorporato nel tuo assembly in fase di compilazione. Puoi quindi leggerli utilizzando un ResourceManger . Il tuo codice sarà simile a questo:

private static class CountryHelper
{
    private static ResourceManager rm;

    static CountryHelper()
    {
        rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
    }

    public static Country GetByCode(string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = code, EnglishName = countryName };
    }
}

Per renderlo un po 'più efficiente puoi caricarli tutti in una volta, in questo modo:

private static class CountryHelper
{
    private static Dictionary<string, Country> countries;

    static CountryHelper()
    {
        ResourceManager rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
        string[] codes = rm.GetString("AllCodes").Split("|"); // AU|SE|... 
        countries = countryCodes.ToDictionary(c => c, c => CreateCountry(rm, c));
    }

    public static Country GetByCode(string code)
    {
        return countries[code];
    }

    private static Country CreateCountry(ResourceManager rm, string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = "SE", EnglishName = countryName };
    }
}

Questo ti aiuterà molto se hai bisogno di far supportare l'applicazione in più lingue.

Se questa sembra essere un'applicazione WPF, un dizionario risorse è una soluzione molto più ovvia.

    
risposta data 30.04.2013 - 00:57
fonte
1

Il miglior esempio di ciò che esiste è probabilmente WPF ... I tuoi moduli sono scritti in XAML, che è fondamentalmente XML, tranne che .NET è in grado di reidratarlo in una rappresentazione di oggetti molto rapidamente. Il file XAML è compilato in un file BAML e schiacciato nel file ".resources", che viene quindi incorporato nell'assembly (l'ultima versione di reflector .NET può mostrarti questi file).

Sebbene non ci sia una grande documentazione per supportarlo, MSBuild dovrebbe in effetti essere in grado di prendere un file XAML, convertirlo in BAML e incorporarlo per te (lo fa per i moduli giusto?).

Quindi, il mio approccio sarebbe: Archivia i tuoi dati in XAML (è solo una rappresentazione XML di un grafo di oggetti), inserisci XAML in un file di risorse e incorporalo nell'assembly. Al runtime, prendi lo XAML e usa XamlServices per reidratarlo. Quindi, avvolgilo in un membro statico o usa Dependency Injection se vuoi che sia più testabile, per consumarlo dal resto del codice.

Alcuni link che sono materiale di riferimento utile:

Nota a margine, puoi effettivamente utilizzare i file app.config o web.config per caricare oggetti ragionevolmente complessi attraverso il meccanismo delle impostazioni se vuoi che i dati vengano modificati più facilmente. E puoi anche caricare XAML dal file system.

    
risposta data 30.04.2013 - 01:02
fonte
1

Suppongo che System.Globalization.CultureInfo.GetCulture (nome) chiami le API di Windows per ottenere i dati da un file di sistema.

Per caricare i dati nel codice, come aveva detto @svick, se stai per caricare 200 oggetti, nella maggior parte dei casi non sarà un problema, a meno che il tuo codice non funzioni su alcuni dispositivi con poca memoria.

Se i tuoi dati non cambiano mai, la mia esperienza sta semplicemente usando un elenco / dizionario come:

private static readonly Dictionary<string, Country> _countries = new Dictionary<string,Country>();

Ma il problema è che devi trovare un modo per inizializzare il tuo dizionario. Immagino che questo sia il punto dolente.

Quindi il problema è cambiato in "Come generare i tuoi dati?". Ma questo riguarda ciò di cui hai bisogno.

Se desideri generare dati per i Paesi, puoi utilizzare

System.Globalization.CultureInfo.GetCultures()

ottieni l'array di CultrueInfo e potresti inizializzare il dizionario con le tue esigenze.

E ovviamente puoi inserire il codice in una classe statica e inizializzare il dizionario nel costruttore statico come:

public static class CountryHelper
{
    private static readonly Dictionary<string, Country> _countries;
    static CountryHelper()
    {
        _countries = new Dictionary<string,Country>();
        // initialization code for your dictionary
        System.Globalization.CultureInfo[] cts = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures);
        for(int i=0; i < cts.Length; i++)
        {
            _countries.Add(cts[i].Name, new Country(cts[i]));
        }
    }

    public static Country GetCountry(string code)
    {
        Country ct = null;
        if(this._countries.TryGet(code, out ct))
        {
            return ct;
        } else
        {
            Log.WriteDebug("Cannot find country with code '{0}' in the list.", code);
            return null;
        }
    }
}
    
risposta data 30.04.2013 - 03:58
fonte
1

La decisione è davvero: vuoi che solo lo sviluppatore (o chiunque abbia accesso a un sistema di compilazione) possa modificare i dati quando ha bisogno di essere modificato, o se un utente finale o eventualmente qualcuno nell'organizzazione dell'utente finale può modificare i dati?

In quest'ultimo caso, suggerirei che un file in formato leggibile dall'utente e modificabile dall'utente sarebbe memorizzato in un posto dove l'utente può accedervi. Ovviamente quando leggi i dati devi stare attento; qualunque cosa tu possa trovare, non ci si può fidare che siano dati validi. Probabilmente preferirei JSON a XML; Trovo più facile capire e avere ragione. È possibile verificare se è disponibile un editor per il formato dei dati; per esempio se usi un plist su MacOS X allora ogni utente che dovrebbe mai avvicinarsi ai dati avrà un editor decente sul suo computer.

Nel primo caso, se i dati fanno parte della tua build, non fanno davvero la differenza. Fai ciò che è più conveniente per te. In C ++, scrivere i dati con un sovraccarico minimo è uno dei pochi posti in cui sono consentite le macro. Se il lavoro di gestione dei dati viene eseguito da una terza parte, è possibile inviare loro i file di origine, lasciarli modificare e si è quindi responsabili di controllarli e utilizzarli nel progetto.

    
risposta data 30.05.2015 - 15:54
fonte

Leggi altre domande sui tag