Сопоставить несколько запросов Dapper со строго типизированным объектом?
У меня есть следующий sproc:
CREATE PROCEDURE [dbo].[dapper_test_sproc]
AS
BEGIN
SET NOCOUNT ON ;
DECLARE @Status INT = 1
DECLARE @Msg VARCHAR(256) = 'Hello World'
DECLARE @StatusLines TABLE
(
rule_code VARCHAR(20),
rule_name VARCHAR(30),
[status] INT,
rule_error_msg VARCHAR(256)
)
INSERT INTO @StatusLines
VALUES ('TEST1', 'TEST2', 1, 'TEST3')
SELECT @Status,
@Msg
SELECT rule_code,
rule_name,
status,
rule_error_msg
FROM @StatusLines
FOR XML RAW
END
В конце sproc вы можете увидеть два набора результатов.
Я использовал следующий код в LinqPad для успешного запроса данных, но я изо всех сил пытаюсь получить отдельные значения из данных. Я не понимаю возвращаемые типы.
using var connection = this.Connection;
var sql = "dapper_test_sproc";
var p = new DynamicParameters();
var grid = await connection
.QueryMultipleAsync(
new CommandDefinition(
sql,
p,
commandType: CommandType.StoredProcedure,
cancellationToken: this.QueryCancelToken));
LINQPad.Extensions.Dump(grid.Read());
LINQPad.Extensions.Dump(grid.Read());
LinqPad показывает следующую информацию:
Например, вызовgrid.Read()
не возвращает полный@Status
и@Msg
из первого набора результатов. Но я узнал, чтоgrid.ReadSingle()
делает. И я понятия не имею, почему.
grid.ReadSingle()["Status"]
выдает ошибкуCannot apply indexing with [] to an expression of type 'object'
Хорошо, это объект, но что за объект? И почему я не могу получить доступ к каким-либо свойствам?
Теперь у меня есть рабочий код, но код ужасно уродлив. Приведение и доступ к элементам по индексам кажется очень подверженным ошибкам. Есть лучший способ сделать это?
public async Task dapper_test_sproc()
{
using var connection = this.Connection;
var sql = "dapper_test_sproc";
var p = new DynamicParameters();
var grid = await connection
.QueryMultipleAsync(
new CommandDefinition(
sql,
p,
commandType: CommandType.StoredProcedure,
cancellationToken: this.QueryCancelToken));
var firstResultSet = await grid.ReadSingleAsync();
var secondResultSet = await grid.ReadSingleAsync();
var firstResultSetDict = (IDictionary<String,Object>)firstResultSet;
var statusBit = (int)firstResultSetDict.ElementAt(0).Value;
var message = (string)firstResultSetDict.ElementAt(1).Value;
var secondResultSetDict = (IDictionary<String,Object>)secondResultSet;
var statusLinesXML = (string)secondResultSetDict.Single().Value;
var passwordValidationStatusMessage = new PasswordValidationStatusMessage
{
Error = !Convert.ToBoolean(statusBit),
Message = message
};
XmlSerializer serializer = new XmlSerializer(typeof(StatusLinesEnvelope));
using var reader = new StringReader($"<root>{statusLinesXML}</root>");
var statusLines = (StatusLinesEnvelope)serializer.Deserialize(reader);
LINQPad.Extensions.Dump(passwordValidationStatusMessage);
LINQPad.Extensions.Dump(statusLines);
}
[XmlRoot(ElementName = "row", Namespace = "")]
public class StatusLines
{
[XmlAttribute(AttributeName = "rule_code")]
public string RuleCode { get; set; }
[XmlAttribute(AttributeName = "rule_name")]
public string RuleName { get; set; }
[XmlAttribute(AttributeName = "status")]
public bool Status { get; set; }
[XmlAttribute(AttributeName = "rule_error_msg")]
public string RuleErrorMsg { get; set; }
}
[XmlRoot(ElementName = "root")]
public class StatusLinesEnvelope
{
[XmlElement(ElementName = "row")]
public List<StatusLines> Row { get; set; }
}
public class PasswordValidationStatusMessage
{
public bool Error { get; set; }
public string Message { get; set; }
}
1 ответ
Я немного изменил имена сохраненных и добавленных столбцов.
ALTER PROCEDURE [dbo].[dapper_test_sproc]
AS
BEGIN
SET NOCOUNT ON ;
DECLARE @Status INT = 1
DECLARE @Msg VARCHAR(256) = 'Hello World'
DECLARE @StatusLines TABLE
(
rule_code VARCHAR(20),
rule_name VARCHAR(30),
[status] INT,
rule_error_msg VARCHAR(256)
)
INSERT INTO @StatusLines
VALUES ('TEST1', 'TEST2', 1, 'TEST3')
-- Add column names
SELECT @Status [Status],
@Msg [Msg]
-- Add column names
select (
SELECT rule_code,
rule_name,
status,
rule_error_msg
FROM @StatusLines
FOR XML RAW
) as XmlValue
END
Теперь код
using System.Data.SqlClient;
using Dapper;
var c = "connection_string";
var sql = "EXEC [dbo].[dapper_test_sproc]";
using (var connection = new SqlConnection(c))
{
var res = await connection.QueryMultipleAsync(sql);
var first = res.Read<First>().ToList();
var second = res.Read<Second>().First();
first.Dump();
second.Dump();
}
class First
{
public int Status {get; set;}
public string Msg {get; set;}
}
class Second
{
public string XmlValue {get; set;}
}
Обновить без имени столбца
Без имени столбца вы должны получить значения, используя позиционирование индекса (также кто использует sproc), именование столбцов всегда является хорошей идеей.
using (var connection = new SqlConnection(c))
{
var res = await connection.QueryMultipleAsync(sql);
var first = res.Read();
var second = res.Read();
var data1 = first.ToList().Select(p => (IDictionary<string, object>)p)
.Select(o =>
new First {
Status = (int)o.Values.ToArray()[0],
Msg = (string)o.Values.ToArray()[1]
}
);
var data2 = second.ToList().Select(p => (IDictionary<string, object>)p)
.Select(o =>
new Second {
XmlValue = (string)o.Values.ToArray()[0]
}
);
data1.Dump();
data2.Dump();
}