A lo largo de este post trataré de mostrar como consturir e invocar funciones definidas en el modelo, más conocidas por su nombre en lengua anglosajona como ‘Model defined functions’. En algunas ocasiones, con el fin de facilitar las consultas sobre un contenedor de trabajo, resulta ideal poder definir una función directamente en nuestro modelo conceptual que sea capaz de realizar este trabajo y hacer mucho más sencillas y claras las consultas ejecutadas en LINQ To Entities.
Con el fin de mostrar un ejemplo, suele ser la via más clara para enseñar el funcionamiento de casi cualquier cosa, partiremos de una entidad que llamaremos Producto y que tendrá la información que puede verse en la siguiente imagen.
Dentro de esta entidad, como podrá observar existe una propiedad llamada FechaLanzamiento a partir de la cual podríamos ver la fecha en la que un producto se ha puesto a la venta. Ahora imagine que muchas de sus consultas filtran el conjunto de resultados en función de lo años que este producto lleva en la calle. Este escenario podría ser válido para crear una función directamente en el modelo y poder hacer uso de ella directamente en nuestras consultas.
El primer paso para crear una función del modelo consiste en abrir este con cualquier editor XML, por ahora no podemos hacerlo en el diseñador, tendremos que seguir esperando para estos temas. Una vez abierto el fichero de modelo, edmx, con nuestro editor XML nos dirigiremos a la sección de nuestro espacio C, anotada por el comentario XML <!—CSDL content—>. Dentro de esta sección podremos crear una entrada como la siguiente:
<Function Name=»YearSince» ReturnType=»Edm.Int32″>
<Parameter Type=»MamazonModel.Producto» Name=»producto»>
</Parameter>
<DefiningExpression>
Year(CurrentDateTime())-Year(producto.FechaLanzamiento)
</DefiningExpression>
</Function>
Con estas sencillas lineas, acabamos de crear nuestra función del modelo, en la que podrá observar como especificar los parámetros de entrada,salida y la definición propia de la función. Como podrá observar tanto los parametros de entrada como los de salida pueden ser tipos escalares, definidos siempre con el espacio de nombres Edm, como entidades o incluso colecciones de entidades, no cubierto en este ejemplo.
Una vez definida nuestra función del modelo, tendremos que ver como invocarla, para lo cual tendremos distintas alternativas. La primera consiste en definir un método en una clase personalizada que contenga la firma de estas funciones.Para ello crearemos una clase denominada ModelFunctions y agregaremos a esta la firma de nuestra función.
1 2 3 4 5 6 7 8 9 10 11 |
<span class="rem">/// <summary></span> <span class="rem">/// Model defined functions set</span> <span class="rem">/// </summary></span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> ModelFunctions { [EdmFunction(<span class="str">"MamazonModel"</span>,<span class="str">"YearSince"</span>)] <span class="kwrd">public</span> <span class="kwrd">static</span> Int32 YearSince(Producto producto) { <span class="kwrd">throw</span> <span class="kwrd">new</span> NotSupportedException(); } } |
El único requisito a la hora de definir estas funciones está en el uso del atributo EdmFunction, el cual debe de contener el espacio de nombres y el propio nombre de la función ( son case sensitive ). Por supuesto, no es necesario realizar implementación ya que esta función será llevada al motor de base de datos correspondiente para que la resuelva.
Ahora que ya tenemos creada la función solamente nos queda probarla:
1 2 3 4 5 6 |
<span class="kwrd">using</span> (MamazonEntities context = <span class="kwrd">new</span> MamazonEntities()) { var result = from p <span class="kwrd">in</span> context.Productoes <span class="kwrd">where</span> ModelFunctions.YearSince(p) > 1 select p; } |
1 |
|
1 |
|
NOTA: Como curiosidad puede observar la clase SqlFunctions dentro del espacio de nombres System.Data.Objects.SqlClient y como esta tiene definida todas las funciones propias de Sql Server para que podamos hacer uso dentro de nuestras consultas de igual forma que nuestra función anterior.
La siguiente forma de ejecutar una función definida en el modelo es como un método propio de nuestro contenedor de trabajo, algo que puede aportarnos una via de localización y mantenimiento algo mejor en algunas ocasiones. Para hacer este trabajo, basta con que creeemos la firma del método anterior dentro de la clase parcial de nuetro contendor como vemos a continuación.
1 2 3 4 5 6 7 8 9 10 |
<span class="kwrd">public</span> <span class="kwrd">partial</span> <span class="kwrd">class</span> MamazonEntities { [EdmFunction(<span class="str">"MamazonModel"</span>, <span class="str">"YearSince"</span>)] <span class="kwrd">public</span> Int32 YearSince(Producto producto) { <span class="kwrd">return</span> <span class="kwrd">this</span>.QueryProvider.Execute<Int32>(Expression.Call(Expression.Constant(<span class="kwrd">this</span>), (MethodInfo)MethodInfo.GetCurrentMethod(), Expression.Constant(producto,<span class="kwrd">typeof</span>(Producto)))); } } |
1 |
|
En este caso el cuerpo del método si debe de contener código indicando la proveedor de consultas como realizar la invocación, no se asuste querido lector, este mismo código le valdría para todas sus funciones, solamente debería sustituir el tipo genérico del metodo Execute por el tipo de retorno y el typeof del parámetro de entrada.
Espero que esta entrada les sea de interes,
saludos
Unai