DynamicParameters no puede agregar plantilla si ForEach está encadenado después de ToList

c# dapper

Pregunta

Tengo una función que construye dinámicamente sentencias SQL basadas en un modelo existente y luego agrega algunos campos comunes que existen en cada tabla.

El problema es que estoy experimentando un comportamiento extraño cuando trato de agregar los campos comunes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.SqlClient;
using Dapper;

public class Program
{
    private static readonly dynamic[] Data = new dynamic[] { new {Foo = 1, Bar = 2}, new {Foo = 3, Bar = 4} }; 
    public static void Main()
    {
        Console.WriteLine("Hello World");
    }

    public static void Broken()
    {
        var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x));
        parameters.ToList().ForEach(x => x.AddDynamicParams(new {Bax = 7}));
        using (var connection = new SqlConnection(""))
        {
            // This fails with a "Must declare scalar variable @Bax".
            connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters);
        }
    }

    public static void Working()
    {
        var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x)).ToList();
        parameters.ForEach(x => x.AddDynamicParams(new {Bax = 7}));
        using (var connection = new SqlConnection(""))
        {
            // This works fine.
            connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters);
        }
    }
}

Si lo nota, la única diferencia es la posición de mi llamada a .ToList() . En la función Broken , la llamo como parte de una cadena antes de .ForEach y en la función Working , la llamo en la misma línea que la asignación.

He intentado reemplazar la llamada a AddDynamicParams con individuales Add llamadas para cada parámetro, pero no parece tener importancia. También he intentado hacer for(var parameter in parameters) { parameter.AddDynamicParams(new {Bax = 7}) } sin suerte.

Desafortunadamente, no hay una buena manera de que se muestren las templates sin establecer un punto de interrupción y examinar el atributo .templates del objeto en Visual Studio. Si alguien puede proporcionar una mejor manera de demostrar que la plantilla está desapareciendo, soy todo oídos.

Respuesta aceptada

Dentro de tu método Broken , estás enumerando efectivamente tu colección dos veces. Esto se debe a la ejecución diferida LINQ .

Esta línea:

 parameters.ToList().ForEach(x => x.AddDynamicParams(new {Bax = 7}));

IEnumerable<dynamic> su IEnumerable<dynamic> creando una List<dynamic> , que usted está modificando usando ForEach , pero luego no usa esta lista, y pasa de nuevo su objeto de parameters a la connection.Execute Método de ForEach :

connection.Execute("**your query**", parameters);

Cada vez que Dapper enumera el objeto de parameter , volverá a proyectar cada objeto (que no son los mismos objetos que se encuentran dentro de la lista anterior) y, por lo tanto, AddDynamicParams una nueva instancia que no se modifica con AddDynamicParams .

Para ser más claro, su método roto es equivalente a lo siguiente:

public static void Broken()
{
    var parameters = ((IEnumerable<dynamic>)Data).Select(x => new DynamicParameters(x));
    var parametersList = parameters.ToList(); // you create a list
    parametersList.ForEach(x => x.AddDynamicParams(new {Bax = 7})); // then you modify that list

    // parametersList != parameters

    using (var connection = new SqlConnection(""))
    {
        // This fails with a "Must declare scalar variable @Bax".
        connection.Execute("INSERT INTO DataTable(Foo, Bar, Bax) VALUES(@Foo, @Bar, @Bax)", parameters); // but here you are passing the old parameters collection
    }
}

Para resolver su problema, utilice su versión de Working o utilice nuevamente LINQ para crear una versión enumerable que funcione:

var parameters = ((IEnumerable<dynamic>)Data).Select(x =>
{
    var p = new DynamicParameters(x);
    p.AddDynamicParams(new {Bax = 7});
    return p;
});


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué