Dapper表值参数作为属性?

3

我有一个类似这样的存储过程:

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

ReminderRuleType 是一个用户自定义类型。

在我的应用程序中,我有如下代码:

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实现了SqlMapper.IDynamicParameters接口,该接口具有AddParameters方法。
当我执行查询时,@Rules参数从未传递(使用SQLProfiler)。我还可以看到AddParameters从未被调用。
这是否可能做到?
谢谢

你是否指定了 commandType: CommandType.StoredProcedure - Marc Gravell
2个回答

2
这里是一个基于您的代码的简化示例,展示了它正常工作的情况;AddParameters被正确调用,并将值传递给存储过程。此外,如果您正在使用DataTable来处理表值参数,该库直接支持,不需要额外的代码。
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");
    }
}

1
我看到你在这里做的事情,最终我得到了几乎相同的解决方案。但是我真正想要的是能够拥有一个普通的 POCO,其中包含一个列表属性,而不必诉诸于 DynamicParameter,或者至少是一个 DTO POCO,其中包含一个 DynamicParameter 属性用于列表。 - Jonesie
1
@Jonesie 我不理解你的评论:我按照你在问题中描述的方式进行了复现,结果也是符合预期的... - Marc Gravell
在你的例子中,你正在将 obj.Rules - 动态参数 - 传递给 Dapper。我需要传递包括一些属性和规则列表的组织 DTO。查看 Dapper 的源代码,我可以看到它基于参数的类型进行分支。为了获得动态参数功能,参数需要是动态的。部分动态不起作用。希望这解释清楚了。 - Jonesie
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Marc Gravell
干杯,伙计。我喜欢 Dapper! - Jonesie

2

这是我创建的完整可用的DynamicParameter:

 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;
    }
}

我这样使用:

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;
            }
        }
    }

干杯


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接