Как использовать мульти-отображение Dapper при отображении одного типа в другой?

Мой сценарий: у меня есть класс с именем Person, который я сохраняю в БД с помощью Dapper. Что касается личности, у меня есть Словарь значений, который я сериализую в строку и сохраняю как varchar(MAX).

public class Person
{
    public string Name {get; set;}
    public int Id {get; set;} //PK in DB
    public int Age {get; set;}
    public IDictionary<string, string> Values {get; set;}
}

Вот как я сохраняю в БД:

DynamicProperties dp = new DynamicProperties();
dp.Add("Name", p.Name);
dp.Add("Age", p.Age);
dp.Add("Values",  Jil.JSON.Serialize<IDictionary<string, string>>(p.Values));

conneciton.Execute(insertSql, dp, commandType: CommandType.StoredProcedure);

Здесь я пытаюсь это зачитать

    private Func<Person, object, Person> GetPerson = new Func<Person, object, Person>
((person, values) => {
                person.Values = Jil.JSON.Deserialize<Dictionary<string, string>>((string)values);
                return person;
            });

string sql =   "SELECT text
                FROM otherTable

                SELECT Name, Id, Age, Values 
                FROM People 
                WHERE Id = @Id"

SqlMapper.GridReader gridReader = connToDeviceConfig.QueryMultiple(sql, new {Id = 5}, commandType: CommandType.StoredProcedure);

List<string> listOfOtherStuff = gridReader.Read<string>().ToList();
List<Person> people = gridReader.Read<Person, object, Person>(GetPerson, splitOn: "Age").ToList();

// listOfOtherStuff и люди разделены

Второй gridReader завершается с ошибкой При использовании API-интерфейсов с несколькими сопоставлениями убедитесь, что вы установили параметр splitOn, если у вас есть ключи, отличные от Id \ r \ n Параметр имени: splitOn

Я чувствую, что, возможно, немного сгибаю Dapper, чтобы попытаться заставить его сделать то, чего он не должен был делать, то есть прочитать строку из БД и десериализовать ее в словарь и присвоить Person.Values.

Это способ сделать это (а у меня просто где-то ошибка)? или есть другой подход, который я должен выбрать?

Я использовал это как ссылку: (Ссылка на приблизительное местоположение в файле на Archive.org)

public void TestProcSupport()
{
    var p = new DynamicParameters();
    p.Add("a", 11);
    p.Add("b", dbType: DbType.Int32, direction: ParameterDirection.Output);
    p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

    connection.Execute(@"create proc #TestProc 
@a int,
@b int output
as 
begin
set @b = 999
select 1111
return @a
end");
    connection.Query<int>("#TestProc", p, commandType: CommandType.StoredProcedure).First().IsEqualTo(1111);

    p.Get<int>("c").IsEqualTo(11);
    p.Get<int>("b").IsEqualTo(999);

}

Трассировки стека:

   at Dapper.SqlMapper.GetNextSplit(Int32 startIdx, String splitOn, IDataReader reader) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 2111
   at Dapper.SqlMapper.GenerateDeserializers(Type[] types, String splitOn, IDataReader reader) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 2057
   at Dapper.SqlMapper.<MultiMapImpl>d__71`8.MoveNext() in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 1857
   at Dapper.SqlMapper.GridReader.<MultiReadInternal>d__9`8.MoveNext() in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 4300
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Dapper.SqlMapper.GridReader.Read[TFirst,TSecond,TReturn](Func`3 func, String splitOn, Boolean buffered) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 4330

1 ответ

Решение

Пробовал другой splitOns?

У меня нет удобного теста БД, но я думаю, что splitOn должен быть первым столбцом, который вы хотите в следующем отображаемом объекте, а не последним столбцом в предыдущем. Также столбцы становятся членами объекта, а не целым значением (хотя может быть исключение для случаев с одним столбцом, опять же, у меня нет удобной тестовой БД).

TL; DR - попробуй что-нибудь подобное

class ValuesObj{
    public string Values {get;set;}
}

// whatever code here

private Func<Position, ValuesObj, Position> GetPerson = new Func<Person, ValuesObj, Person>
((person, valuesObj) => {
                string values = valuesObj.Values;
                person.Values = Jil.JSON.Deserialize<Dictionary<string, string>>((string)values);
                return position;
            });

string sql =   "SELECT text
                FROM otherTable

                SELECT Name, Id, Age, Values 
                FROM People 
                WHERE Id = @Id"

SqlMapper.GridReader gridReader = connToDeviceConfig.QueryMultiple(sql, new {Id = 5}, commandType: CommandType.StoredProcedure);

List<string> listOfOtherStuff = gridReader.Read<string>().ToList();
List<Person> people = gridReader.Read<Person, ValuesObj, Person>(GetPerson, splitOn: "Values").ToList();

В противном случае, я бы предложил использовать dynamic вернуть Dapper методы.

Другие вопросы по тегам