EF 4 – Features CTP 2 y VS2010 RC

Para los que estéis probando EF 4 Features CTP tened en cuenta los cambios en VS2010 y la compatibilidad de estas features en la nueva versión de Visual Studio. Si estais usando STE ( Selft Tracking Entities ) ahora están ‘out-of-box’ en VS 2010 RC y por lo tanto no necesitais instalar Features CTP, que además os fastidiaría los nuevos ttinclude y por lo tanto no serían compatibles. Para los que os interesen las plantillas de POCO estas estarán disponibles como extension a VS y por lo tanto descargables automáticamente desde el Extensions Manager de Visual Studio ( tampoco estarán en EF Features ).

Por lo tanto, que queda en EF FEATURES?? pues solamente la posibilidad de hacer ‘Code Only’…

 

 

Espero que os sirva de interes y que no tengais que reparar la instalación de la RC como un servidor…

 

Saludos

Unai

Entity Framework y Specification Pattern

Introducción

Hace ya unos cuantos dias que vengo dándole vueltas a la cabeza para escribir un post sobre especificaciones y como construirlas sobre la infraestructura de Entity Framework, aunque en realidad sería igual sobre cualquier elemento IQueryable, aunque con alguna nota de implementación. Pensando en esta entrada no sabía si comenzar con una explicación sobre este patrón o ceñirme directamente a una posible implementación,  puesto que, me parece magnifica la documentación sobre el mismo que Fowler y Evans tienen en el documento de referencia. Al final, y despues de darle muchas vueltas me quedaré entre medias puesto que creo que es necesario realizar una pequeña introducción, aunque aconsejo encarecidamente desde aquí leerse el documento anterior si nunca ha visto información acerca de este patrón.

Sintetizándolo, aunque sea demasiada síntesis, podríamos decir que el patrón espeficación trata de lograr una separación entre la sentencia que unos objetos deberían de cumplir y el objeto que realiza la selección. Por ejemplo una especificación podría describir “los clientes que pertenecen a una determinada situación geográfica” pero no saber nada acerca de como realizar dicha selección. En definitiva no se trata nada más y nada menos que agregar una responsabilidad desacoplada de los objetos de dominio que la usan.

Este patrón es muy escuchado y usado en arquitecturas basadas en modelos de dominio y puesto en práctica en muchas arquitecturas para definir “criterios” de selección. Basándonos en la definición formal de este patrón, mostrada en la Figura 1, podríamos ver a primeras que una implementación de este patrón trabajando con IQueryable no tendría mucho sentido.

 

Specification_UML

La razón principal de la afirmación anterior viene de la propia definición del patron, la cual implica trabajar con objetos directamente en memoria puesto que el método IsSatisfiedBy tomaría una instancia del objeto en el cual queremos comprobar si cumple un determinado criterio y devolver true o false según se cumpla o no, algo que por supuesto no deseamos por la sobrecarga que esto implicaría. Por todo esto podríamos modificar un poco nuestra definición de Specification para que en vez de devolver un booleano negando o afirmando el cumplimiento de una especificación determinada podríamos devolver una “expression” con el criterio a cumplir. En el siguiente fragmento de código tendríamos un esqueleto de nuestro contrato base con esta ligera modificación.

    /// <summary>
    /// Base contract for Specification pattern, for more information
    /// about this pattern see http://martinfowler.com/apsupp/spec.pdf
    /// or http://en.wikipedia.org/wiki/Specification_pattern.
    /// Really this is variant implementation for add feature of linq and
    /// lambda expression into this pattern.
    /// </summary>
    /// <typeparam name="TEntity">Type of entity</typeparam>
    public interface ISpecification<TEntity>
        where TEntity : class,new()
    {
        /// <summary>
        /// Check if this specification is satisfied by a 
        /// specific expression lambda
        /// </summary>
        /// <returns></returns>
        Expression<Func<TEntity, bool>> SatisfiedBy();
    }

Profundizando

Llegados a este punto podríamos decir que ya tenemos la base y la idea de lo que queremos construir, ahora, solamente falta seguir las propias normas y guias de este patrón empezándonos a crear nuestras especificaciones directas o “hard coded specifications” y nuestras especificaciones compuestas, al estilo And, Or …

Según avanzas en esta aproximación uno se da cuenta de que tendrá que realizar un buen esfuerzo de trabajo con árboles de expresiones, algo en el que no todo el mundo está muy puesto, y a lo que se le tiene algo de miedo, del que uno se puede curar leyendo el excelente libro de mi amigo y colega Octavio :-)

Mi primera intentona, y fallida a mi pesar, era similar a lo siguiente:

    /// <summary>
    /// A logic AND Specification
    /// </summary>
    /// <typeparam name="T">Type of entity that check this specification</typeparam>
    public class AndSpecification<T>
       : CompositeSpecification<T>
       where T : class,new()
    {
        #region Members

        private ISpecification<T> _RightSideSpecification = null;
        private ISpecification<T> _LeftSideSpecification = null;

        #endregion

        #region Public Constructor

        /// <summary>
        /// Default constructor for AndSpecification
        /// </summary>
        /// <param name="leftSide">Left side specification</param>
        /// <param name="rightSide">Right side specification</param>
        public AndSpecification(ISpecification<T> leftSide, ISpecification<T> rightSide)
        {
            if (leftSide == (ISpecification<T>)null)
                throw new ArgumentNullException("leftSide");

            if (rightSide == (ISpecification<T>)null)
                throw new ArgumentNullException("rightSide");

            this._LeftSideSpecification = leftSide;
            this._RightSideSpecification = rightSide;
        }

        #endregion

        #region Composite Specification overrides

        /// <summary>
        /// Left side specification
        /// </summary>
        public override ISpecification<T> LeftSideSpecification
        {
            get { return _LeftSideSpecification; }
        }

        /// <summary>
        /// Right side specification
        /// </summary>
        public override ISpecification<T> RightSideSpecification
        {
            get { return _RightSideSpecification; }
        }

        /// <summary>
        /// <see cref="Microsoft.Samples.NLayerApp.Domain.Core.Specification.ISpecification"/>
        /// </summary>
        /// <returns><see cref="Microsoft.Samples.NLayerApp.Domain.Core.Specification.ISpecification"/></returns>
        public override Expression<Func<T, bool>> SatisfiedBy()
        {
            Expression<Func<T, bool>> left = _LeftSideSpecification.SatisfiedBy();
            Expression<Func<T, bool>> right = _RightSideSpecification.SatisfiedBy();

            InvocationExpression invokedExpr = Expression.Invoke(right, left.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, invokedExpr), left.Parameters);
        }

        #endregion
    }

Os podreís imaginar que la especificacion OR era similar a esta, simplemente modificando Expression.AndAlso por Expression.Or. Todo funcionaba de maravilla en mis test contra mi mock de los ObjectSet de Entity Framework hasta que pasando las pruebas de integración me di cuenta que el QueryProvider de Entity Framework no soportaba el método Invoke, al contrario que un QueryProvider sobre objetos en memoria, y que por lo tanto esta forma no me servía :-(.

Aunque ahora mismo pondré la solución fijaros como, de una forma elegante, manteniendo el principio de separación de responsabilidades y dejando un concepto de negocio como es un tipo especial de búsqueda perfectamente explícito, se podrían declarar especificaciones como la siguiente

NOTA: Seguramente que alguna vez ha pensado como hacer consultas distintas en función de parámetros usando conjunciones o disjunciones de expressiones, pues esta es una posible solución.

 

    /// <summary>
    /// AdHoc specification for finding orders
    /// by shipping values
    /// </summary>
    public class OrderShippingSpecification
        :Specification<Order>
    {
        #region Members

        string _ShippingName = default(String);
        string _ShippingAddress = default(String);
        string _ShippingCity = default(String);
        string _ShippingZip = default(String);

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor for this specification
        /// </summary>
        /// <param name="shippingName">Shipping Name or null for not include this value in search</param>
        /// <param name="shippingAddress">Shipping Address or null for not include this vlaue in search</param>
        /// <param name="shippingCity">Shipping City or null for not include this value in search</param>
        /// <param name="shippingZip">Shipping Zip or null for not include this value in search</param>
        public OrderShippingSpecification(string shippingName,string shippingAddress,string shippingCity,string shippingZip)
        {
            _ShippingName = shippingName;
            _ShippingAddress = shippingAddress;
            _ShippingCity = shippingCity;
            _ShippingZip = shippingZip;
        }

        #endregion

        #region Specification Overrides

        /// <summary>
        /// <see cref=" Microsoft.Samples.NLayerApp.Domain.Core.Specification.Specification{TEntity}"/>
        /// </summary>
        /// <returns><see cref=" Microsoft.Samples.NLayerApp.Domain.Core.Specification.Specification{TEntity}"/></returns>
        public override System.Linq.Expressions.Expression<Func<Order, bool>> SatisfiedBy()
        {
            Specification<Order> beginSpec = new TrueSpecification<Order>();

            if (_ShippingName != null)
                beginSpec &= new DirectSpecification<Order>(o =>o.ShippingName!=null &&  o.ShippingName.Contains(_ShippingName));

            if (_ShippingAddress != null)
                beginSpec &= new DirectSpecification<Order>(o => o.ShippingAddress !=null && o.ShippingAddress.Contains(_ShippingAddress));

            if (_ShippingCity != null)
                beginSpec &= new DirectSpecification<Order>(o => o.ShippingCity != null && o.ShippingCity.Contains(_ShippingCity));

            if (_ShippingZip != null)
                beginSpec &= new DirectSpecification<Order>(o => o.ShippingZip != null && o.ShippingZip.Contains(_ShippingZip));

            return beginSpec.SatisfiedBy();

        }

        #endregion
    }

Una posible solución para especificaciones And y OR

Os podreís imaginar que seguramente existe más de una aproximación para este tema y que yo probablemente me haya decantado por una aproximación algo dura, pero la verdad es que me parecía la más adecuada. Releyendo algún post sobre el tema me acorde de la serie que Matt Warren tenía sobre el tema y como hacía uso del patrón Visitor para evaluar las expresiones, ExpressionVisitor. Además, navegando, me encontré con un ejemplo aceptable que resolvía este tema en el blog de Colling Meek el cual fué la solución que adopte.

Dada la explicación, lo que necesitamos es la siguiente clase que nos haga una recomposición de las expressiones en vez de un InvocationExpression, esta clase de apoyo es la siguiente:

    /// <summary>
    /// Extension methods for add And and Or with parameters rebinder
    /// </summary>
    public static class ExpressionBuilder
    {
        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // build parameter map (from parameters of second to parameters of first)
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with parameters from the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
            // apply composition of lambda expression bodies to parameters from the first expression 
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.Or);
        }

    }
 

La definición completa por lo tanto de una especificación And queda como sigue:

    /// <summary>
    /// A logic AND Specification
    /// </summary>
    /// <typeparam name="T">Type of entity that check this specification</typeparam>
    public class AndSpecification<T>
       : CompositeSpecification<T>
       where T : class,new()
    {
        #region Members

        private ISpecification<T> _RightSideSpecification = null;
        private ISpecification<T> _LeftSideSpecification = null;

        #endregion

        #region Public Constructor

        /// <summary>
        /// Default constructor for AndSpecification
        /// </summary>
        /// <param name="leftSide">Left side specification</param>
        /// <param name="rightSide">Right side specification</param>
        public AndSpecification(ISpecification<T> leftSide, ISpecification<T> rightSide)
        {
            if (leftSide == (ISpecification<T>)null)
                throw new ArgumentNullException("leftSide");

            if (rightSide == (ISpecification<T>)null)
                throw new ArgumentNullException("rightSide");

            this._LeftSideSpecification = leftSide;
            this._RightSideSpecification = rightSide;
        }

        #endregion

        #region Composite Specification overrides

        /// <summary>
        /// Left side specification
        /// </summary>
        public override ISpecification<T> LeftSideSpecification
        {
            get { return _LeftSideSpecification; }
        }

        /// <summary>
        /// Right side specification
        /// </summary>
        public override ISpecification<T> RightSideSpecification
        {
            get { return _RightSideSpecification; }
        }

        /// <summary>
        /// <see cref="Microsoft.Samples.NLayerApp.Domain.Core.Specification.ISpecification"/>
        /// </summary>
        /// <returns><see cref="Microsoft.Samples.NLayerApp.Domain.Core.Specification.ISpecification"/></returns>
        public override Expression<Func<T, bool>> SatisfiedBy()
        {
            Expression<Func<T, bool>> left = _LeftSideSpecification.SatisfiedBy();
            Expression<Func<T, bool>> right = _RightSideSpecification.SatisfiedBy();

            return (left.And(right));
         }

        #endregion
    }
 

¿Por dónde continuamos?

Dentro de la jerarquía de especificaciones que se propone en el documento de Eric y Fowler podemos encontrar desde la especificación Not hasta una base para LeafSpecifications que tendríamos que construir… es decir, un poquito más de curro para poder disfrutar…

 

Un resumen

La verdad espero que le encontréis utilidad a esta forma de implementar el patrón Specification, y por supuesto, si no lo conociais espero que os levante un poco la curiosidad y veais las bondades que este puede tener  dentro de vuestros desarrollos, por supuesto me encantaría ver vuestras opiniones al respecto y si realmente esta forma de encajar criterios es adecuado para vuestras soluciones…

 

Saludos

Unai Zorrilla