Extensibilidad en el Entity Framework Designer en VS2010

por Daniel Laco  23. marzo 2010

 

Una de las cosas mas intersantes que tiene el diseñador del EF en Visual Studio 2010 es la posiblidad de extender las funcionalidades mediante el agregado de nuevas propiedades a una entidad, o la posibilidad de poder interceptar el momento de grabar un archivo edmx, u otras opciones mas.

Esto es muy útil para cuando hay equipos de desarrollo extendidos, o varios proveedores diferentes trabajando en una empresa  y se quiere mantener una uniformidad al momento de escribir código.

Asi que mediante un ejemplo sencillo explicaremos los pasos necesarios para poder implementar una propiedad en las entidades que nos permita definir si una entidad es auditada o no.

Mostraremos como agregar esa propiedad y como utilizarla posteriomente en nuestro código.

Aclaración: todo lo mostrado en este artículo, está realizado sobre la RC de VS2010.

Como paso siguiente, manos a la obra!!

El equipo de ADO.NET publicó en CodePlex el ADO.NET Entity Data Model Designer Extension Starter Kit que nos permite hacer mucho mas fácil la tarea, y ya tiene varias opciones configuradas por default.

Podemos por ejemplo: 

      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas en cada tipo de entidad. El fundamento del tema lo encontramos en un ar tículo del blog de ADO.NET referido a diseño en http://blogs.msdn.com/efdesign/archive/2008/08/12/structural-annotations-one-pager.aspx (en inglés)
      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas durante las actualizaciones en cada entidad que se agregue al modelo conceptual.
      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas cuando las entidades son seleccionadas en el Designer o en el Model Browser.
      • También se puede extender el modo en se que cargan y graban los archivos .admx.
      • O permitir que el Diseñador grabe o carge archivos en un formato personalizado.

 

Paso 1: Instalar la extensión que se baja desde CodePlex

ExtensionManager

Paso 2: Crear el proyecto.

CrearProyecto

Paso 3: Una vez que tenemos el proyecto creado, podemos hacer las modificaciones que nos interesan. En este caso vamos a crear una propiedad para cada Entidad que nos permita habilitar o deshabilitar la auditoría. Creamos entonces una clase AuditarProperty:

using System;
using System.Linq;
using System.Xml.Linq;
using System.ComponentModel;
using Microsoft.Data.Entity.Design.Extensibility;
 
namespace EFEjemploExtension
{
    /// <summary>
    /// </summary>
    class AuditarProperty
    {
        internal static readonly string _namespace = "http://schemas.vemn.com/EFExtensions";
        internal static XName _xnMyNamespace = XName.Get("AuditarProperty", _namespace);
        internal const string _category = "Extensiones";
 
        private XElement _parent;
        private PropertyExtensionContext _context;
 
        public AuditarProperty(XElement parent, PropertyExtensionContext context)
        {
            _context = context;
            _parent = parent;
        }
 
        // Esta propiedad This property is saved in the conceptual content of an .edmx document in the following format:
        // <EntityType>
        //  <!-- other entity properties -->
        //  <MyNewProperty xmlns="http://schemas.vemn.com/MyNewProperty">True</MyNewProperty>
        // </EntityType>
        [DisplayName("Auditar Entidad")]
        [Description("Configura si la entidad es Auditable")]
        [Category(AuditarProperty._category)]
        [DefaultValue(false)]
        public bool AuditarEntidad
        {
            get
            {
                bool propertyValue = false;
                if (_parent.HasElements)
                {
                    XElement lastChild = _parent.Elements().Where<XElement>(element => element != null && element.Name == AuditarProperty._xnMyNamespace).LastOrDefault();
                    if (lastChild != null)
                    {
 
                        bool boolValue = false;
                        if (Boolean.TryParse(lastChild.Value.Trim(), out boolValue))
                        {
                            propertyValue = boolValue;
                        }
                    }
                }
                return propertyValue;
            }
 
            set
            {
                bool propertyValue = value;
 
 
                using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set AuditarEntidad"))
                {
                    if (_parent.HasElements)
                    {
                        XElement lastChild = _parent.Elements().Where<XElement>(element => element != null && element.Name == AuditarProperty._xnMyNamespace).LastOrDefault();
                        if (lastChild != null)
                        {
 
                            lastChild.SetValue(propertyValue.ToString());
                        }
                        else
                        {
                            _parent.Elements().Last().AddAfterSelf(new XElement(_xnMyNamespace, propertyValue.ToString()));
                        }
                    }
                    else
                    {
                        _parent.Add(new XElement(_xnMyNamespace, propertyValue.ToString()));
                    }
  scope.Complete();
                }
            }
        }
    }
}

 

Paso 4: Creamos el Factory.

using System.Xml.Linq;
using System.ComponentModel.Composition;
using Microsoft.Data.Entity.Design.Extensibility;
 
namespace EFEjemploExtension
{
    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(IEntityDesignerExtendedProperty))]
    [EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
    class AuditarPropertyFactory : IEntityDesignerExtendedProperty
    {
        /// <summary>
        /// </summary>
        /// <param name="element"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public object CreateProperty(XElement element, PropertyExtensionContext context)
        {
            return new AuditarProperty(element, context);
        }
    }
}

 

Paso 5: Una vez que hemos terminado, se compila y queda el arc hivo .vsix para ser in stalado en Visual Studio. En el Extension Manager veremos algo asi:

ExtensionInstalada>

Paso 6: Ya tenemos diponible la propiedad en el Diseñador de EF. Cuando agreguemos nuevas tablas o cuando se haga una actualización, las entidades tendrán ahora una nueva propiedad. Si seleccionamos una entidad veremos algo como:

AuditorPropiedad

 Paso 7: Por último, unos extension methods para poder consultar la Metadata del modelo que  nos permita obtener el valor de la propiedad:

 

public static class EFExtension
{
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static bool GetExtentionPropertyAsBoolean<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return (GetExtentionPropertyInternal<T>(workspace,propertyName).ToString().ToLower() == "true") ;   
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static string GetExtentionPropertyAsString<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return GetExtentionPropertyInternal<T>(workspace, propertyName).ToString();
    }
 
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static int  GetExtentionPropertyAsInt<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return Convert.ToInt32(GetExtentionPropertyInternal<T>(workspace, propertyName));
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static object GetExtentionPropertyAsObject<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return GetExtentionPropertyInternal<T>(workspace, propertyName);
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    private static object GetExtentionPropertyInternal<T>(MetadataWorkspace workspace, string propertyName)
    {
        StructuralType objectSpaceType;
        workspace.LoadFromAssembly(typeof(T).Assembly);
        if (!workspace.TryGetItem<StructuralType>(typeof(T).FullName, DataSpace.OSpace, out objectSpaceType))
        {
            throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "No se puede encontrar este tipo de datos en la Metadata", typeof(T)));
        }
        StructuralType edmType = workspace.GetEdmSpaceType(objectSpaceType);
 
        MetadataProperty mp = edmType.MetadataProperties.Where(x => x.Name == propertyName).Single();
        XElement xc = (XElement)mp.Value;
        return xc.Value;
    }
 
}

 

La forma de utilizar esta propiedad en nuestro codigo es:

NorthwindEntities ctx = new NorthwindEntities();
bool audita = ctx.MetadataWorkspace.GetExtentionPropertyAsBoolean<Customers>("http://schemas.vemn.com/EFExtensions:AuditarProperty");
//Hago algo con la auditoria

 

De esta forma, hemos visto como de manera relativamente sencilla se puede extender el Entity Designer y de que manera nos puede ayudar al momento de estandarizar configuraciones en las entidades.

Por supuesto que la cantidad de escenarios e ideas posibles con esta funcionalidad son muchas como pueden ser:

  • Revisión o Comenarios sobre los datos.
  • Seguridad en los Datos.
  • Agregado de Atributos del CLR
  • Validaciones de Anotaciones.
  • etc.
  • etc.

 

Aqui pueden bajar el código de ejemplo utilizado en el artículo.

 

 

 

 

Tags: , ,

ADO.NET | Entity Framework | ORM | Visual Studio

Comentarios (1) -

27/05/2010 16:41:29 #

Air Jordan Shoes

yaa this happens most of the time not have enough time to spend with loved ones, lonliness, depression, drinking all these give a thought its better to do a 9-5 job than blogging

Air Jordan Shoes | Responder

Agregar comentario

  Country flag

biuquote
  • Comentario
  • Vista previa
Loading

Acerca de los Autores

Este es el blog del equipo de VEMN SA 
Presentaremos temas que nos parezcan de interés sobre tecnología .NET, Procesos y Metodologías y todo aquello relacionado con el proceso de desarrollo de Software

Month List

BlogRoll

Download OPML file OPML