Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
Dapper
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tsai
Dapper
Commits
ae30d96b
Commit
ae30d96b
authored
Jul 28, 2014
by
James Holwell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added support for arbitrarily many types in multi mapping
parent
d149e0c2
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
265 additions
and
0 deletions
+265
-0
SqlMapper.cs
Dapper NET40/SqlMapper.cs
+104
-0
SqlMapperAsync.cs
Dapper NET45/SqlMapperAsync.cs
+45
-0
Tests.cs
DapperTests NET45/Tests.cs
+18
-0
Tests.cs
Tests/Tests.cs
+98
-0
No files found.
Dapper NET40/SqlMapper.cs
View file @
ae30d96b
...
...
@@ -1488,6 +1488,28 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
{
return
MultiMap
<
TFirst
,
TSecond
,
TThird
,
TFourth
,
TFifth
,
TSixth
,
TSeventh
,
TReturn
>(
cnn
,
sql
,
map
,
param
as
object
,
transaction
,
buffered
,
splitOn
,
commandTimeout
,
commandType
);
}
/// <summary>
/// Perform a multi mapping query with arbitrary input parameters
/// </summary>
/// <typeparam name="TReturn">The return type</typeparam>
/// <param name="cnn"></param>
/// <param name="sql"></param>
/// <param name="types">array of types in the recordset</param>
/// <param name="map"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <param name="buffered"></param>
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns></returns>
public
static
IEnumerable
<
TReturn
>
Query
<
TReturn
>(
this
IDbConnection
cnn
,
string
sql
,
Type
[]
types
,
Func
<
object
[],
TReturn
>
map
,
dynamic
param
=
null
,
IDbTransaction
transaction
=
null
,
bool
buffered
=
true
,
string
splitOn
=
"Id"
,
int
?
commandTimeout
=
null
,
CommandType
?
commandType
=
null
)
{
var
command
=
new
CommandDefinition
(
sql
,
(
object
)
param
,
transaction
,
commandTimeout
,
commandType
,
buffered
?
CommandFlags
.
Buffered
:
CommandFlags
.
None
);
var
results
=
MultiMapImpl
<
TReturn
>(
cnn
,
command
,
types
,
map
,
splitOn
,
null
,
null
);
return
buffered
?
results
.
ToList
()
:
results
;
}
#endif
partial
class
DontMap
{
}
static
IEnumerable
<
TReturn
>
MultiMap
<
TFirst
,
TSecond
,
TThird
,
TFourth
,
TFifth
,
TSixth
,
TSeventh
,
TReturn
>(
...
...
@@ -1559,6 +1581,72 @@ partial class DontMap { }
}
}
static
IEnumerable
<
TReturn
>
MultiMapImpl
<
TReturn
>(
this
IDbConnection
cnn
,
CommandDefinition
command
,
Type
[]
types
,
Func
<
object
[],
TReturn
>
map
,
string
splitOn
,
IDataReader
reader
,
Identity
identity
)
{
if
(
types
.
Length
<
1
)
{
throw
new
ArgumentException
(
"you must provide at least one type to deserialize"
);
}
object
param
=
command
.
Parameters
;
identity
=
identity
??
new
Identity
(
command
.
CommandText
,
command
.
CommandType
,
cnn
,
types
[
0
],
param
==
null
?
null
:
param
.
GetType
(),
types
);
CacheInfo
cinfo
=
GetCacheInfo
(
identity
,
param
);
IDbCommand
ownedCommand
=
null
;
IDataReader
ownedReader
=
null
;
bool
wasClosed
=
cnn
!=
null
&&
cnn
.
State
==
ConnectionState
.
Closed
;
try
{
if
(
reader
==
null
)
{
ownedCommand
=
command
.
SetupCommand
(
cnn
,
cinfo
.
ParamReader
);
if
(
wasClosed
)
cnn
.
Open
();
ownedReader
=
ownedCommand
.
ExecuteReader
();
reader
=
ownedReader
;
}
DeserializerState
deserializer
=
default
(
DeserializerState
);
Func
<
IDataReader
,
object
>[]
otherDeserializers
=
null
;
int
hash
=
GetColumnHash
(
reader
);
if
((
deserializer
=
cinfo
.
Deserializer
).
Func
==
null
||
(
otherDeserializers
=
cinfo
.
OtherDeserializers
)
==
null
||
hash
!=
deserializer
.
Hash
)
{
var
deserializers
=
GenerateDeserializers
(
types
,
splitOn
,
reader
);
deserializer
=
cinfo
.
Deserializer
=
new
DeserializerState
(
hash
,
deserializers
[
0
]);
otherDeserializers
=
cinfo
.
OtherDeserializers
=
deserializers
.
Skip
(
1
).
ToArray
();
SetQueryCache
(
identity
,
cinfo
);
}
Func
<
IDataReader
,
TReturn
>
mapIt
=
GenerateMapper
(
types
.
Length
,
deserializer
.
Func
,
otherDeserializers
,
map
);
if
(
mapIt
!=
null
)
{
while
(
reader
.
Read
())
{
yield
return
mapIt
(
reader
);
}
}
}
finally
{
try
{
if
(
ownedReader
!=
null
)
{
ownedReader
.
Dispose
();
}
}
finally
{
if
(
ownedCommand
!=
null
)
{
ownedCommand
.
Dispose
();
}
if
(
wasClosed
)
cnn
.
Close
();
}
}
}
private
static
Func
<
IDataReader
,
TReturn
>
GenerateMapper
<
TFirst
,
TSecond
,
TThird
,
TFourth
,
TFifth
,
TSixth
,
TSeventh
,
TReturn
>(
Func
<
IDataReader
,
object
>
deserializer
,
Func
<
IDataReader
,
object
>[]
otherDeserializers
,
object
map
)
{
switch
(
otherDeserializers
.
Length
)
...
...
@@ -1582,6 +1670,22 @@ partial class DontMap { }
}
}
private
static
Func
<
IDataReader
,
TReturn
>
GenerateMapper
<
TReturn
>(
int
length
,
Func
<
IDataReader
,
object
>
deserializer
,
Func
<
IDataReader
,
object
>[]
otherDeserializers
,
Func
<
object
[],
TReturn
>
map
)
{
return
r
=>
{
var
objects
=
new
object
[
length
];
objects
[
0
]
=
deserializer
(
r
);
for
(
var
i
=
1
;
i
<
length
;
++
i
)
{
objects
[
i
]
=
otherDeserializers
[
i
-
1
](
r
);
}
return
map
(
objects
);
};
}
private
static
Func
<
IDataReader
,
object
>[]
GenerateDeserializers
(
Type
[]
types
,
string
splitOn
,
IDataReader
reader
)
{
var
deserializers
=
new
List
<
Func
<
IDataReader
,
object
>>();
...
...
Dapper NET45/SqlMapperAsync.cs
View file @
ae30d96b
...
...
@@ -407,6 +407,51 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
}
}
/// <summary>
/// Perform a multi mapping query with arbitrary input parameters
/// </summary>
/// <typeparam name="TReturn">The return type</typeparam>
/// <param name="cnn"></param>
/// <param name="sql"></param>
/// <param name="types">array of types in the recordset</param>
/// <param name="map"></param>
/// <param name="param"></param>
/// <param name="transaction"></param>
/// <param name="buffered"></param>
/// <param name="splitOn">The Field we should split and read the second object from (default: id)</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns></returns>
public
static
Task
<
IEnumerable
<
TReturn
>>
QueryAsync
<
TReturn
>(
this
IDbConnection
cnn
,
string
sql
,
Type
[]
types
,
Func
<
object
[],
TReturn
>
map
,
dynamic
param
=
null
,
IDbTransaction
transaction
=
null
,
bool
buffered
=
true
,
string
splitOn
=
"Id"
,
int
?
commandTimeout
=
null
,
CommandType
?
commandType
=
null
)
{
var
command
=
new
CommandDefinition
(
sql
,
(
object
)
param
,
transaction
,
commandTimeout
,
commandType
,
buffered
?
CommandFlags
.
Buffered
:
CommandFlags
.
None
,
default
(
CancellationToken
));
return
MultiMapAsync
<
TReturn
>(
cnn
,
command
,
types
,
map
,
splitOn
);
}
private
static
async
Task
<
IEnumerable
<
TReturn
>>
MultiMapAsync
<
TReturn
>(
this
IDbConnection
cnn
,
CommandDefinition
command
,
Type
[]
types
,
Func
<
object
[],
TReturn
>
map
,
string
splitOn
)
{
if
(
types
.
Length
<
1
)
{
throw
new
ArgumentException
(
"you must provide at least one type to deserialize"
);
}
object
param
=
command
.
Parameters
;
var
identity
=
new
Identity
(
command
.
CommandText
,
command
.
CommandType
,
cnn
,
types
[
0
],
param
==
null
?
null
:
param
.
GetType
(),
types
);
var
info
=
GetCacheInfo
(
identity
,
param
);
bool
wasClosed
=
cnn
.
State
==
ConnectionState
.
Closed
;
try
{
if
(
wasClosed
)
await
((
DbConnection
)
cnn
).
OpenAsync
().
ConfigureAwait
(
false
);
using
(
var
cmd
=
(
DbCommand
)
command
.
SetupCommand
(
cnn
,
info
.
ParamReader
))
using
(
var
reader
=
await
cmd
.
ExecuteReaderAsync
(
command
.
CancellationToken
).
ConfigureAwait
(
false
))
{
var
results
=
MultiMapImpl
<
TReturn
>(
null
,
default
(
CommandDefinition
),
types
,
map
,
splitOn
,
reader
,
identity
);
return
command
.
Buffered
?
results
.
ToList
()
:
results
;
}
}
finally
{
if
(
wasClosed
)
cnn
.
Close
();
}
}
private
static
IEnumerable
<
T
>
ExecuteReader
<
T
>(
IDataReader
reader
,
Type
effectiveType
,
Identity
identity
,
CacheInfo
info
)
{
var
tuple
=
info
.
Deserializer
;
...
...
DapperTests NET45/Tests.cs
View file @
ae30d96b
...
...
@@ -78,6 +78,24 @@ public void TestMultiMapWithSplitAsync()
}
}
public
void
TestMultiMapArbitraryWithSplitAsync
()
{
var
sql
=
@"select 1 as id, 'abc' as name, 2 as id, 'def' as name"
;
using
(
var
connection
=
Program
.
GetOpenConnection
())
{
var
productQuery
=
connection
.
QueryAsync
<
Product
>(
sql
,
new
[]
{
typeof
(
Product
),
typeof
(
Category
)
},
(
objects
)
=>
{
var
prod
=
(
Product
)
objects
[
0
];
prod
.
Category
=
(
Category
)
objects
[
1
];
return
prod
;
});
var
product
=
productQuery
.
Result
.
First
();
// assertions
product
.
Id
.
IsEqualTo
(
1
);
product
.
Name
.
IsEqualTo
(
"abc"
);
product
.
Category
.
Id
.
IsEqualTo
(
2
);
product
.
Category
.
Name
.
IsEqualTo
(
"def"
);
}
}
public
void
TestMultiMapWithSplitClosedConnAsync
()
{
var
sql
=
@"select 1 as id, 'abc' as name, 2 as id, 'def' as name"
;
...
...
Tests/Tests.cs
View file @
ae30d96b
...
...
@@ -783,7 +783,105 @@ public void TestMultiMap()
}
}
class
ReviewBoard
{
public
int
Id
{
get
;
set
;
}
public
string
Name
{
get
;
set
;
}
public
User
User1
{
get
;
set
;
}
public
User
User2
{
get
;
set
;
}
public
User
User3
{
get
;
set
;
}
public
User
User4
{
get
;
set
;
}
public
User
User5
{
get
;
set
;
}
public
User
User6
{
get
;
set
;
}
public
User
User7
{
get
;
set
;
}
public
User
User8
{
get
;
set
;
}
public
User
User9
{
get
;
set
;
}
}
public
void
TestMultiMapArbitraryMaps
()
{
// please excuse the trite example, but it is easier to follow than a more real-world one
var
createSql
=
@"
create table #ReviewBoards (Id int, Name varchar(20), User1Id int, User2Id int, User3Id int, User4Id int, User5Id int, User6Id int, User7Id int, User8Id int, User9Id int)
create table #Users (Id int, Name varchar(20))
insert #Users values(1, 'User 1')
insert #Users values(2, 'User 2')
insert #Users values(3, 'User 3')
insert #Users values(4, 'User 4')
insert #Users values(5, 'User 5')
insert #Users values(6, 'User 6')
insert #Users values(7, 'User 7')
insert #Users values(8, 'User 8')
insert #Users values(9, 'User 9')
insert #ReviewBoards values(1, 'Review Board 1', 1, 2, 3, 4, 5, 6, 7, 8, 9)
"
;
connection
.
Execute
(
createSql
);
try
{
var
sql
=
@"
select
rb.Id, rb.Name,
u1.*, u2.*, u3.*, u4.*, u5.*, u6.*, u7.*, u8.*, u9.*
from #ReviewBoards rb
inner join #Users u1 on u1.Id = rb.User1Id
inner join #Users u2 on u2.Id = rb.User2Id
inner join #Users u3 on u3.Id = rb.User3Id
inner join #Users u4 on u4.Id = rb.User4Id
inner join #Users u5 on u5.Id = rb.User5Id
inner join #Users u6 on u6.Id = rb.User6Id
inner join #Users u7 on u7.Id = rb.User7Id
inner join #Users u8 on u8.Id = rb.User8Id
inner join #Users u9 on u9.Id = rb.User9Id
"
;
var
types
=
new
[]
{
typeof
(
ReviewBoard
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
),
typeof
(
User
)
};
Func
<
object
[],
ReviewBoard
>
mapper
=
(
objects
)
=>
{
var
board
=
(
ReviewBoard
)
objects
[
0
];
board
.
User1
=
(
User
)
objects
[
1
];
board
.
User2
=
(
User
)
objects
[
2
];
board
.
User3
=
(
User
)
objects
[
3
];
board
.
User4
=
(
User
)
objects
[
4
];
board
.
User5
=
(
User
)
objects
[
5
];
board
.
User6
=
(
User
)
objects
[
6
];
board
.
User7
=
(
User
)
objects
[
7
];
board
.
User8
=
(
User
)
objects
[
8
];
board
.
User9
=
(
User
)
objects
[
9
];
return
board
;
};
var
data
=
connection
.
Query
<
ReviewBoard
>(
sql
,
types
,
mapper
).
ToList
();
var
p
=
data
.
First
();
p
.
Id
.
IsEqualTo
(
1
);
p
.
Name
.
IsEqualTo
(
"Review Board 1"
);
p
.
User1
.
Id
.
IsEqualTo
(
1
);
p
.
User2
.
Id
.
IsEqualTo
(
2
);
p
.
User3
.
Id
.
IsEqualTo
(
3
);
p
.
User4
.
Id
.
IsEqualTo
(
4
);
p
.
User5
.
Id
.
IsEqualTo
(
5
);
p
.
User6
.
Id
.
IsEqualTo
(
6
);
p
.
User7
.
Id
.
IsEqualTo
(
7
);
p
.
User8
.
Id
.
IsEqualTo
(
8
);
p
.
User9
.
Id
.
IsEqualTo
(
9
);
p
.
User1
.
Name
.
IsEqualTo
(
"User 1"
);
p
.
User2
.
Name
.
IsEqualTo
(
"User 2"
);
p
.
User3
.
Name
.
IsEqualTo
(
"User 3"
);
p
.
User4
.
Name
.
IsEqualTo
(
"User 4"
);
p
.
User5
.
Name
.
IsEqualTo
(
"User 5"
);
p
.
User6
.
Name
.
IsEqualTo
(
"User 6"
);
p
.
User7
.
Name
.
IsEqualTo
(
"User 7"
);
p
.
User8
.
Name
.
IsEqualTo
(
"User 8"
);
p
.
User9
.
Name
.
IsEqualTo
(
"User 9"
);
}
finally
{
connection
.
Execute
(
"drop table #Users drop table #ReviewBoards"
);
}
}
public
void
TestMultiMapGridReader
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment