【问题标题】:Dapper table valued parameter as a property?Dapper 表值参数作为属性?
【发布时间】:2015-04-13 01:29:47
【问题描述】:

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

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 实现了具有 AddParameters 方法的 SqlMapper.IDynamicParameters。

当我执行查询时,@Rules 参数永远不会被传递(使用 SQLProfiler)。我还可以看到从未调用过 AddParameters。

这可能吗?

谢谢

【问题讨论】:

  • 你指定commandType: CommandType.StoredProcedure了吗?

标签: dapper


【解决方案1】:

这是一个基于您的代码的简化示例,表明它运行良好; AddParameters 被正确调用,并将值传送到存储过程。附带说明:如果您为 TVP 使用 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");
    }
}

【讨论】:

  • 我看到你在这里做了什么,我最终得到了几乎相同的解决方案。我真正想要的是能够拥有一个具有列表属性的普通 POCO,而不必求助于 DynamicParameter,或者至少是一个具有列表的 DynamicParameter 属性的 DTO POCO。
  • @Jonesie 我不明白你的评论:我从字面上复制了你在问题中描述的内容,它的行为符合预期......
  • 在您的示例中,您将 obj.Rules(动态参数)传递给 dapper。我需要传递包括一些属性和规则列表的组织 DTO。查看 Dapper 的源代码,我可以看到它基于参数的类型进行了分支。要获得动态参数功能,参数必须是动态的。让它的一部分动态是行不通的。希望能解释清楚。
  • @Jonesie 那时我可能错过了您的问题中的一些内容 - 我怀疑我看到了 ("@Rules", _orgDoc.Rules); 并认为它是精巧通话的一部分。你实际上并没有显示你在哪里调用 dapper,所以我不得不做出一些猜测......你提到 RuleTableValuedParameters 实现了接口,所以我假设这就是你作为参数传递的 - 即:.Rules .
  • 干杯哥们。我爱 Dapper!
【解决方案2】:

这是我创建的完整的动态参数:

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

干杯

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多