Me gustan las migas con tropezones

Al igual que cuando como migas mi cuerpo me sabe a fiesta, cuando veo una patada a un acceso a datos mi cuerpo se descompone e intento luchar por dejar claro que debes hacer y que es lo que nunca deberías de hacer.

Y ayer en twitter se produjo una conversación que pertenece a la segunda opción “nunca que accedas a datos con EF tienes que utilizar esto”.

https://twitter.com/panicoenlaxbox/status/402507006063300608

Este Viernes tuve la suerte de estar en una charla de Enrique Catalá en #gusenet.

 Debate: de ADO.NET a EF6 con SQL Server y compartir momentos con gente preocupada por el acceso a datos.

Eladio Rincón

Sergio Navarro

Javi

Raúl Serrano

Fran

Y mis dos inseparables Pin y Pon, junto con algunos asistentes más, que no les sigo la pista por twitter.

No muchos la verdad:( para lo importante que puede llegar a ser esto en nuestra profesión.

De todo lo que pudimos hablar que no fue poco y que nos duro un par de días más, que pesaos pero que envidia Paco Monfort, me quedo con esta frase de Eladio

problema no son ORMs. problema es "desprecio" a capa d persistencia.

Sí Eladio, tienes toda la razón, pero tampoco nos lo pone fácil el framework, más bien nos pone tropezones que en las migas están muy buenos pero que no son más que eso, zancadillas y facilidad para cometer errores y no haber pensado las cosas bien desde el principio.

Cuando ayer Sergio León escribió esto

https://twitter.com/panicoenlaxbox/status/402508906066214912

Me salto el chispazo,  algo estaba mal. Y como siempre acostumbro cuando eso pasa a pulsar F12 en el método en cuestión y esperar que me dice el bueno de Visual Studio.

Algo está mal

Es fácil ver que unas son peligrosas y las otras no. Las dos primeras son dos autenticas bombas ejecutadas en EF y lo triste es que se pueden ejecutar:(.

Vamos con un ejemplo sencillo que puedes ejecutar en un proyecto mvc copiando simplemente el código.

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Web;

   4: using System.Linq;

   5: using System.Web.Mvc;

   6: using System.Data.Entity;

   7: namespace MvcApplication12.Controllers

   8: {

   9:     public class HomeController : Controller

  10:     {

  11:         public ActionResult Index()

  12:         {

  13:             using (MyContext ct = new MyContext())

  14:             {

  15:                 ct.MyClass.Add(new MyClass() { Name = "Una locura utilizarlo" });

  16:                 ct.MyClass.Add(new MyClass() { Name = "No utilizarlo en EF es estar cuerdo" });

  17:                 ct.SaveChanges();

  18:             }

  19:  

  20:             using (MyContext ct = new MyContext())

  21:             {

  22:  

  23:                 var result = ct.MyClass.Where(Filter);

  24:                 foreach (var item in result)

  25:                 {

  26:                     Console.Write(item);

  27:                 }

  28:             }

  29:  

  30:             

  31:  

  32:             ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

  33:  

  34:             return View();

  35:         }

  36:  

  37:         public static bool Filter(MyClass myclass)

  38:         {

  39:             return myclass.Name.Contains("locura");

  40:         }

  41:  

  42:           

  43:         public ActionResult About()

  44:         {

  45:             ViewBag.Message = "Your app description page.";

  46:  

  47:             return View();

  48:         }

  49:  

  50:         public ActionResult Contact()

  51:         {

  52:             ViewBag.Message = "Your contact page.";

  53:  

  54:             return View();

  55:         }

  56:     }

  57:  

  58:     public class MyContext : DbContext

  59:     {

  60:         public MyContext() : base("DefaultConnection") { }

  61:         public DbSet<MyClass> MyClass { get; set; }

  62:     }

  63:  

  64:     public class MyClass

  65:     {

  66:         public int Id { get; set; }

  67:         public string Name { get; set; }

  68:     }

  69:     

  70: }

 

Quiero que nos centremos en este fragmento de código.

   1: using (MyContext ct = new MyContext())

   2:             {

   3:                 ct.MyClass.Add(new MyClass() { Name = "Una locura utilizarlo" });

   4:                 ct.MyClass.Add(new MyClass() { Name = "No utilizarlo en EF es estar cuerdo" });

   5:                 ct.SaveChanges();

   6:             }

   7:  

   8:             using (MyContext ct = new MyContext())

   9:             {

  10:  

  11:                 var result = ct.MyClass.Where(Filter);

  12:                 foreach (var item in result)

  13:                 {

  14:                     Console.Write(item);

  15:                 }

  16:             }

Estamos insertando dos registros en nuestra base de datos y creyendo que estamos filtrando de esta aquellos registros cuyo campo “Name” contiene “locura”.

¿Tu crees?, pues la respuesta es sencilla, No. Estás ejecutando contra tu motor de base de datos esta sentencia sql.

“select id,name from MyClass”

Y pensando que lo que querías ejecutar es esto otro.

select id,name from MyClass where name like ‘%’ + @name + ‘%’

Y esto, ¿que es? pues si tengo dos registros como es el caso nada pero si tengo cientos,miles o millones lo que estás haciendo es traerte toda la tabla al cliente y después filtrar en el cliente con LINQ TO OBJECT, vamos que a la postre es como si hubiésemos escrito esto.

   1: using (MyContext ct = new MyContext())

   2: {

   3:     foreach (var item in ct.MyClass)

   4:     {

   5:         if (item.Name.Contains("Locura"))

   6:             Console.WriteLine(item);

   7:     }                

   8: }

Ahora amigo Sergio si lo ves claro y tu también Quique🙂 ,es que no se puedes trabajar tanto y hay que escribir los repositorios por las mañanas, cuando uno está fresco y no fiarse siempre de lo que dice Resharper:).

Cuando me quejaba del Framework no es que a mí me guste patalear sino que cuando algo está mal me gusta decirlo claro y conciso, vamos que soy poco diplomático “está mal” y en este caso para mi es un error definir los métodos extensores de IEnumerable<T> Linq to Object en el mismo namespace que los extensores de IQueryable<T>.

¿Por qué no se creo un namespace llamado System.Linq.Query por ejemplo?

De esta forma Resharper no se equivoca puesto que en vez de utilizar using System.Linq usas System.Linq.Query y esos métodos no aparecen:).

Consejos.

1. Si no sabes de bb.dd y estás definiendo consultas linq con EF, tienes un serio problema. El mayor cuello de botella de cualquier aplicación es la bb.dd, así que aprende. Por mucho que te hayan contado que eso ya no se lleva.

2.Utiliza siempre que trabajes con EF “query syntax” en vez de “method sintax”, por muy friki que te parezca. Se asemeja más a la realidad de una bb.dd aunque el resultado puede ser el mismo en algunos casos, pero no en este.

En este ejemplo en ejecución hubiese saltado una exception del tipo System.NotSupportedException si se hubiese escrito con query syntax.

Method Syntax.

var result = ct.MyClass.Where(Filter) // Funciona pero con resultados desagradables.

Query Syntax.

var result = from item in ct.MyClass

                   where Filter(item)

                   select item. //Compila pero en ejecución lanza una exception del tipo System.NotSupportedException.

3. Ten presente que cualquier cosa que en Entity Framework no devuelva un IQueryable<T> huele mal ,excepto FirstOrDefault, First,Count,Sum etc,etc.

Referencias.

Enumerable.Where Method 

No lo utilices nunca en Entity Framework.

Queryable.Where Method

Utilizalo suiempre con Entity Framework.

Linq to Object

LINQ to Entities

Sintaxis de consultas y sintaxis de métodos en LINQ (C#)

Query syntax vs. Method syntax

En el proximo #yonodetuxaml de #gusenet (4,5,6 de Abril de 2014)vamos a regalar 30 Litros de cerveza que le acabo de ganar a Pon y Migas con tropezones Sonrisa. Apuntalo Pin que paga Pooooon

PD. Sergio últimamente me haces trabajar mucho…..