Trabajo avanzado con ficheros de recursos (.resx)


Me encontré con la necesidad ubicar los recursos en una estructura independiente, no limitándonos a los directorios prefijados en Mvc. Además que los cambios en ellos surtieran efecto sin necesidad de recompilar.

Recursos incrustados vs Contenidos

  • Recurso incrustado: Es la opción por defecto en los proyectos. Los recursos se compilan en dlls; y se cargan en memoria al cargar la dll. Esto es beneficioso para el rendimiento y mantenimiento del código (las claves de recursos se generan automáticamente como constantes). Hay un montón de documentación, yo recomendaría este artículo: Resource Files and ASP.NET MVC Projects
  • Recurso como Contenido: Cuando no queremos volver a compilar el código para cambiar los recursos. Cuando queremos editar/añadir archivo de recursos y que los cambios ya se muestren. En este caso, se puede deployar como contenido.
    Esto se puede hacer cambiando la propiedad Build Action en el cuadro de propiedades del archivo de recursos de Visual Studio. Cambiarlo de recursos Embbeded Resource a Content.
    Deployar un recurso

Funcionamiento Standard

En MVC, se tienen dos opciones:

  • Poner los Recursos en el directorio App_LocalResources dentro de los directorios vistas. Y acceder a ellos mediante la función GetLocalResourceObject.
  • Poner los Recursos en el directorio App_GlobalResouces dentro del directorio raíz del proyecto. Y acceder a ellos mediante la función GetGlobalResourceObject.

Es posible reemplazar el comportamiento predeterminado de los recursos. En este artículo que encontré los conceptos claves para extender estas opciones limitadas.
Extending the ASP.NET 2.0 Resource-Provider Model
En este artículo explica como anular el comportamiento predeterminado; se describe como obtener los recursos de una base de datos en lugar de ficheros.

Sobreescribiendo el Funcionamiento Standard

Básicamente se debe interceptar la Factory que generará los proveedores de recursos. Y insertar el nuestro. A continuación se describen los pasos:

  1. Creación de la Factory de Proveedor de Recursos. Extendemos la clase ResourceProviderFactory que nos retornará nuestros proveedores de recursos. En el CreateGlobalResourceProvider/ CreateLocalResourceProvider se retornará nuestro proveedor.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.Compilation;
    
    namespace Infrastructure.Resource
    {
        using System;
        using System.Web.Compilation;
        using System.Web;
        using System.Globalization;
    
        public class FileResourceProviderFactory: System.Web.Compilation.ResourceProviderFactory
        {
    
            public FileResourceProviderFactory() : base()
            {
            }
    
            public override IResourceProvider CreateGlobalResourceProvider(string classKey)
            {
                return new FileResourceProvider(classKey);
            }
    
            public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
            {
                return new FileResourceProvider(virtualPath);
            }
        }
    }
    
  2. Creación del Proveedor. Nuestro proveedor debe extenderse interfaz IResourceProvider.
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Resources;
    using System.Text;
    using System.Web.Compilation;
    
    namespace Infrastructure.Resource
    {
        public class FileResourceProvider: IResourceProvider
        {
    
            private String _file;
    
            private static string Resolve(string path, CultureInfo culture)
            {
    
                var pathResource = path;
                if (null != culture)
                {
                    pathResource = PathResource(path, culture.Name);
                    if (!File.Exists(pathResource)) pathResource = PathResource(path, culture.TwoLetterISOLanguageName);
                    if (!File.Exists(pathResource)) pathResource = path;
                }
    
                return System.Web.HttpContext.Current.Server.MapPath(pathResource);
            }
    
            private static string PathResource(string path, String culture)
            {
                return path.Replace(System.IO.Path.GetFileName(path), string.Format("{0}.{1}{2}",
                                                                                    System.IO.Path.
                                                                                        GetFileNameWithoutExtension(
                                                                                            path),
                                                                                    culture,
                                                                                    System.IO.Path.GetExtension(path)));
            }
    
            public FileResourceProvider(String file)
            {
                _file = file;
    
            }
    
            public object GetObject(string resourceKey, System.Globalization.CultureInfo culture)
            {
                if (string.IsNullOrEmpty(resourceKey))
                {
                    throw new ArgumentNullException("resourceKey");
                }
    
                if (culture == null)
                {
                    culture = CultureInfo.CurrentUICulture;
                }
    
                var rsxrFilePathFr = GetCachedResourceReader(culture);
                return (from DictionaryEntry item in rsxrFilePathFr
                        where item.Key.Equals(resourceKey) select item.Value)
                        .FirstOrDefault();
            }
    
            public ResXResourceReader GetCachedResourceReader(CultureInfo culture)
            {
                var cache = System.Web.HttpContext.Current.Cache;
                ResXResourceReader resourceReader;
                var key = PathResource(_file, culture.Name);
                if (null == (resourceReader = cache[key] as ResXResourceReader))
                {
                    var fileByCulture = Resolve(_file, culture);
                    resourceReader = new ResXResourceReader(fileByCulture);
                    cache[key] = resourceReader;
                }
                return resourceReader;
            }
    
            public System.Resources.IResourceReader ResourceReader
            {
                get
                {
                    return GetCachedResourceReader(CultureInfo.CurrentUICulture);
                }
            }
        }
    }
    
  3. Sobreescribir comportamiento standard. Añadir en el Web.Config para que utilice nuestro sistema de acceso a recursos.
    <system.web>
    	...
    	<globalization resourceProviderFactoryType="Infrastructure.Resource.FileResourceProviderFactory, Infrastructure, Version=1.0.0.0, Culture=neutral" />
    	...
    </system.web>
    
Anuncios
Esta entrada fue publicada en Desarrollo y etiquetada , . Guarda el enlace permanente.

2 respuestas a Trabajo avanzado con ficheros de recursos (.resx)

  1. Bubus dijo:

    Disculpe y como seria su uso de esta solucion en el dataanotation de un modelo en una vista MVC.

    • Rodrigo Perez Burgues dijo:

      El DataAnotation es una tecnología posterior a la de acceso a los recursos; entiendo que internamente utilizaran la misma API. En principio creo que funcionaria; pero se tendría que probar. 😉

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s