Multiple entity updates with Entity Framework – EF Fetch Updates

 


[Updated Sample 22-07-2008 ]


New Features


Support call methods in expressions ( contains..)


Support external call methods in expressions


Support various entity members call methods ( substring, tolower, toupper )


Support access to variables in expressions


[Updated Sample 17-07-2008 ]


Fix bugs


 


In Spanish 


Before you start reading this post, ensure you have some free time, because the post can be a bit longJ.


Probably, many of those who have played with Entity Framework have asked himself how to update or delete several entities at a time; for instance, how to indicate EF that we want to update all customers in Madrid to certain values. Actually, once they knew that there is no support by default for these tasks, some have researched how much worst is to execute several independent updates and/or deletes (even when executed in batch) with respect to a unique UPDATE or DELETE sentence specifying a filter, and have posted about the differences in the MSDN Entity Framework newsgroups. In this post, I pretend to show a possible implementation of this task; I have served here of several ideas taken from the blog of Alex James, member of the EF product team.


Let’s start from the beginning:


About metadata


One of the most important problems we must solve along this way is to obtain the correspondences, within an EDM conceptual model, between our model entities and the underlying relational database. This task, which could seem very simple (at least, that’s what I thought when I started researching about this matter), is not easy at all. MetadataWorkspace, the main class within EF that allows us to work with conceptual model’s metadata, does not offer the possibility of obtaining information on the C-S space, the mapping space between objects and storage. So here we hit our first roadblock… After asking several questions to the product team (sorry Danny Simmons), it is clear that, at least in this first version, there won’t be a way to obtain the entity-to-table mapping information. Alex James, in one of his posts, talks about querying EntitySet elements present in conceptual models; there he comments that, given a simple entity within a conceptual model, if we check out its SELECT query we’ll find a sentence with the form SELECT [ColumnName] AS [PropertyName] … FROM [Table] … This opened a possible way to know how to associate the name of a property with the name of the underlying column, to solve the metadata problem. Regretfully, I have observed that if we rename an entity’s property, the generated query doesn’t reflect this change and continues showing [ColumnName] as [PropertyName], where [PropertyName] is the old name of the property.


At this point, I decided that I should directly access the metadata specification, getting to the resources that the Entity modeler embeds into the resulting assembly (this is the default option since VS2008 SP1 Beta). Using this approach, the only thing necessary is to parse the MSL used by the EF infrastructure. The following XML fragment represents part of the contents of one such MSL document, where we can see that the eBook entity maps to the eBook table, and the mapping of the different properties of the entity.


<EntitySetMapping Name=«eBook«>


<EntityTypeMapping TypeName=«IsTypeOf(MAmazonModel.eBook)«>


<MappingFragment StoreEntitySet=«eBook«>


<ScalarProperty Name=«idProducto« ColumnName=«idProducto« />


<ScalarProperty Name=«ISBN_13« ColumnName=«ISBN_13« />


<ScalarProperty Name=«ISBN_10« ColumnName=«ISBN_10« />


<ScalarProperty Name=«Edicion« ColumnName=«Edicion« />


<ScalarProperty Name=«Paginas« ColumnName=«Paginas« />


<ScalarProperty Name=«Indice« ColumnName=«Indice« />


<ScalarProperty Name=«LenguajeLibro« ColumnName=«Lenguaje« />


<ScalarProperty Name=«CapituloEjemplo« ColumnName=«CapituloEjemplo« />


</MappingFragment>


</EntityTypeMapping>


</EntitySetMapping>


Query expressiveness


Once we have in hand the information regarding the mapping of our conceptual entities to the underlying database, the next question is how to implement a mechanism such that the developer can specify the different parts of an UPDATE sentence (SET and WHERE), and a DELETE sentence (WHERE). As we are working with EF and LINQ to Entities, we don’t want to lose the blessing of strong typing; our goal is to express these parts using elements of our programming language, and not «dumb» character strings. So here we will leverage expression trees, used internally by EF, LINQ to SQL and other IQueryable LINQ providers. Thanks to expression trees, we will offer the programmer the possibility of writing strongly typed queries from which SQL sentences will be generated automatically at runtime. For more information on expression trees, I recommend the book of my friend Octavio Hernandez, «C#3.0 y LINQ».


For the SET part of our UPDATE sentence we can use the MemberInitExpression expression type, which allows us to specify how a concrete type is initialized. The next sentence shows an example of this kind of expression:


    Expression<Func<Customer, Customer>> memberExpression = c => new Customer() { FirstName=«Unai» };


Note how the use of an expression of this type allows us to specify, in a relatively easy way, the SET part of an UPDATE sentence; in this case, the intended translation is something similar to UPDATE [TABLE] SET FirstName=’Unai’.


For the WHERE part of the UPDATE y DELETE sentences, we could use a BinaryExpression, just like this:


Expression<Func<Product, bool>> binary = p => p.id > 21 && p.Title != «The title»;


Just like in the previous case, it’s quite intuitive to see that this could be translated to something like WHERE id > 21 AND Title <> ‘The title’


At this point, we already know how we will allow the specification of the different parts of our sentences, using expression trees formed of nodes like those previously shown. So now we must create a parser for those expressions, a task that can be easier or tougher depending on the features we would like to support. The following method can serve as a very simple SET parser, and is shown so that you can understand the work involved in generating the corresponding SQL fragment.


static void ParseExpression(Expression<Func<Customer, Customer>> expression)


{


if (expression!= null)


{


MemberInitExpression initExpression = inner.Body as MemberInitExpression;


if (initExpression != null)


{


var result = (from m in initExpression.Bindings.OfType<MemberAssignment>()


select new { PropertyName = m.Member.Name, Value = m.Expression.ToString() }).ToList();


StringBuilder sb = new StringBuilder();


sb.Append(«SET «);


sb.Append(result[0].PropertyName);


sb.Append(«=»);


sb.Append(result[0].Value);


for (int i = 1; i < result.Count; i++)


{


sb.Append(«,»);


sb.Append(result[i].PropertyName);


sb.Append(«=»);


sb.Append(result[i].Value);


}


Console.WriteLine(sb.ToString());


}


else


Console.WriteLine(«The expression is not valid»);


}


else


Console.WriteLine(«The expression is not valid»);


}


Among the many different problems left for parser of this kind to solve we must mention the translation of assignments like new Customer () { Fecha= DateTime.Now }, the use of variables inside those assignments, method calls…


Closing the circle… ¡or opening it!


Now that we have a mechanism to access the metadata information in a not very elegant, but effective wayJ, and we have way to specify the DML operations we’d like to execute, the only thing left is to implement the execution of those commands against the relational store. For that matter, we can create extension methods like UpdateAll() o DeleteAll() for ObjectQuery<T>, the base class for all queryable objects in EF. From ObjectQuery we can have access to the EF connection string, which includes the specific storage connection provider and connection string, so this operation is not difficult to implement.


<add name=»MAmazonEntities» connectionString=»metadata=res://*/MAmazonModel.csdl|res://*/MAmazonModel.ssdl|res://*/MAmazonModel.msl;provider= System.Data.SqlClient;provider connection string=&quot;Data Source=.SQLEXPRESS;Initial Catalog=MAmazon;Integrated Security=True;MultipleActiveResultSets=True&quot;» providerName=»System.Data.EntityClient» />


With this post, I’m including a sample that shows all we have talked about here. In that implementation, expression tree analyzers are capable of handling additional elements like expressions with assignments, like in the following fragment:


using (MAmazonEntities entities = new MAmazonEntities())


{


entities.Product.UpdateAll(p => new Product() { LaunchDate = new DateTime(2008,2,2), Title = «Unai» }, p => p.id > 21 && p.Title != «Hello»);


entities.Product.UpdateAll(p => new Product() { LaunchDate = DateTime.Now }, p => p.id > 20);


entities.Product.DeleteAll(p => p.id > 20);


Console.ReadLine();


}


Hope it helps!


Sample entity Framework

Actualizaciones de múltiples entidades con Entity Framework- EF Fetch Updates


[Updated Sample 22-07-2008 ]


Nuevas mejoras


Soporte de llamada a métdos


Soporte de llamada a métodos de miembros de entidades


Soporte de acceso a variables dentro de las expressiones


[Updated Sample 17-07-2008 ]


English Version 


Amigo mio, antes de empezar a leer este post asegúrese de que dispone de algo de tiempo, ya que puede que sea algo extenso J.


Seguramente muchos de los que habéis jugueteado con Entity Framework os habréis preguntado cómo actualizar o borrar un conjunto de entidades, es decir, como indicarle a EF que deseamos actualizar todos los clientes de Madrid a unos determinados valores. De hecho, una vez conocido que esta tarea no se puede realizar, algunas personas, después de llevarse las manos a la cabeza, se han puesto a investigar cuanto de malo es la realización de múltiples consultas de actualizaciones y/o borrados, aunque se realicen en batch, con respecto a una sentencia update o delete con un filtro específico y han posteado sobre estas diferencias en los foros de MSDN sobre Entity Framework. A lo largo de este post pretendo mostrar una posible implementación para realizar esta tarea, para ello me he servido de algunas ideas de Alex James, miembro del grupo de producto de EF, en el que hacía algunas consideraciones sobre cómo se podría realizar este trabajo.


Empecemos por el principio:


Acerca de la metadata


Uno de los puntos más importantes que necesitamos resolver para realizar esta tarea es como averiguar las relaciones de nuestras entidades dentro del modelo conceptual de EDM , Entity Data Model, con las tablas de la base de datos subyacente con la que estemos trabajando. Aunque esto pudiera parecer que lo deberíamos de tener resuelto de una forma sencilla, así lo pensaba yo cuando ya hace tiempo me puse a investigar esto, para nada es algo trivial. MetadataWorkspace, una de las clases de los contextos de EF, que nos permite trabajar con la metadata de los modelos conceptuales no nos permite obtener información del espacio CSSpace, espacio de mapeo entre objetos y almacenamiento. Llegamos por lo tanto al primer inconveniente…. Después de lanzar varias preguntas al grupo de producto, pobre Danny Simmons, tengo claro que no hay, ni habrá en esta primera versión, ninguna forma pública de acceder a la información del mapeo de las entidades con las tablas. Alex James, en uno de sus post hace una referencia sobre las consultas de los EntitySet presentes en los modelos conceptuales, en este post comenta que dada una entidad sencilla dentro de un modelo conceptual, si revisamos la consulta SELECT de esta podríamos ver una estructura de sentencia en forma SELECT [NombreColumna] AS [NombrePropiedad]…. FROM [TABLA]… Al conocer esto se abre una vía interesante para averiguar cómo asociar el nombre de una propiedad con el nombre de una columna dada con lo que tendríamos resuelto el problema de conocer la metadata. Para mi desgracia, después de la ilusión que esto supuso, pude comprobar que si modificábamos el nombre de una propiedad de una entidad, la consulta generada no reflejaba este cambio y seguía enviando [NombreColumna] as [NombrePropiedad] dónde [NombrePropiedad] era el valor antiguo de la misma.


Llegados a este punto, en el que me daba por medio perdido, decidí que una buena vía era atacar directamente a la especificación de la metadata, accediendo a los recursos incrustados que el modelador de entidades generaba, esta es la opción por defecto a partir de la primera beta del SP1 de VS 2008. Con esto lo único que tenía que hacer era interpretar un XML, el MSL que usaban también ellos dentro de la infraestructura de EF. El siguiente trozo de XML representa parte del contenido de uno de estos archivos incrustados, que por supuesto puede ser consultado de una forma muy sencilla usando Linq To XML, en el que vemos que la entidad eBook está asociada con una tabla eBook y los mapeos de las distintas propiedades de la entidad.


<EntitySetMapping Name=«eBook«>


<EntityTypeMapping TypeName=«IsTypeOf(MamazonModel.eBook)«>


<MappingFragment StoreEntitySet=«eBook«>


<ScalarProperty Name=«idProducto« ColumnName=«idProducto« />


<ScalarProperty Name=«ISBN_13« ColumnName=«ISBN_13« />


<ScalarProperty Name=«ISBN_10« ColumnName=«ISBN_10« />


<ScalarProperty Name=«Edicion« ColumnName=«Edicion« />


<ScalarProperty Name=«Paginas« ColumnName=«Paginas« />


<ScalarProperty Name=«Indice« ColumnName=«Indice« />


<ScalarProperty Name=«LenguajeLibro« ColumnName=«Lenguaje« />


<ScalarProperty Name=«CapituloEjemplo« ColumnName=«CapituloEjemplo« />


</MappingFragment>


</EntityTypeMapping>


</EntitySetMapping>


La expresividad de las consultas


Bien, ya tenemos la información relativa al mapeo de nuestras entidades conceptuales con las columnas y tablas de la base de datos. Ahora nos surge la pregunta sobre como implementar un mecanismo para que el desarrollador pueda especificar las distintas partes de una sentencia UPDATE, el SET y el WHERE, y de una sentencia DELETE, el WHERE. Ya que estamos trabajando con EF y Linq To Entities no queremos perder la capacidad de trabajar de una forma fuertemente tipada, nuestro objetivo es poder especificar estas partes usando elementos del lenguaje, no mediante el uso de simples secuencias de caracteres entrecomilladas. Con la llegada de Linq en .NET 3.5 tenemos la posibilidad de usar árboles de expresiones, los mismos que internamente usan EF y L2SQL por ejemplo. Gracias a estos árboles de expresiones podemos darle a los programadores una forma de escribir consultas de una forma tipada que posteriormente nosotros podremos evaluar revisando el árbol creado, le recomiendo encarecidamente desde aquí el libro de mi gran amigo y mejor persona Octavio Hernandez, C#3.0 y LinQ de Krasis Press.


Para la parte SET de nuestra sentencia UPDATE disponemos del tipo de expresión MemberInitExpression, la cual nos permitirá especificar cómo se inicia un determinado tipo, las siguientes líneas de código muestra un ejemplo de este tipo de expresión:


    Expression<Func<Customer, Customer>> memberExpression = c => new Customer() { FirstName=«Unai» };


Puede fijarse, como el uso de este tipo de expresión nos permite de una forma más o menos sencilla especificar el SET de nuestra sentencia, en este caso lo que querríamos decir sería algo similar a UPDATE [TABLA] SET FirstName=’Unai’.


Para la parte WHERE de las sentencias UPDATE y DELETE podríamos usar el tipo de expresión BinaryExpression, tal cual la siguiente:


Expression<Func<Producto, bool>> binary = p => p.idProducto > 21 && p.TitProduct != «Titulo»;


Al igual que con la expresión anterior es bastante intuitivo ver que lo que se quiere especificar es algo como WHERE idProducto > 21 AND TitProducto <> ‘Titulo’


Llegados a este punto ya conocemos como queremos darle al programador la forma de especificar las distintas partes de nuestras sentencias, con el uso de árboles de expresiones como los anteriores, por lo tanto toca el trabajo de crearse un analizador de estas expresiones, algo que podemos hacer fácil o muy complicado dependiendo de todas las posibilidades que estemos dispuestos a soportar. En el siguiente ejemplo de código podemos ver un ‘analizador’ muy sencillo y bastante limitado pero útil para que entienda realmente el trabajo que es necesario realizar.


static void ParseExpression(Expression<Func<Customer, Customer>> expression)


{


Expression<Func<Customer, Customer>> inner = expression as Expression<Func<Customer, Customer>>;


if (inner != null)


{


MemberInitExpression initExpression = inner.Body as MemberInitExpression;


if (initExpression != null)


{


var result = (from m in initExpression.Bindings.OfType<MemberAssignment>()


select new { PropertyName = m.Member.Name, Value = m.Expression.ToString() }).ToList();


StringBuilder sb = new StringBuilder();


sb.Append(«SET «);


sb.Append(result[0].PropertyName);


sb.Append(«=»);


sb.Append(result[0].Value);


for (int i = 1; i < result.Count; i++)


{


sb.Append(«,»);


sb.Append(result[i].PropertyName);


sb.Append(«=»);


sb.Append(result[i].Value);


}


Console.WriteLine(sb.ToString());


}


else


Console.WriteLine(«La expression no es una expresión valida»);


}


else


Console.WriteLine(«La expression no es una expresión valida»);


}


De entre los muchos trabajos que le quedarían a un analizador para este tipo de árboles serían como resolver asignaciones como new Customer (){Fecha= DateTime.Now} o el uso de variables dentro de las asignaciones, o llamadas a métodos…….


Cerrando el círculo… o abriéndolo!!


Ya sabemos cómo podemos acceder a toda la información de la metadata, de una forma poco elegante pero efectiva J y sabemos cómo proporcionar a los programadores una forma de especificar las operaciones de DML que queremos realizar, aunque el trabajo del analizador de los árboles sea realmente algo complicado si queremos que soporte muchas de las casuísiticas comunes… por lo que solamente queda ejecutar los comandos obtenidos de lo anterior. Para ello podemos crear métodos extensores de ObjectQuery<T> como UpdateAll y DeleteAll, al tener acceso a ObjectQuery también tenemos acceso a la cadena de conexión, la cual especifica el proveedor específico de conexión y la cadena de conexión contra el almacenamiento, así que otro problema resuelto.


<add name=»MamazonEntities» connectionString=»metadata=res://*/MamazonModel.csdl|res://*/MamazonModel.ssdl|res://*/MamazonModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.SQLEXPRESS;Initial Catalog=Mamazon;Integrated Security=True;MultipleActiveResultSets=True&quot;» providerName=»System.Data.EntityClient» />


Junto a este post os adjunto un ejemplo de todo lo comentado anteriomente, que podéis descargar desde aquí, en esta implementación los analizadores de árboles de expresiones son capaces de analizar algunos elementos extra como el uso de expresiones con asignaciones al estilo de las siguientes:


using (MamazonEntities entities = new MamazonEntities())


{


entities.Producto.UpdateAll(p => new Producto() { FechaLanzamientoProduco = new DateTime(2008,2,2), TitProduct = «Unai» }, p => p.idProducto > 21 && p.TitProduct != «hola»);


entities.Producto.UpdateAll(p => new Producto() { FechaLanzamientoProduco = DateTime.Now }, p => p.idProducto > 20);


entities.Producto.DeleteAll(p => p.idProducto > 20);


Console.ReadLine();


}


Sample entity Framework

Entity Framework y la generalización de consultas

El otro día, dando la charla de Entity Framework (organizada conjuntamente con Microsoft, CIIN y Plain Concepts), surgieron hacia el final de la charla ciertas preguntas con respecto al uso de EF en proyectos reales. La pregunta más destacada y también la más común de las que me han realizado en este tipo de eventos tiene que ver con la generalización de consultas. Es decir, cómo aprovechar las capacidades de EF para no tener que repetir muchos de los métodos de consulta generales para cada una de las entidades. El objetivo es, en definitiva, dado un conjunto de entidades generadas por EDM, cómo hacer para sin apenas esfuerzos tener implementados los métodos de consulta tradicionales como GetAll, GetByFilter, etc. y los métodos de manipulación comunes como Add, Update y Delete. Por supuesto, una de las condiciones indispensables es que todo el trabajo sea tipado; es decir, que para cada una de las entidades dispongamos de estos métodos de consulta generalizados para su tipo específico.

A continuación veremos un esbozo simple de cómo conseguir esto, aunque hay varias alternativas, y por supuesto lo siguiente será un ejemplo sencillo que ofrece muchas más posibilidades. Si se fija un poco en el código generado por EDM, verá como el contexto de trabajo, ObjectContext, dispone de los distintos ObjectQuery<T> sobre los que podemos hacer consultas. Para ello se basa en el método genérico de ObjectContext
CreateQuery, al que le pasamos una sentencia de Entity SQL similar a la siguiente:

public
global::System.Data.Objects.ObjectQuery<Producto> Producto

{

get

{

if ((this._Producto == null))

{

this._Producto =

base.CreateQuery<Producto>(«[Producto]»);

}


return
this._Producto;

}

}   Â

 

Fíjese como la expresión e-SQL es el nombre del EntitySet con el que estamos trabajando. Si deseamos crearlo para un EntityType específico, la expresión seria OfType(([Producto]),([Model].[Audio])), por poner un ejemplo.

 

Llegados a este punto, ya podríamos construir una clase similar a la siguiente:

 

public
class
EntityQuery<T>


where T:EntityObject

{


public
string EntitySetName { get; set; }

 


public EntityQuery(string entitySetName)

{


this.EntitySetName = entitySetName;

}

 


private
ObjectQuery<T> CreateQuery(ObjectContext context)

{


return context.CreateQuery<T>(EntitySetName);

}

}

 

Si se da cuenta, EntityQuery es una clase genérica, aunque solamente para entidades de EDM, que se inicializa con una expresión determinada que nos permitirá crear un ObjectQuery de un tipo en particular, sobre el cual ya podremos construir nuestras consultas y realizar los trabajos de actualización, inserción, etc. A continuación incluimos el método que nos permitirá obtener todos los elementos de ObjectQuery construido a modo de ejemplo:

 

public
List<T> GetAll()

{

using (Entities context = new
Entities())

{

ObjectQuery<T> query = this.CreateQuery(context);


return query.ToList<T>();

}

}

 

Bien, ya tenemos una clase EntityQuery genérica a la que agregaremos todas las consultas que pueden aplicarse a las entidades de nuestro modelo; ahora solamente queda particularizarlas. Para ello podemos optar por dos vías: la primera es crear distintas instancias de esta clase especificando el EntityObject particular e inicializando en nombre del EntitySet en su constructor; la segunda consiste en crear herencias de esta clase en las que inicializaremos el valor del nombre del EntitySet llamando a la clase base. Un ejemplo de esta última vía es:

 

public
class
ProductoQuery

:EntityQuery<Producto>

{


public ProductoQuery()

:base(«[Producto]»)

{

}

}

 

Con esto, ProductoQuery ya dispondrá sin más trabajo de todos los métodos de consulta implementados en EntityQuery particularizados para ese EntityObject (Producto en nuestro caso), y además también dispondrá de una posible vía para crear las consultas específicas que deseemos tener sobre esta entidad.

 

Fíjese además en que la posible automatización de estas clases es trivial usando cualquier framework de plantillas de los que tenemos disponibles en la actualidad.

 

 

Conclusión:

Lo realizado hasta aquí es un ejercicio simple, que podríamos ampliar mucho más permitiendo el uso de árboles de expresión en los métodos genéricos de EntityQuery, creando un formato de filtros para no tener que conocer e-SQL, y por supuesto aplicando las distintas casuísticas a la hora de actualizar, insertar y borrar elementos del contexto; pero como punto de partida para el que desee implementar una solución genérica creo que es bastante adecuado.

 

Un saludo,

 

Unai