EF 4 + CTP 5 = Code First : Conventions III

En los dos entradas anteriores sobre convenciones, I y II, se han detallado tanto el significado como la jerarquía de las mismas, así como el funcionamiento real y su relación con Data Annotations. A lo largo de esta entrada trataremos de ver como crear una nueva convención, la cual además de fines formativos tenga validez en la realidad.

 

Los antecedentes

El mapeo por defecto de una relación uno a muchos en esta CTP tiene asociada una convención por la cual este tipo de relaciones tienen establecida la restricción de borrado en cascada. Así, si por ejemplo, partiéramos del siguiente código, podríamos ver en la base de datos la opción OnDelete Cascade para la relación entre Customer y Order.

 public class Customer
 {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public virtual ICollection<Order> Orders { get; set; }
 }
 public class Order
 {
        public int OrderId { get; set; }
        public decimal TotalOrder { get; set; }
        
        public int CustomerId { get; set; }
        public virtual Customer Customer {get;set;}
 }
 

Por supuesto, esta convención de borrado en cascada para las relaciones uno a muchos puede ser eliminada por código quitando la misma de la lista de convenciones como podemos ver a continuación:

 

El problema de esta solución es que requiere de un código adicional. Además, al quitar la convención estamos eliminando esta característica de todas las posibles relaciones.. Nuestro objetivo será el de crear un atributo de Data Annotations, similar al MaxLenght que nos sirva de convención y con el cual podrámos establecer si una propiedad de navegación llevará asociada una restricción en cascada o no.

protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
       modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
       base.OnModelCreating(modelBuilder);
 }

La solución

El primer paso que realizaremos será el de definir nuestro atributo, recuerde de la anterior entrada que todas las convenciones de tipo AttributeConfigurationConvention<,,> llevan asociadas un atributo, y como, además, si necesitaramos un atributo distinto a los definidos en Data Annotations podemos crear el nuestro.

 

namespace System.ComponentModel.DataAnnotations
{
    public class CascadeDeleteAttribute : Attribute
    {
        #region Properties

        bool _cascadeDeleteAction;
        /// <summary>
        /// Get cacade delete action value
        /// </summary>
        public bool CascadeDeleteAction
        {
            get
            {
                return _cascadeDeleteAction;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Create a new instance if CascadeDeleteAttribute
        /// </summary>
        /// <param name="cascadeDeleteAction">True if cacade delete action is preferred option</param>
        public CascadeDeleteAttribute(bool cascadeDeleteAction)
        {
            _cascadeDeleteAction = cascadeDeleteAction;
        }

        #endregion
    }
}

 

Una vez creado el atributo, pasaremos a crear nuestra convención. Puesto que la misma queremos que aplique a las relaciones tendremos que seleccionar PropertyInfo y NavigationPropertyConfiguration como los dos primeros elementos de nuestro genérico. A continuación podemos ver el código de nuestra nueva convención:

public class DeleteCascadeActionAttributeConvention
        : AttributeConfigurationConvention<PropertyInfo, NavigationPropertyConfiguration, CascadeDeleteAttribute>
{

        public override void Apply(PropertyInfo memberInfo, NavigationPropertyConfiguration configuration, CascadeDeleteAttribute attribute)
        {
            configuration.DeleteAction = (attribute.CascadeDeleteAction) ? EdmOperationAction.Cascade : EdmOperationAction.None;
        }
}
 

Por supuesto, esta convención hay que agregarla a la lista de convenciones:

protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
      modelBuilder.Conventions.Add(new DeleteCascadeActionAttributeConvention());

      base.OnModelCreating(modelBuilder);
}

Ahora, para utilizarla simplemente tenemos que decorar nuestras propiedades de navegación como sigue:

 

public class Order
{
    public int OrderId { get; set; }
    public decimal TotalOrder { get; set; }
        
    public int CustomerId { get; set; }
    [CascadeDelete(false)]
     public virtual Customer Customer {get;set;}
}

Espero que os resulte de interés

Saludos

Unai

Published 4/1/2011 13:02 por Unai
Comparte este post:
http://geeks.ms/blogs/unai/archive/2011/01/04/ef-4-ctp-5-code-first-conventions-iii.aspx

Comentarios

# EF 4 + CTP 5 = Code First: Conventions IV

En el último post sobre convenciones miramos con usar las características de plugable conventions en

Wednesday, January 19, 2011 1:09 AM por O bruxo mobile

# re: EF 4 + CTP 5 = Code First : Conventions III

Muy buenos Post, Unai, la verdad es que se agradece este tipo de material en el que se va al grano y se evita toda la literatura.

Estoy probando la Beta 2 del ODAC de Oracle para Entity Framework y me pregunto si soportara Code First, has probado algo de esto con la Beta?

Monday, August 22, 2011 8:59 AM por Carlos