Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
jobily
Efcore.Pg
提交
1eed21e0
E
Efcore.Pg
项目概览
jobily
/
Efcore.Pg
12 个月 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
Efcore.Pg
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
1eed21e0
编写于
7月 10, 2022
作者:
S
Shay Rojansky
提交者:
GitHub
7月 09, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Implement sum and average over TimeSpan (#2423)
Closes #2339
上级
82a758cd
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
282 addition
and
1 deletion
+282
-1
src/EFCore.PG.NodaTime/Extensions/NpgsqlNodaTimeDbFunctionsExtensions.cs
...odaTime/Extensions/NpgsqlNodaTimeDbFunctionsExtensions.cs
+44
-0
src/EFCore.PG.NodaTime/Extensions/NpgsqlNodaTimeServiceCollectionExtensions.cs
...e/Extensions/NpgsqlNodaTimeServiceCollectionExtensions.cs
+1
-0
src/EFCore.PG.NodaTime/Query/Internal/NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin.cs
...rnal/NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin.cs
+69
-0
src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlAggregateDbFunctionsExtensions.cs
...nctionsExtensions/NpgsqlAggregateDbFunctionsExtensions.cs
+18
-0
src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMiscAggregateMethodTranslator.cs
...anslators/Internal/NpgsqlMiscAggregateMethodTranslator.cs
+20
-0
src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs
...EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs
+9
-0
test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs
...ore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs
+38
-0
test/EFCore.PG.FunctionalTests/Query/NorthwindFunctionsQueryNpgsqlTest.cs
...unctionalTests/Query/NorthwindFunctionsQueryNpgsqlTest.cs
+1
-1
test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs
....FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs
+2
-0
test/EFCore.PG.NodaTime.FunctionalTests/NodaTimeQueryNpgsqlTest.cs
...re.PG.NodaTime.FunctionalTests/NodaTimeQueryNpgsqlTest.cs
+80
-0
未找到文件。
src/EFCore.PG.NodaTime/Extensions/NpgsqlNodaTimeDbFunctionsExtensions.cs
0 → 100644
浏览文件 @
1eed21e0
// ReSharper disable once CheckNamespace
namespace
Microsoft.EntityFrameworkCore
;
/// <summary>
/// Provides extension methods supporting NodaTime function translation for PostgreSQL.
/// </summary>
public
static
class
NpgsqlNodaTimeDbFunctionsExtensions
{
/// <summary>
/// Computes the sum of the non-null input intervals. Corresponds to the PostgreSQL <c>sum</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be summed.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
Period
?
Sum
(
this
DbFunctions
_
,
IEnumerable
<
Period
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Sum
)));
/// <summary>
/// Computes the sum of the non-null input intervals. Corresponds to the PostgreSQL <c>sum</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be summed.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
Duration
?
Sum
(
this
DbFunctions
_
,
IEnumerable
<
Duration
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Sum
)));
/// <summary>
/// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the PostgreSQL <c>avg</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be computed into an average.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
Period
?
Average
(
this
DbFunctions
_
,
IEnumerable
<
Period
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Average
)));
/// <summary>
/// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the PostgreSQL <c>avg</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be computed into an average.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
Duration
?
Average
(
this
DbFunctions
_
,
IEnumerable
<
Duration
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Average
)));
}
src/EFCore.PG.NodaTime/Extensions/NpgsqlNodaTimeServiceCollectionExtensions.cs
浏览文件 @
1eed21e0
...
...
@@ -22,6 +22,7 @@ public static class NpgsqlNodaTimeServiceCollectionExtensions
new
EntityFrameworkRelationalServicesBuilder
(
serviceCollection
)
.
TryAdd
<
IRelationalTypeMappingSourcePlugin
,
NpgsqlNodaTimeTypeMappingSourcePlugin
>()
.
TryAdd
<
IMethodCallTranslatorPlugin
,
NpgsqlNodaTimeMethodCallTranslatorPlugin
>()
.
TryAdd
<
IAggregateMethodCallTranslatorPlugin
,
NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin
>()
.
TryAdd
<
IMemberTranslatorPlugin
,
NpgsqlNodaTimeMemberTranslatorPlugin
>()
.
TryAdd
<
IEvaluatableExpressionFilterPlugin
,
NpgsqlNodaTimeEvaluatableExpressionFilterPlugin
>();
...
...
src/EFCore.PG.NodaTime/Query/Internal/NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin.cs
0 → 100644
浏览文件 @
1eed21e0
using
Npgsql.EntityFrameworkCore.PostgreSQL.Query
;
namespace
Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal
;
public
class
NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin
:
IAggregateMethodCallTranslatorPlugin
{
public
NpgsqlNodaTimeAggregateMethodCallTranslatorPlugin
(
ISqlExpressionFactory
sqlExpressionFactory
)
{
if
(
sqlExpressionFactory
is
not
NpgsqlSqlExpressionFactory
npgsqlSqlExpressionFactory
)
{
throw
new
ArgumentException
(
$"Must be an
{
nameof
(
NpgsqlSqlExpressionFactory
)}
"
,
nameof
(
sqlExpressionFactory
));
}
Translators
=
new
IAggregateMethodCallTranslator
[]
{
new
NpgsqlNodaTimeAggregateMethodTranslator
(
npgsqlSqlExpressionFactory
)
};
}
public
virtual
IEnumerable
<
IAggregateMethodCallTranslator
>
Translators
{
get
;
}
}
public
class
NpgsqlNodaTimeAggregateMethodTranslator
:
IAggregateMethodCallTranslator
{
private
static
readonly
bool
[][]
FalseArrays
=
{
Array
.
Empty
<
bool
>(),
new
[]
{
false
}
};
private
readonly
NpgsqlSqlExpressionFactory
_sqlExpressionFactory
;
public
NpgsqlNodaTimeAggregateMethodTranslator
(
NpgsqlSqlExpressionFactory
sqlExpressionFactory
)
=>
_sqlExpressionFactory
=
sqlExpressionFactory
;
public
virtual
SqlExpression
?
Translate
(
MethodInfo
method
,
EnumerableExpression
source
,
IReadOnlyList
<
SqlExpression
>
arguments
,
IDiagnosticsLogger
<
DbLoggerCategory
.
Query
>
logger
)
{
if
(
source
.
Selector
is
not
SqlExpression
sqlExpression
||
method
.
DeclaringType
!=
typeof
(
NpgsqlNodaTimeDbFunctionsExtensions
))
{
return
null
;
}
switch
(
method
.
Name
)
{
case
nameof
(
NpgsqlNodaTimeDbFunctionsExtensions
.
Sum
):
return
_sqlExpressionFactory
.
AggregateFunction
(
"sum"
,
new
[]
{
sqlExpression
},
source
,
nullable
:
true
,
argumentsPropagateNullability
:
FalseArrays
[
1
],
returnType
:
sqlExpression
.
Type
,
sqlExpression
.
TypeMapping
);
case
nameof
(
NpgsqlNodaTimeDbFunctionsExtensions
.
Average
):
return
_sqlExpressionFactory
.
AggregateFunction
(
"avg"
,
new
[]
{
sqlExpression
},
source
,
nullable
:
true
,
argumentsPropagateNullability
:
FalseArrays
[
1
],
returnType
:
sqlExpression
.
Type
,
sqlExpression
.
TypeMapping
);
default
:
return
null
;
}
}
}
src/EFCore.PG/Extensions/DbFunctionsExtensions/NpgsqlAggregateDbFunctionsExtensions.cs
浏览文件 @
1eed21e0
...
...
@@ -36,6 +36,24 @@ public static T[] JsonAgg<T>(this DbFunctions _, IEnumerable<T> input)
public
static
T
[]
JsonbAgg
<
T
>(
this
DbFunctions
_
,
IEnumerable
<
T
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
JsonbAgg
)));
/// <summary>
/// Computes the sum of the non-null input intervals. Corresponds to the PostgreSQL <c>sum</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be summed.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
TimeSpan
?
Sum
(
this
DbFunctions
_
,
IEnumerable
<
TimeSpan
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Sum
)));
/// <summary>
/// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the PostgreSQL <c>avg</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be computed into an average.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public
static
TimeSpan
?
Average
(
this
DbFunctions
_
,
IEnumerable
<
TimeSpan
>
input
)
=>
throw
new
InvalidOperationException
(
CoreStrings
.
FunctionOnClient
(
nameof
(
Average
)));
#
region
Range
/// <summary>
...
...
src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlMiscAggregateMethodTranslator.cs
浏览文件 @
1eed21e0
...
...
@@ -128,6 +128,26 @@ public class NpgsqlMiscAggregateMethodTranslator : IAggregateMethodCallTranslato
returnType
:
sqlExpression
.
Type
,
sqlExpression
.
TypeMapping
);
case
nameof
(
NpgsqlAggregateDbFunctionsExtensions
.
Sum
):
return
_sqlExpressionFactory
.
AggregateFunction
(
"sum"
,
new
[]
{
sqlExpression
},
source
,
nullable
:
true
,
argumentsPropagateNullability
:
FalseArrays
[
1
],
returnType
:
sqlExpression
.
Type
,
sqlExpression
.
TypeMapping
);
case
nameof
(
NpgsqlAggregateDbFunctionsExtensions
.
Average
):
return
_sqlExpressionFactory
.
AggregateFunction
(
"avg"
,
new
[]
{
sqlExpression
},
source
,
nullable
:
true
,
argumentsPropagateNullability
:
FalseArrays
[
1
],
returnType
:
sqlExpression
.
Type
,
sqlExpression
.
TypeMapping
);
case
nameof
(
NpgsqlAggregateDbFunctionsExtensions
.
JsonbObjectAgg
):
case
nameof
(
NpgsqlAggregateDbFunctionsExtensions
.
JsonObjectAgg
):
var
isJsonb
=
method
.
Name
==
nameof
(
NpgsqlAggregateDbFunctionsExtensions
.
JsonbObjectAgg
);
...
...
src/EFCore.PG/Query/Internal/NpgsqlSqlNullabilityProcessor.cs
浏览文件 @
1eed21e0
...
...
@@ -199,6 +199,15 @@ PostgresUnknownBinaryExpression postgresUnknownBinaryExpression
&&
visitedBase
is
SqlFunctionExpression
{
Name
:
"COALESCE"
,
Arguments
:
{
}
}
coalesceExpression
&&
coalesceExpression
.
Arguments
[
0
]
is
PostgresFunctionExpression
wrappedFunctionExpression
)
{
// The base logic assumes sum is operating over numbers, which breaks sum over PG interval.
// Detect that case and remove the coalesce entirely (note that we don't need coalescing since sum function is in
// EF.Functions.Sum, and returns nullable. This is a temporary hack until #38158 is fixed.
if
(
sqlFunctionExpression
.
Type
==
typeof
(
TimeSpan
)
||
sqlFunctionExpression
.
Type
.
FullName
is
"NodaTime.Period"
or
"NodaTime.Duration"
)
{
return
coalesceExpression
.
Arguments
[
0
];
}
var
visitedArguments
=
coalesceExpression
.
Arguments
!.
ToArray
();
visitedArguments
[
0
]
=
VisitPostgresFunctionComponents
(
wrappedFunctionExpression
);
...
...
test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs
浏览文件 @
1eed21e0
...
...
@@ -346,6 +346,44 @@ public async Task Where_TimeSpan_TotalMilliseconds(bool async)
WHERE (date_part('epoch', m.""Duration"") / 0.001) < 3700000.0"
);
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
virtual
async
Task
GroupBy_Property_Select_Sum_over_TimeSpan
(
bool
async
)
{
await
AssertQueryScalar
(
async
,
ss
=>
ss
.
Set
<
Mission
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Sum
(
g
.
Select
(
o
=>
o
.
Duration
))),
ss
=>
ss
.
Set
<
Mission
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
(
TimeSpan
?)
new
TimeSpan
(
g
.
Sum
(
o
=>
o
.
Duration
.
Ticks
))));
AssertSql
(
@"SELECT sum(m.""Duration"")
FROM ""Missions"" AS m
GROUP BY m.""Id"""
);
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
virtual
async
Task
GroupBy_Property_Select_Average_over_TimeSpan
(
bool
async
)
{
await
AssertQueryScalar
(
async
,
ss
=>
ss
.
Set
<
Mission
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Average
(
g
.
Select
(
o
=>
o
.
Duration
))),
ss
=>
ss
.
Set
<
Mission
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
(
TimeSpan
?)
new
TimeSpan
((
long
)
g
.
Average
(
o
=>
o
.
Duration
.
Ticks
))));
AssertSql
(
@"SELECT avg(m.""Duration"")
FROM ""Missions"" AS m
GROUP BY m.""Id"""
);
}
#
endregion
TimeSpan
#
region
DateOnly
...
...
test/EFCore.PG.FunctionalTests/Query/NorthwindFunctionsQueryNpgsqlTest.cs
浏览文件 @
1eed21e0
...
...
@@ -13,7 +13,7 @@ public class NorthwindFunctionsQueryNpgsqlTest : NorthwindFunctionsQueryRelation
:
base
(
fixture
)
{
ClearLog
();
Fixture
.
TestSqlLoggerFactory
.
SetTestOutputHelper
(
testOutputHelper
);
//
Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
public
override
async
Task
IsNullOrWhiteSpace_in_predicate
(
bool
async
)
...
...
test/EFCore.PG.FunctionalTests/Query/NorthwindGroupByQueryNpgsqlTest.cs
浏览文件 @
1eed21e0
...
...
@@ -3163,6 +3163,8 @@ into g
GROUP BY o.""CustomerID"""
);
}
// See aggregate tests over TimeSpan in GearsOfWarQueryNpsgqlTest
private
void
AssertSql
(
params
string
[]
expected
)
=>
Fixture
.
TestSqlLoggerFactory
.
AssertBaseline
(
expected
);
...
...
test/EFCore.PG.NodaTime.FunctionalTests/NodaTimeQueryNpgsqlTest.cs
浏览文件 @
1eed21e0
...
...
@@ -755,6 +755,48 @@ public Task Period_FromTicks_is_not_translated()
()
=>
ctx
.
Set
<
NodaTimeTypes
>().
Where
(
t
=>
Period
.
FromNanoseconds
(
t
.
Id
).
Seconds
==
1
).
ToListAsync
());
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
async
Task
GroupBy_Property_Select_Sum_over_Period
(
bool
async
)
{
using
var
ctx
=
CreateContext
();
// Note: Unlike Duration, Period can't be converted to total ticks (because its absolute time varies).
var
query
=
ctx
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Sum
(
g
.
Select
(
o
=>
o
.
Period
)));
_
=
async
?
await
query
.
ToListAsync
()
:
query
.
ToList
();
AssertSql
(
@"SELECT sum(n.""Period"")
FROM ""NodaTimeTypes"" AS n
GROUP BY n.""Id"""
);
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
async
Task
GroupBy_Property_Select_Average_over_Period
(
bool
async
)
{
using
var
ctx
=
CreateContext
();
// Note: Unlike Duration, Period can't be converted to total ticks (because its absolute time varies).
var
query
=
ctx
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Average
(
g
.
Select
(
o
=>
o
.
Period
)));
_
=
async
?
await
query
.
ToListAsync
()
:
query
.
ToList
();
AssertSql
(
@"SELECT avg(n.""Period"")
FROM ""NodaTimeTypes"" AS n
GROUP BY n.""Id"""
);
}
#
endregion
Period
#
region
Duration
...
...
@@ -894,6 +936,44 @@ public async Task Duration_Seconds(bool async)
WHERE floor(date_part('second', n.""Duration""))::int = 8"
);
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
async
Task
GroupBy_Property_Select_Sum_over_Duration
(
bool
async
)
{
await
AssertQueryScalar
(
async
,
ss
=>
ss
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Sum
(
g
.
Select
(
o
=>
o
.
Duration
))),
expectedQuery
:
ss
=>
ss
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
(
Duration
?)
Duration
.
FromTicks
(
g
.
Sum
(
o
=>
o
.
Duration
.
TotalTicks
))));
AssertSql
(
@"SELECT sum(n.""Duration"")
FROM ""NodaTimeTypes"" AS n
GROUP BY n.""Id"""
);
}
[
ConditionalTheory
]
[
MemberData
(
nameof
(
IsAsyncData
))]
public
async
Task
GroupBy_Property_Select_Average_over_Duration
(
bool
async
)
{
await
AssertQueryScalar
(
async
,
ss
=>
ss
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
EF
.
Functions
.
Average
(
g
.
Select
(
o
=>
o
.
Duration
))),
expectedQuery
:
ss
=>
ss
.
Set
<
NodaTimeTypes
>()
.
GroupBy
(
o
=>
o
.
Id
)
.
Select
(
g
=>
(
Duration
?)
Duration
.
FromTicks
((
long
)
g
.
Average
(
o
=>
o
.
Duration
.
TotalTicks
))));
AssertSql
(
@"SELECT avg(n.""Duration"")
FROM ""NodaTimeTypes"" AS n
GROUP BY n.""Id"""
);
}
#
endregion
#
region
Interval
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录