如何在将一种类型映射到另一种类型时使用 Dapper 的多重映射?
How to use Dapper's multi-mapping while mapping one type to another?
我的场景:
我有一个名为 Person 的 class,我使用 Dapper 将其保存到数据库中。
在 Person 上,我有一个值字典,我将其序列化为一个字符串并将其存储为 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 和 people 是分开的
第二个 gridReader 失败并显示 当使用多映射 API 时,如果您有 Id\r\nParameter 名称以外的键,请确保设置 splitOn 参数:splitOn
我觉得我可能正在稍微弯曲 Dapper 以尝试让它做一些它不应该做的事情,即 从
数据库并将其反序列化为字典并分配给 Person.Values.
这是这样做的方法吗(我只是在某个地方遇到了错误)?或者我应该采取另一种方法吗?
我用这个作为参考:
(Link to approximate location in file at 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
试过其他 splitOn
吗?
我没有方便的数据库测试,但我认为 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
return Dapper 方法。
我的场景: 我有一个名为 Person 的 class,我使用 Dapper 将其保存到数据库中。 在 Person 上,我有一个值字典,我将其序列化为一个字符串并将其存储为 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 和 people 是分开的
第二个 gridReader 失败并显示 当使用多映射 API 时,如果您有 Id\r\nParameter 名称以外的键,请确保设置 splitOn 参数:splitOn
我觉得我可能正在稍微弯曲 Dapper 以尝试让它做一些它不应该做的事情,即 从 数据库并将其反序列化为字典并分配给 Person.Values.
这是这样做的方法吗(我只是在某个地方遇到了错误)?或者我应该采取另一种方法吗?
我用这个作为参考: (Link to approximate location in file at 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
试过其他 splitOn
吗?
我没有方便的数据库测试,但我认为 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
return Dapper 方法。