Dapper tabla valoró el parámetro como una propiedad?

dapper

Pregunta

Tengo un proceso almacenado como este:

CREATE PROCEDURE [dbo].[Organisation_Insert]
 @OrganisationXId uniqueidentifier
,@Enabled bit
,@Timezone nvarchar(50)
,@MinimumValue float
,@Rules ReminderRuleType READONLY ...

ReminderRuleType es un tipo definido por el usuario.

En mi aplicación tengo esto:

class OrganisationDTO
    {
        private readonly IOrganisationDocument _orgDoc;
        public long OrganisationId { get { return _orgDoc.OrganisationId; } }
        public Guid OrganisationXId { get { return _orgDoc.OrganisationXId; } }
        public string TimeZone { get { return _orgDoc.TimeZone; } }
        public bool Enabled { get { return _orgDoc.Enabled; } }
        public decimal MinimumValue { get { return _orgDoc.MinimumValue; } }
        public RuleTableValuedParameters Rules { get; private set; }

        public OrganisationDTO(IOrganisationDocument orgDoc)
        {
            _orgDoc = orgDoc;
            Rules = new RuleTableValuedParameters("@Rules", _orgDoc.Rules);
        }
    }

RuleTableValuedParameters implementa SqlMapper.IDynamicParameters que tiene un método AddParameters.

Cuando ejecuto la consulta, el parámetro @Rules nunca se pasa (usando SQLProfiler). También puedo ver que AddParameters nunca se llama.

¿Es posible hacer esto?

Gracias

Respuesta aceptada

Aquí hay un ejemplo simplificado basado en su código que lo muestra funcionando bien; AddParameters se invoca correctamente y los valores se transmiten al procedimiento almacenado. Como nota al margen: si está utilizando DataTable para sus TVP, la biblioteca lo admite directamente sin necesidad de código adicional.

public void SO29596645_TvpProperty()
{
    try { connection.Execute("CREATE TYPE SO29596645_ReminderRuleType AS TABLE (id int NOT NULL)"); }
    catch { }
    connection.Execute(@"create proc #SO29596645_Proc (@Id int, @Rules SO29596645_ReminderRuleType READONLY)
                        as begin select @Id + ISNULL((select sum(id) from @Rules), 0); end");
    var obj = new SO29596645_OrganisationDTO();
    int val = connection.Query<int>("#SO29596645_Proc", obj.Rules, commandType: CommandType.StoredProcedure).Single();

    // 4 + 9 + 7 = 20
    val.IsEqualTo(20);

}

class SO29596645_RuleTableValuedParameters : Dapper.SqlMapper.IDynamicParameters {
    private string parameterName;

    public SO29596645_RuleTableValuedParameters(string parameterName)
    {
        this.parameterName = parameterName;
    }


    public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity)
    {
        Console.WriteLine("> AddParameters");
        SqlCommand lazy = (SqlCommand)command;
        lazy.Parameters.AddWithValue("Id", 7);
        DataTable table = new DataTable {
            Columns = {{"Id", typeof(int)}},
            Rows = {{4}, {9}}
        };
        lazy.Parameters.AddWithValue("Rules", table);
        Console.WriteLine("< AddParameters");
    }
}
class SO29596645_OrganisationDTO
{
    public SO29596645_RuleTableValuedParameters Rules { get; private set; }

    public SO29596645_OrganisationDTO()
    {
        Rules = new SO29596645_RuleTableValuedParameters("@Rules");
    }
}

Respuesta popular

Aquí está el DynamicParameter completo que he creado:

 public class OrganisationDynamicParameter : SqlMapper.IDynamicParameters
{
    private readonly IOrganisation _orgModel;

    public OrganisationDynamicParameter(IOrganisation orgModel)
    {
        _orgModel = orgModel;
    }

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
    {
        SqlParameter p;
        var sqlCommand = (SqlCommand)command;
        sqlCommand.CommandType = CommandType.StoredProcedure;

        p = sqlCommand.Parameters.Add("@OrganisationXId", SqlDbType.UniqueIdentifier);
        p.Value = _orgModel.OrganisationXId;
        p = sqlCommand.Parameters.Add("@Enabled", SqlDbType.Bit);
        p.Value = _orgModel.Enabled;
        p = sqlCommand.Parameters.Add("@Timezone", SqlDbType.NVarChar, 50);
        p.Value = _orgModel.TimeZone;
        p = sqlCommand.Parameters.Add("@MinimumValue", SqlDbType.Float);
        p.Value = _orgModel.MinimumValue;

        List<SqlDataRecord> ruleList = _orgModel.Rules.Select(MapRuleData).ToList();
        if (ruleList.Count > 0)
        {
            p = sqlCommand.Parameters.Add("@Rules", SqlDbType.Structured);
            p.Direction = ParameterDirection.Input;
            p.TypeName = "ReminderRuleType";
            p.Value = ruleList;
        }
    }

    protected SqlDataRecord MapRuleData(IReminderRule value)
    {
        var rec = new SqlDataRecord(new[]
        {
            new SqlMetaData("RuleId", SqlDbType.BigInt),
            new SqlMetaData("OrganisationId", SqlDbType.BigInt),
            new SqlMetaData("Name", SqlDbType.NVarChar, 200),
            new SqlMetaData("OffsetDays", SqlDbType.Int),
            new SqlMetaData("SubjectTemplate", SqlDbType.NVarChar, -1),
            new SqlMetaData("BodyTemplate", SqlDbType.NVarChar, -1)
        });

        rec.SetInt64(0, value.RuleId);
        rec.SetInt64(1, value.OrganisationId);
        rec.SetString(2, value.Name);
        rec.SetInt32(3, value.OffsetDays);
        rec.SetString(4, value.SubjectTemplate);
        rec.SetString(5, value.BodyTemplate);
        return rec;
    }
}

Yo uso esto así:

public IOrganisation CreateOrganisation(IOrganisation organisation)
    {
        var dtoOrg = new OrganisationDynamicParameter(organisation);
        return ExecuteSPReturningOrganisation("Organisation_Insert", dtoOrg);
    }

    protected IOrganisation ExecuteSPReturningOrganisation(string query, object parameters)
    {
        using (IDbConnection con = ConnectionFactory.CreateOpenConnection())
        {
            using (
                SqlMapper.GridReader multi = con.QueryMultiple(query, parameters,
                    commandType: CommandType.StoredProcedure))
            {
                OrganisationModel org = multi.Read<OrganisationModel>().SingleOrDefault();
                if (org != null)
                {
                    org.Rules = multi.Read<ReminderRuleModel>().ToArray();
                }

                return org;
            }
        }
    }

Aclamaciones



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é