Buscando al culpable

Os pongo en antecedentes Sql Azure no soporta transacciones distribuidas y para confirmarlo podéis leer esta entrada TransactionScope() in Sql Azure, o por lo menos con ese mismo escenario es con el que yo me he encontrado.

La aplicación está desarrollada utilizando los siguientes componentes.

  • Aplicación desarrollada en MVC3
  • Entity Framework 4.2
  • Sql Azure

Una de las reglas de negocio de esta aplicación es que a la entidad cliente se le debe de asociar un usuario, pues bueno que fue lo que hice, para no reinventar la rueda y puesto que MVC3 me dota de la posibilidad de utilizar MemberShip Provider.

Vamos a utilizar el metodo MemberShip.CreateUser y en la entidad Cliente creo una columna que sea un Guid y que haga referencia a este, aparentemente solucionado en un entorno de desarrollo, pero cuando hago el despliegue en Azure me encuentro con un magnífico error, que resumiendo me esta confirmando que este no soporta transacciones distribuidas.

Nos queda localizar quien es el culpable de este error y nos ponemos manos a la obra.

1. Transaction Scope. Primer descartado puesto que en otros puntos de la aplicación se está utilizando y no generaba ningún error.

2. MemberShip Provider. Para que TransactionScope cree una transacción distribuida una de las condiciones es que la cadena de conexión sea diferente. Así que vamos a confirmar que MemberShip no altera la cadena de conexión. Donde miramos? Pues en la propiedad  Provider del objeto MemberShip y dentro de este en el campo privado _sqlConnectionString.

Esto lo podemos hacer de dos formas para el más friki con System.Reflection utilizando este código.

   1: var connectionStringField = Membership.Provider.GetType()

   2: .GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);

   3:  

   4: var conexion = connectionStringField.GetValue(Membership.Provider);

Y para los que no tienen ganas de complicarse la vida que utilicen “inspección rapida” de visual studio o cualquier otro mecanismo yo utilizo este puesto que es rápidoSonrisa Shift+F9, bueno eso lo dejo a elección del consumidor.

Dibujo3

Pues me parece que solo nos queda un candidato, será será EntityFramework, pues sí hemos acertado y por qué?.

3. EntityFramework. A partir de la versión 4.2 hace cosas por nosotros, que no se si las deberían de hacer pero sin preguntarlo agregan a nuestra cadena de conexión “Application Name=EntityFrameworkMUE” si esta en nuestro config no contiene “Application Name”. Y de verdad que no miento y para ello lo mejor es abrir con Reflector EntityFramework.dll y en el objeto InternalConnection localizamos un metodo estático que se llama.

“AddAppNameCookieToConnectionString”

No muestro el código completo pero si un aperitivo de lo que haceSonrisa

   1: builder.ApplicationName = "EntityFrameworkMUE";

   2: connection.ConnectionString = builder.ToString();

   3: return connection.ConnectionString;

Redoble de tambores. a partir de la versión 4.2 EntityFramework es el causante de generar transacciones distribuidas, puesto que agrega a la cadena de conexión cosas sin nuestro permiso.

En esta otra entrada del foro de c# podéis encontrar opiniones de otras personas con las que coincido en su totalidad Transaction Scope y sobre todo lo que más me alegra es que en ese foro se muestre la solución a una respuesta que en otro con muchísimo prestigio se encuentre esto Using TransactionScope with Entity Framework code first and universal providers, no somos tan malos. Así que a participar, que entre todos podemos hacer que la cosa mejore

Solución.

Agregar a nuestra cadena de conexion en el config “Application Name=No me ayudes tanto que me gusta hacerlo a mi”

Conclusiones.

Sencillas.

1. Que MVC no debería incorporar MemberShip y mucho menos crear un Controlador AcountController con todo el mecanismo incorporado. Uno es abierto y fácil de configurar “MVC” y el otro es hermético como una lata, MemberShip. Por algo se creo en 2005.

2.Que le agradezco a EntityFramework todo lo que me ahorra, pero que no haga cosas de este tipo,  que tenga yo la libertad de configurar lo que quiera y como quiera. A los señores del equipo yo les invitaría a tomarse unas vacaciones en conjunto con el equipo de MVC.

3. Que estamos en un momento en el que los cambios tan rápidos nos están haciendo caer en este tipo de cosas, detalles que son muy difíciles de resolver y que por tanto, con un poco más de calma quizá todos seamos más felices, quien produce y quien consume.

Entity Framework y los indices únicos.

Ya me imaginaba yo que después de ver con Profiler las sentencias que genera  con algún que otro problema nos íbamos a encontrar.

Vamos a plantearnos el siguiente problema, tenemos una entidad países con las siguientes propiedades.

   1: public class Pais

   2: {

   3:     public int Id { get; set; }

   4:     public string Nombre { get; set; }

   5:     public string Codigo { get; set; }

   6: }

Utilizando EF 4.3 y Migrations vamos a generar la tabla en la bb.dd y uno de los requerimientos es que “Codigo” sea único, si queréis ampliar temas de Migrations podéis visitar el blog de Javier Torrecilla. Yo os muestro el código tal y como lo he generado para crear la tabla.

   1: namespace WindowsFormsApplication1.Migrations

   2: {

   3:     using System.Data.Entity.Migrations;

   4:     

   5:     public partial class Paises : DbMigration

   6:     {

   7:         public override void Up()

   8:         {

   9:             CreateTable(

  10:                 "Pais",

  11:                 c => new

  12:                     {

  13:                         Id = c.Int(nullable: false, identity: true),

  14:                         Nombre = c.String(nullable:false,maxLength:50,unicode:false),

  15:                         Codigo = c.String(nullable: false, maxLength: 3, unicode: false),

  16:                     })

  17:                 .PrimaryKey(t => t.Id);

  18:  

  19:             CreateIndex("Pais", "Codigo", unique: true);

  20:             

  21:         }

  22:         

  23:         public override void Down()

  24:         {

  25:             DropTable("Pais");

  26:            

  27:         }

  28:     }

  29: }

Ahora vamos con nuestros requerimientos, en la tabla tenemos dos paises “España” y “Francia” que los podemos agregar con las siguientes instrucciones.

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

   2: {

   3:     ct.Paises.Add(new Pais(){Nombre="España",Codigo="34"});

   4:     ct.Paises.Add(new Pais() { Nombre = "Francia", Codigo = "33" });

   5:     ct.SaveChanges();

   6: }

Y ahora nos dicen que lea la tabla Países y que cambie el código de “España a 33” y elimine “Francia” y de paso que inserte “China con código 35”.

Bueno pues sencillo

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

   2: {

   3:     var paises = ct.Paises.AsEnumerable();

   4:     foreach (var pais in paises)

   5:     {

   6:         if (pais.Nombre == "Francia")

   7:         {

   8:             ct.Paises.Remove(pais);

   9:         }

  10:         else if (pais.Nombre == "España")

  11:         {

  12:             pais.Codigo = "33";

  13:         }

  14:     }

  15:     ct.Paises.Add(new Pais() { Nombre = "La China", Codigo = "35" });

  16:     ct.SaveChanges();

  17: }

Que ocurre que cuando ejecutamos nos encontramos con una sorprendente exception del tipo DbUpdateException, pero si miramos su InnerExceptión nos encontramos una SqlExceptión que nos dice lo siguiente.

“Cannot insert duplicate key row in object ‘dbo.Pais’ with unique index ‘IX_Codigo’.”

Claro que ocurre que Entity Framework ejecuta primero “Update,Delete,Insert”, con lo cual yo siempre pensé que era “Delete,Update,Insert”, pero aún así en determinados escenarios nos podría ocasionar problemas.

Conclusiones.

Os voy a ser sincero, es un problema complicado de resolver, pero como dicen poder se puede, la verdad que no lo he probado con Merge, pero yo hasta ahora lo hacía de la siguiente forma.

Delete Tabla From, Update Tabla From, Insert Into Select y la verdad que os confieso que dormía bastante tranquilo, cosa que a partir de este momento no lo voy a hacer tanto.

Lanzo una pregunta ¿Os gustaría en linq que se pudieses ejecutar este tipo de sentencías?

Null. La Historia interminable 2/3

Si recordamos el anterior Post, una de las frase que comente fue que desde el 2005 yo estaba libre de null y claro no hay que decirlo sino realmente demostrarlo y es por eso el motivo de esta entrada.

Lo primero que vamos a hacer es definir una interface que herede de IDataRecord y en ella definir unos métodos que nos permitan leer Nullables.

   1: public interface IDataRecordNullable : IDataRecord

   2: {

   3:     bool? GetNullBoolean(int i);

   4:     byte? GetNullByte(int i);

   5:     char? GetNullChar(int i);

   6:     DateTime? GetNullDateTime(int i);

   7:     Decimal? GetNullDecimal(int i);

   8:     double? GetNullDouble(int i);

   9:     float? GetNullFloat(int i);

  10:     Guid? GetNullGuid(int i);

  11:     Int16? GetNullInt16(int i);

  12:     Int32? GetNullInt32(int i);

  13:     Int64? GetNullInt64(int i);

  14:     string GetNullString(int i);

  15: }

Una vez definida  nos toca implementarla para ello voy a definir una clase a la cual en el constructor le voy a pasar como parametro un IDataRecord.

Solamente voy a implementar una par de métodos y eso sí os dejo la tarea del resto por si alguien quiere vivir un poco más feliz.

   1: public class MyDataRecord : IDataRecordNullable

   2: {

   3:     private readonly IDataRecord DataRecord;

   4:     public MyDataRecord(IDataRecord DataRecord)

   5:     {

   6:         if (DataRecord == null)

   7:         {

   8:             throw new ArgumentNullException();

   9:         }

  10:         this.DataRecord = DataRecord;

  11:     }

  12:     public int GetInt32(int i)

  13:     {

  14:         return DataRecord.GetInt32(i);

  15:     }

  16:     public int? GetNullInt32(int i)

  17:     {

  18:         if (IsDBNull(i))

  19:             return null;

  20:         return GetInt32(i);

  21:     }

  22: }

A partir de este momento la cosa se hace más agradable simplemente lo que tengo que hacer es utilizar esa clase para leer mis datos tal y como os muestro.

   1: using (SqlConnection cn = new SqlConnection(@"Tu conexion"))

   2: {

   3:     cn.Open();

   4:     string sql = @"SELECT CAST(NULL AS INT) NUMERO,CAST(NULL AS VARCHAR(50)) 

   5:                 CADENA UNION ALL SELECT CAST(1 AS INT),CAST('HOLA' AS VARCHAR(50)) ";

   6:     SqlCommand cmd = new SqlCommand(sql, cn);

   7:     SqlDataReader rd = cmd.ExecuteReader();

   8:                 

   9:     MyDataRecord reader = new MyDataRecord(rd);

  10:  

  11:     while (rd.Read())

  12:     {

  13:         int? numero = reader.GetNullInt32(0);

  14:         string Cadena = reader.GetNullString(1);

  15:     }

  16: }

Como podéis observar sencillo y por fin me puedo librar de utilizar este código una y otra vez.

   1: int? numero = rd.IsDBNull(0) ? null : rd.GetInt32(i);

El ejemplo es útil para cualquier versión del framework a partir de la 2.0, pero claro os voy a dar una idea, para los más modernos, entre los que me incluyo. Porque no utilizamos Extension Methods y podemos hacer lo mismo sin tener que crear un nuevo objeto cuando voy a leer.

Vamos con ello.

   1: public static class ExtensionDbDataReader

   2: {

   3:     public static int? GetNullInt32(this DbDataReader reader,int i)

   4:     {

   5:         if (reader.IsDBNull(i))

   6:             return null;

   7:         return reader.GetInt32(i);

   8:     }

   9: }

Como veis ya no tengo que ir instanciando mi clase sino simplemente al utilizar un Extension Methods me encuentro con un método extensor en cualquiera de las clases que heredan  DbDataReader, jo sin quererlo como que me sirve para cualquier motor de bb.dd.

Bueno os dejo una tarea, que es implementar 11 métodos para cada uno de los tipos primitivos y uno para el maldito “string”. Y ahora voy con ese, que se quedo en el tintero en el anterior post.

Si analizamos nuestras aplicaciones un porcentaje muy alto de nuestras propiedades son string concretamente en una app de aproximadamente unas 500 tablas  y 3800 columnas 2500, son varchar o sus derivados, es decir el 66%. Todas ellas no tienen otra representación en el CLR que no sea un string y claro a este se le puede asignar null y no dice nada, hasta que llega a la bb.dd que si la columna no permite null esta si va a decir y además cosas feas.

Pues hasta el momento solamente nos queda una solución y es bastante tediosa, algo parecido a implementar en nuestras clases INotifyPropertyChanged o bien decorarlas con atributos de System.ComponentModel.DataAnnotations dependiendo de la tecnología que utilicemos en nuestras vistas, ambas de verdad para mí feas, pero feas.

Vamos a solucionar el problema de todos los string que no permiten nulos o por lo menos como yo lo he hecho hasta el momento.

   1: public class Entidad

   2: {

   3:     public int Id { get; set; }

   4:     string _Nombre;

   5:     public string Nombre

   6:     {

   7:         set

   8:         {

   9:             _Nombre = value;

  10:         }

  11:         get

  12:         {

  13:             return _Nombre ?? string.Empty;

  14:         }

  15:     }

  16: }

Como no solamente de leer vive el hombre y a veces nos toca escribir, cuando queremos guardar un valor null en la bb.dd, también tenemos que tener en cuenta alguna que otra cosa y es que no podemos enviar null sino System.DbNull.Value, esto es más sencillo y un poco más corto, simplemente vamos a crear un Extension method para SqlParameterCollection tal y como os muestro.

   1: public static class ExtensionDbParameterCollection

   2: {

   3:     public static SqlParameter AddValueWithNull(this SqlParameterCollection Parameters,string ParameterName,SqlDbType Type,int Size, object Value )

   4:     {     

   5:         var Parametro =  Parameters.Add(ParameterName, Type, Size);

   6:         Parametro.Value = (Value == null) ? System.DBNull.Value : Value;

   7:         return Parametro;

   8:     }

   9: }

Como podéis observar lo he implementado para SqlServer el resto de servidores os lo dejo a vosotros y una cosa importante no utilizar AddWithValue, en esta entrada del foro de c# podéis ver los motivos.

SqlParameterCollection.AddWithValue Method.

Para terminar y como se que alguno va a decir ¿porque no utilizas generic y te ahorras esos 11 métodos y el maldito? Pues sencillo porque me obliga a hacer cast en cada una de las lecturas y menos óptimo puede ser cualquier cosa y más si leemos muchos registros, de todas formas os dejo ese método con generic. Pero ya os he advertido de las consecuencias Guiño.

   1: public static class ExtensionDbDataReader

   2: {

   3:     public static Nullable<T> GetNullValue<T>(this DbDataReader Reader, int i) where T:struct

   4:     {

   5:         if (Reader.IsDBNull(i))

   6:             return null;

   7:         return (T)Reader.GetValue(i);

   8:         

   9:     }

  10: }

Saludos y a implementarlo.