Almacenando datos en código

15

Algunas veces en mi pasado he querido almacenar datos en código. Estos serían datos que rara vez cambian y se usan en lugares donde el acceso a una base de datos no es posible, práctico o deseable. Un pequeño ejemplo sería almacenar una lista de países. Para eso podrías hacer algo como:

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);
    }
}

He logrado hacer esto en el pasado porque los conjuntos de datos eran relativamente pequeños y los objetos bastante simples. Ahora estoy trabajando con algo que tendrá objetos más complejos (5 a 10 propiedades cada uno, algunas propiedades serán diccionarios) y alrededor de 200 objetos en total.

Los datos en sí cambian muy raramente y cuando cambian realmente no es tan importante. Así que pasarlo a la próxima versión de lanzamiento está perfectamente bien.

Estoy planeando usar T4 o ERB o alguna otra solución de plantillas para convertir mi fuente de datos en algo que esté almacenado estáticamente en el ensamblaje.

Parece que mis opciones son

  1. Almacena los datos en XML. Compile el archivo XML como un recurso de montaje. Cargue los datos según sea necesario, almacene los datos cargados en un Diccionario para un rendimiento de uso repetido.
  2. Genera algún tipo de objeto u objetos estáticos que se inicializan al inicio.

Estoy bastante seguro de que entiendo las implicaciones de rendimiento de la opción 1. Al menos, mi corazonada es que no tendría un rendimiento estelar.

En cuanto a la opción 2, no sé qué hacer. No sé lo suficiente sobre los aspectos internos de .NET framework para saber la mejor manera de almacenar realmente estos datos en código C # y las mejores maneras de inicializarlos. Hurgué con el reflector .NET para ver cómo funciona System.Globalization.CultureInfo.GetCulture(name) , ya que este es realmente un flujo de trabajo muy similar al que quiero. Desafortunadamente, ese rastro terminó en extern , por lo que no hay pistas allí. ¿Inicializar una propiedad estática con todos los datos, como en mi ejemplo, el camino a seguir? ¿O sería mejor crear los objetos a pedido y luego almacenarlos en caché, de esta manera?

    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();
    }

La ventaja de crearlos a la vez en un miembro estático es que puedes usar LINQ o cualquier otra cosa para mirar todos los objetos y consultarlos si lo deseas. Aunque sospecho que hacer esto tiene una penalización de rendimiento de inicio. Espero que alguien tenga experiencia con esto y pueda compartir sus opiniones.

    
pregunta mroach 30.04.2013 - 00:32
fuente

5 respuestas

11

Iría con la opción uno. Es simple y amp; fácil de leer. Alguien más mirando su código lo entenderá de inmediato. También será más fácil actualizar sus datos XML si es necesario. (Además, podría dejar que el usuario los actualice si tiene un buen interfaz y almacenó los archivos por separado)

Solo optimícelo si lo necesita; la optimización prematura es mala :)

    
respondido por el Rocklan 30.04.2013 - 06:12
fuente
7

Dado que estos son esencialmente pares clave-valor, recomiendo almacenar estas cadenas como un archivo de recursos incrustado en su montaje en tiempo de compilación. Luego puede leerlos utilizando un ResourceManger . Su código se vería así:

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 };
    }
}

Para hacerlo un poco más eficiente, puedes cargarlos todos a la vez, de esta manera:

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 };
    }
}

Esto te ayudará a mucho si alguna vez necesitas que tu aplicación sea compatible con varios idiomas.

Si se trata de una aplicación WPF, un diccionario de recursos es una solución mucho más obvia.

    
respondido por el p.s.w.g 30.04.2013 - 00:57
fuente
1

El mejor ejemplo de esto que existe es probablemente WPF ... Sus formularios están escritos en XAML, que es básicamente XML, excepto que .NET puede rehidratarlos en una representación de objetos muy rápidamente. El archivo XAML se compila en un archivo BAML y se comprime en el archivo ".resources", que luego se incrusta dentro del ensamblaje (la última versión del reflector .NET puede mostrarle estos archivos).

Aunque no hay una gran documentación que lo respalde, MSBuild debería, de hecho, poder tomar un archivo XAML, convertirlo en BAML e insertarlo (¿lo hace para los formularios, no?).

Por lo tanto, mi enfoque sería: Almacene sus datos en XAML (es solo una representación XML de un gráfico de objetos), las cosas que XAML en un archivo de recursos, e incruste en el ensamblaje. En el tiempo de ejecución, tome XAML y use XamlServices para rehidratarla. Luego envuélvalo en un miembro estático o use Inyección de dependencia si desea que sea más comprobable, para consumirlo del resto de su código.

Algunos enlaces que son material de referencia útil:

En una nota al margen, puedes usar los archivos app.config o web.config para cargar objetos razonablemente complejos a través del mecanismo de configuración si quieres que los datos se cambien más fácilmente. Y también puedes cargar XAML desde el sistema de archivos.

    
respondido por el guysherman 30.04.2013 - 01:02
fuente
1

Supongo que System.Globalization.CultureInfo.GetCulture (nombre) llamaría a las API de Windows para obtener los datos de un archivo del sistema.

Para cargar los datos en código, como dijo @svick, si está a punto de cargar 200 objetos, no será un problema en la mayoría de los casos, a menos que su código se ejecute en algunos dispositivos con poca memoria.

Si sus datos nunca cambian, mi experiencia es simplemente usando una lista / diccionario como:

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

Pero el problema es que necesita encontrar una manera de inicializar su diccionario. Supongo que este es el punto de dolor.

Por lo tanto, el problema se cambia a "¿Cómo generar sus datos?". Pero esto se relaciona con lo que necesitas.

Si desea generar datos para los países, puede utilizar el

System.Globalization.CultureInfo.GetCultures()

obtenga la matriz CultrueInfo, y podría inicializar su diccionario con sus necesidades.

Y, por supuesto, podría colocar el código en una clase estática e inicializar el diccionario en el constructor estático como:

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;
        }
    }
}
    
respondido por el use.your.illusion 30.04.2013 - 03:58
fuente
1

La decisión es realmente: ¿Desea que solo el desarrollador (o cualquier persona con acceso a un sistema de compilación) modifique los datos cuando sea necesario modificarlos, o debe un usuario final o posiblemente alguien de la organización del usuario final? ¿Para modificar los datos?

En el último caso, sugeriría que un archivo en formato legible por el usuario y editable por el usuario se almacene en un lugar donde el usuario pueda acceder a él. Obviamente, cuando lees los datos necesitas tener cuidado; Todo lo que encuentre no puede ser confiable como datos válidos. Probablemente prefiero JSON a XML; Me resulta más fácil de entender y hacer las cosas bien. Puede verificar si hay un editor disponible para el formato de datos; por ejemplo, si utiliza un plist en MacOS X, cada usuario que deba acercarse a los datos tendrá un editor decente en su computadora.

En el primer caso, si los datos son parte de su compilación, realmente no hay una diferencia. Haz lo que sea más conveniente para ti. En C ++, escribir los datos con una sobrecarga mínima es uno de los pocos lugares donde se permiten macros. Si un tercero realiza el trabajo de mantenimiento de los datos, puede enviarles los archivos de origen, permitirles editarlos y, a continuación, es responsable de verificarlos y utilizarlos en su proyecto.

    
respondido por el gnasher729 30.05.2015 - 15:54
fuente

Lea otras preguntas en las etiquetas