miércoles, 25 de marzo de 2020

Cómo hacer un Join Linq con multiples DataContext

Muchas veces tenemos que hacer un Query con Linq que nos une a 2 DataContext Entity Framework, pero podemos encontrar un problema al momento de ejecutarlo y la solución al problema debe tener en cuenta que no se degrade el rendimiento del Query ante la base de datos.

PROBLEMA : Al hacer un Query donde incluyo 2 DataContext obtengo el siguiene error :

"La expresión LINQ especificada contiene referencias a consultas que están asociadas a contextos diferentes"
"The specified LINQ expression contains references to queries that are associated with different contexts."

El siguiente es el Query que se ejecuta uniendo 2 DataContext :

protected void Button1_Click(object sender, EventArgs e)
{
    try
    {
        NCSCEntities db = new NCSCEntities();   //DataContext 1
        NCSCViewEntities dbViews = new NCSCViewEntities();  //DataContext 2

        var query = (from p in db.Requests
                                from v in dbViews.V_Union1
                                where p.Id == 22495 && v.IdRequest == 12493 && p.Id == v.IdRequest                                  orderby v.Request2
                                select new { Id = p.Id, IdCompleTravellers = p.Comments });

        //Se va a ejecutar todos los registros de dbViews.V_Union1 como si hiciera un Select * from V_Union1
        //lo cual es una mala decision de Rendimiento.
        Console.WriteLine(query.Count());

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}


SOLUCION 1 : La forma más rápida de corregir el problema es colocando el método .AsEnumerable() asi :

protected void Button1_Click(object sender, EventArgs e)
{
    try
    {
        NCSCEntities db = new NCSCEntities();   //DataContext 1
        NCSCViewEntities dbViews = new NCSCViewEntities();  //DataContext 2

        var query = (from p in db.Requests.AsEnumerable()
                                from v in dbViews.V_Union1
                                where p.Id == 22495 && v.IdRequest == 12493 && p.Id == v.IdRequest                                  orderby v.Request2
                                select new { Id = p.Id, IdCompleTravellers = p.Comments });

        //Se va a ejecutar todos los registros de dbViews.V_Union1 como si hiciera un Select * from V_Union1
        //lo cual es una mala decision de Rendimiento.
        Console.WriteLine(query.Count());

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

La anterior solución funciona, pero trae un gran problema de rendimiento, cuando vaya a ejecutar el resultado del Query ejemplo : Console.WriteLine(query.Count()) se van a ejecutar todos los registros de dbViews.V_Union1 como si hiciera un Select * from V_Union_TR_PO, aun cuando tenga el filtro : v.IdRequest == 12493, lo cual es una mala decision de Rendimiento. Este comportamiento es causado por haber colocado el método .AsEnumerable()

Si activa el SQL Analizer se dará cuenta que se hace un Query a TODOS los registros :



SOLUCION 2 (Versión mejorada ): Lo mejor es separar los Queries, hacer de manera individual  el Query de cada DataContext y y luego si hacer un JOIN para obtener el resultado buscado. En este caso solo se ejecuta ante Sql Server 2 Queries que devuelven 1 o 2 registros, de acuerdo a lo establecido en la clausula Where, lo cual aumenta enormemente el rendimiento. Además para temas de mantenimiento en el codigo se me hace mas facil.

NOTA : Si al hacer el Join de los 2 Queries llega a tener algun mensaje de error, por unir los 2 DataContext, puede utilizar el método .ToList() en cada Query individual.

protected void Button2_Click(object sender, EventArgs e)
{
    try
    {
        NCSCEntities db = new NCSCEntities();
        NCSCViewEntities dbViews = new NCSCViewEntities();

        var query1 = (from p in db.Requests
                        where p.Id == 22495
                        select new { Id = p.Id, Commentary = p.CommentTravel }); //.ToList();

        var query2 = (from v in dbViews.V_Union1
                        where v.IdRequest == 12493
                        select new { Id = v.IdRequest, Commentary = v.ComentaryTravelStatus }); //.ToList();


        var listTravellers = (from p in query1
                                from v in query1
                                where p.Id == v.Id
                                select new { Id = p.Id, IdCompleTravellers = p.Commentary }).ToList();

        Console.WriteLine(listTravellers.Count);

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

No hay comentarios.: