Dapper.Query<KeyValueType<T1,T2>> Return Null 集合
Dapper.Query<KeyValueType<T1,T2>> Return a collection of Nulls
简单问题(已编辑:首先显示简单可重现的示例,然后是详细场景)
具有以下类:
public class SalesMetrics {
public decimal SalesDollars { get; set; }
public decimal SalesUnits { get; set; }
}
public class ProductGroup {
public string ProductId { get; set; }
public string ProductName { get; set; }
}
使用以下 Dapper 查询,我的结果等于 [{Key = null, Value = null}]
:
IEnumerable<KeyValuePair<ProductGroup, SalesMetrics>> result = sqlConnection
.Query<KeyValuePair<ProductGroup, SalesMetrics>>(
sql: @"SELECT
1 As ProductId,
'Test' AS ProductName,
1.00 As SalesDollars,
1 As SalesUnits");
我想知道 Dapper 是否可以将 KeyValuePair 作为输出类型处理,是这样吗:查询需要如何?
完整场景(为什么我需要这个)
我正在创建一个销售查询生成器函数,它可以根据不同的分组谓词对我的销售结果进行分组,并且应该 return 基于该谓词的不同结果类型 type
。
我正在使用 Dapper
nuget 包从 SQL-Server
获取结果。我正在对 IDbConnection
使用 Dapper.Query<T>()
扩展方法
基本上,无论分组类型如何,我都想 return 一个 sum
的 SalesDollars 和 SalesUnits。对于这部分输出,我创建了以下 class SalesMetrics
我希望我的销售查询函数接受组 class(ProductGroup
,或任何其他 class ...)作为 generic parameter
命名为 TGroup
, 函数应该 return 一个 KeyValuePair<TGroup,SalesMetric>
的集合
数据源
这是我的销售布局TableFlatSales
CREATE TABLE dbo.FlatSales (
SalesDate DATE NOT NULL,
ProductId INT NOT NULL,
ProductName VARCHAR(100) NOT NULL,
ProductCategoryId INT NOT NULL,
ProductCategoryName VARCHAR(100) NOT NULL,
CustomerGroupId INT NOT NULL,
CustomerGroupName VARCHAR(100) NOT NULL,
CustomerId INT NOT NULL,
CustomerName VARCHAR(100) NOT NULL,
SalesUnits INT NOT NULL,
SalesDollars INT NOT NULL
)
我遇到了问题
我有以下查询数据库的功能。
public static IEnumerable<KeyValuePair<TGroup,SalesMetrics>> SalesTotalsCompute<TGroup>(System.Data.IDbConnection connection)
{
string[] groupByColumnNames = typeof(TGroup)
.GetProperties(bindingAttr: System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Select(x => x.Name)
.ToArray();
string joinedGroupByColumnsNames = string.Join(",", groupByColumnNames);
return connection.Query<KeyValuePair<TGroup, SalesMetrics>>(sql: $@"
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
{joinedGroupByColumnsNames}
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}
");
}
NULL 集合
代码 does not fail
但它 return Key 和 Value 都是 NULL
的 KeyValuePair 列表。
我试过像 ProductName as [Key.ProductName]
那样为我的专栏添加别名,但它并没有改变任何东西(也没有失败)...
为 ProductGroup
生成的 Sql 查询如下(return 都是空的 KeyValuePair):
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
ProductId,ProductName
FROM dbo.FlatSales
GROUP BY ProductId,ProductName
OR
SELECT SUM(SalesDollars) AS SalesDollars as [Value.SalesDollars],
SUM(SalesUnits) AS SalesUnits as [Value.SalesUnits],
ProductId As [Key.ProductId],ProductName As [Key.ProductName]
FROM dbo.FlatSales
GROUP BY ProductId,ProductName
有什么想法吗?
我怀疑 Dapper 是否支持开箱即用的复杂对象。
也许你可以从 Dapper 中获益 multi-mapping feature:
public static IEnumerable<KeyValuePair<TGroup, SalesMetrics>> SalesTotalsCompute<TGroup>(System.Data.IDbConnection connection)
{
string joinedGroupByColumnsNames = string.Join(",", GetCachedColumnNamesFor<TGroup>());
return connection.Query<TGroup, SalesMetrics, KeyValuePair<TGroup, SalesMetrics>>(
sql: $@"SELECT {joinedGroupByColumnsNames},
SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}",
map: (groupData, salesMetricsData) => new KeyValuePair<TGroup, SalesMetrics>(groupData, salesMetricsData),
splitOn: "SalesDollars");
}
备注
- 我重新排序了列,因为 splitOn 需要两个对象分开的列的名称,否则你必须传递
joinedGroupByColumnsNames
-array 中的第一个项目,这有点更随机
- 如果您使用的是 .NET Standard,请考虑返回
ValueTuple
而不是 KeyValuePair
- 不要在每次调用时都使用反射,我建议添加一个只进行一次反射的方法
GetCachedColumnNamesFor
,使用静态 ConcurrentDictionary,调用 ConcurrentDictionary.GetOrAdd
方法。
其他方法
您也可以让 ProductGroup
继承自 SalesMetrics
(或者创建一个 ISalesMetrics
接口并让 ProductGroup
实现该接口)并执行 Query<ProductGroup>(...)
。另一个好处是编译器会阻止两个模型中的重复字段。
生成的方法将如下所示:
public static IEnumerable<TSalesData> SalesTotalsCompute<TSalesData>(System.Data.IDbConnection connection)
where TSalesData : ISalesMetric
{
string joinedGroupByColumnsNames = string.Join(",", GetCachedNonSalesMetricColumnNamesFor<TSalesData>());
return connection.Query<TSalesData>(sql: $@"
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
{joinedGroupByColumnsNames}
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}
");
}
这里的 GetCachedNonSalesMetricColumnNamesFor
方法反映了 TSalesData
的属性,排除了 ISalesMetric
接口的属性,再次缓存结果。
简单问题(已编辑:首先显示简单可重现的示例,然后是详细场景)
具有以下类:
public class SalesMetrics {
public decimal SalesDollars { get; set; }
public decimal SalesUnits { get; set; }
}
public class ProductGroup {
public string ProductId { get; set; }
public string ProductName { get; set; }
}
使用以下 Dapper 查询,我的结果等于 [{Key = null, Value = null}]
:
IEnumerable<KeyValuePair<ProductGroup, SalesMetrics>> result = sqlConnection
.Query<KeyValuePair<ProductGroup, SalesMetrics>>(
sql: @"SELECT
1 As ProductId,
'Test' AS ProductName,
1.00 As SalesDollars,
1 As SalesUnits");
我想知道 Dapper 是否可以将 KeyValuePair 作为输出类型处理,是这样吗:查询需要如何?
完整场景(为什么我需要这个)
我正在创建一个销售查询生成器函数,它可以根据不同的分组谓词对我的销售结果进行分组,并且应该 return 基于该谓词的不同结果类型 type
。
我正在使用 Dapper
nuget 包从 SQL-Server
获取结果。我正在对 IDbConnection
Dapper.Query<T>()
扩展方法
基本上,无论分组类型如何,我都想 return 一个 sum
的 SalesDollars 和 SalesUnits。对于这部分输出,我创建了以下 class SalesMetrics
我希望我的销售查询函数接受组 class(ProductGroup
,或任何其他 class ...)作为 generic parameter
命名为 TGroup
, 函数应该 return 一个 KeyValuePair<TGroup,SalesMetric>
数据源
这是我的销售布局TableFlatSales
CREATE TABLE dbo.FlatSales (
SalesDate DATE NOT NULL,
ProductId INT NOT NULL,
ProductName VARCHAR(100) NOT NULL,
ProductCategoryId INT NOT NULL,
ProductCategoryName VARCHAR(100) NOT NULL,
CustomerGroupId INT NOT NULL,
CustomerGroupName VARCHAR(100) NOT NULL,
CustomerId INT NOT NULL,
CustomerName VARCHAR(100) NOT NULL,
SalesUnits INT NOT NULL,
SalesDollars INT NOT NULL
)
我遇到了问题
我有以下查询数据库的功能。
public static IEnumerable<KeyValuePair<TGroup,SalesMetrics>> SalesTotalsCompute<TGroup>(System.Data.IDbConnection connection)
{
string[] groupByColumnNames = typeof(TGroup)
.GetProperties(bindingAttr: System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Select(x => x.Name)
.ToArray();
string joinedGroupByColumnsNames = string.Join(",", groupByColumnNames);
return connection.Query<KeyValuePair<TGroup, SalesMetrics>>(sql: $@"
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
{joinedGroupByColumnsNames}
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}
");
}
NULL 集合
代码 does not fail
但它 return Key 和 Value 都是 NULL
的 KeyValuePair 列表。
我试过像 ProductName as [Key.ProductName]
那样为我的专栏添加别名,但它并没有改变任何东西(也没有失败)...
为 ProductGroup
生成的 Sql 查询如下(return 都是空的 KeyValuePair):
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
ProductId,ProductName
FROM dbo.FlatSales
GROUP BY ProductId,ProductName
OR
SELECT SUM(SalesDollars) AS SalesDollars as [Value.SalesDollars],
SUM(SalesUnits) AS SalesUnits as [Value.SalesUnits],
ProductId As [Key.ProductId],ProductName As [Key.ProductName]
FROM dbo.FlatSales
GROUP BY ProductId,ProductName
有什么想法吗?
我怀疑 Dapper 是否支持开箱即用的复杂对象。
也许你可以从 Dapper 中获益 multi-mapping feature:
public static IEnumerable<KeyValuePair<TGroup, SalesMetrics>> SalesTotalsCompute<TGroup>(System.Data.IDbConnection connection)
{
string joinedGroupByColumnsNames = string.Join(",", GetCachedColumnNamesFor<TGroup>());
return connection.Query<TGroup, SalesMetrics, KeyValuePair<TGroup, SalesMetrics>>(
sql: $@"SELECT {joinedGroupByColumnsNames},
SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}",
map: (groupData, salesMetricsData) => new KeyValuePair<TGroup, SalesMetrics>(groupData, salesMetricsData),
splitOn: "SalesDollars");
}
备注
- 我重新排序了列,因为 splitOn 需要两个对象分开的列的名称,否则你必须传递
joinedGroupByColumnsNames
-array 中的第一个项目,这有点更随机 - 如果您使用的是 .NET Standard,请考虑返回
ValueTuple
而不是KeyValuePair
- 不要在每次调用时都使用反射,我建议添加一个只进行一次反射的方法
GetCachedColumnNamesFor
,使用静态 ConcurrentDictionary,调用ConcurrentDictionary.GetOrAdd
方法。
其他方法
您也可以让 ProductGroup
继承自 SalesMetrics
(或者创建一个 ISalesMetrics
接口并让 ProductGroup
实现该接口)并执行 Query<ProductGroup>(...)
。另一个好处是编译器会阻止两个模型中的重复字段。
生成的方法将如下所示:
public static IEnumerable<TSalesData> SalesTotalsCompute<TSalesData>(System.Data.IDbConnection connection)
where TSalesData : ISalesMetric
{
string joinedGroupByColumnsNames = string.Join(",", GetCachedNonSalesMetricColumnNamesFor<TSalesData>());
return connection.Query<TSalesData>(sql: $@"
SELECT SUM(SalesDollars) AS SalesDollars,
SUM(SalesUnits) AS SalesUnits,
{joinedGroupByColumnsNames}
FROM dbo.FlatSales
GROUP BY {joinedGroupByColumnsNames}
");
}
这里的 GetCachedNonSalesMetricColumnNamesFor
方法反映了 TSalesData
的属性,排除了 ISalesMetric
接口的属性,再次缓存结果。