Skip to content

Commit d01876b

Browse files
authored
Properly implement support for Cosmos hierarchical partition keys (#34557) (#34560)
Fixes #34553 (cherry picked from commit a72420c)
1 parent b59aa3a commit d01876b

17 files changed

+323
-184
lines changed

src/EFCore.Cosmos/Extensions/CosmosQueryableExtensions.cs

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,58 +18,118 @@ namespace Microsoft.EntityFrameworkCore;
1818
/// </remarks>
1919
public static class CosmosQueryableExtensions
2020
{
21-
internal static readonly MethodInfo WithPartitionKeyMethodInfo
21+
internal static readonly MethodInfo WithPartitionKeyMethodInfo1
22+
= typeof(CosmosQueryableExtensions).GetTypeInfo()
23+
.GetDeclaredMethods(nameof(WithPartitionKey))
24+
.Single(mi => mi.GetParameters().Length == 2);
25+
26+
internal static readonly MethodInfo WithPartitionKeyMethodInfo2
2227
= typeof(CosmosQueryableExtensions).GetTypeInfo()
2328
.GetDeclaredMethods(nameof(WithPartitionKey))
2429
.Single(mi => mi.GetParameters().Length == 3);
2530

31+
internal static readonly MethodInfo WithPartitionKeyMethodInfo3
32+
= typeof(CosmosQueryableExtensions).GetTypeInfo()
33+
.GetDeclaredMethods(nameof(WithPartitionKey))
34+
.Single(mi => mi.GetParameters().Length == 4);
35+
2636
/// <summary>
27-
/// Specify the partition key value for partition used for the query. Required when using
28-
/// a resource token that provides permission based on a partition key for authentication.
37+
/// Specify the partition key for partition used for the query.
38+
/// Required when using a resource token that provides permission based on a partition key for authentication,
2939
/// </summary>
3040
/// <remarks>
3141
/// See <see href="https://aka.ms/efcore-docs-query">Querying data with EF Core</see>, and
3242
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
3343
/// </remarks>
3444
/// <typeparam name="TEntity">The type of entity being queried.</typeparam>
3545
/// <param name="source">The source query.</param>
36-
/// <param name="partitionKey">The partition key value.</param>
46+
/// <param name="partitionKeyValue">The partition key value.</param>
3747
/// <returns>A new query with the set partition key.</returns>
38-
public static IQueryable<TEntity> WithPartitionKey<TEntity>(this IQueryable<TEntity> source, string partitionKey)
48+
public static IQueryable<TEntity> WithPartitionKey<TEntity>(this IQueryable<TEntity> source, object partitionKeyValue)
3949
where TEntity : class
40-
=> WithPartitionKey(source, partitionKey, []);
50+
{
51+
Check.NotNull(partitionKeyValue, nameof(partitionKeyValue));
52+
53+
return
54+
source.Provider is EntityQueryProvider
55+
? source.Provider.CreateQuery<TEntity>(
56+
Expression.Call(
57+
instance: null,
58+
method: WithPartitionKeyMethodInfo1.MakeGenericMethod(typeof(TEntity)),
59+
source.Expression,
60+
Expression.Constant(partitionKeyValue, typeof(object))))
61+
: source;
62+
}
4163

4264
/// <summary>
43-
/// Specify the partition key for partition used for the query. Required when using
44-
/// a resource token that provides permission based on a partition key for authentication,
65+
/// Specify the partition key for partition used for the query.
66+
/// Required when using a resource token that provides permission based on a partition key for authentication,
4567
/// </summary>
4668
/// <remarks>
4769
/// See <see href="https://aka.ms/efcore-docs-query">Querying data with EF Core</see>, and
4870
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
4971
/// </remarks>
5072
/// <typeparam name="TEntity">The type of entity being queried.</typeparam>
5173
/// <param name="source">The source query.</param>
52-
/// <param name="partitionKeyValue">The partition key value.</param>
53-
/// <param name="additionalPartitionKeyValues">Additional values for hierarchical partitions.</param>
74+
/// <param name="partitionKeyValue1">The first value in a hierarchical partition key.</param>
75+
/// <param name="partitionKeyValue2">The second value in a hierarchical partition key.</param>
5476
/// <returns>A new query with the set partition key.</returns>
5577
public static IQueryable<TEntity> WithPartitionKey<TEntity>(
5678
this IQueryable<TEntity> source,
57-
object partitionKeyValue,
58-
params object[] additionalPartitionKeyValues)
79+
object partitionKeyValue1,
80+
object partitionKeyValue2)
5981
where TEntity : class
6082
{
61-
Check.NotNull(partitionKeyValue, nameof(partitionKeyValue));
62-
Check.HasNoNulls(additionalPartitionKeyValues, nameof(additionalPartitionKeyValues));
83+
Check.NotNull(partitionKeyValue1, nameof(partitionKeyValue1));
84+
Check.NotNull(partitionKeyValue2, nameof(partitionKeyValue2));
85+
86+
return
87+
source.Provider is EntityQueryProvider
88+
? source.Provider.CreateQuery<TEntity>(
89+
Expression.Call(
90+
instance: null,
91+
method: WithPartitionKeyMethodInfo2.MakeGenericMethod(typeof(TEntity)),
92+
source.Expression,
93+
Expression.Constant(partitionKeyValue1, typeof(object)),
94+
Expression.Constant(partitionKeyValue2, typeof(object))))
95+
: source;
96+
}
97+
98+
/// <summary>
99+
/// Specify the partition key for partition used for the query.
100+
/// Required when using a resource token that provides permission based on a partition key for authentication,
101+
/// </summary>
102+
/// <remarks>
103+
/// See <see href="https://aka.ms/efcore-docs-query">Querying data with EF Core</see>, and
104+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
105+
/// </remarks>
106+
/// <typeparam name="TEntity">The type of entity being queried.</typeparam>
107+
/// <param name="source">The source query.</param>
108+
/// <param name="partitionKeyValue1">The first value in a hierarchical partition key.</param>
109+
/// <param name="partitionKeyValue2">The second value in a hierarchical partition key.</param>
110+
/// <param name="partitionKeyValue3">The third value in a hierarchical partition key.</param>
111+
/// <returns>A new query with the set partition key.</returns>
112+
public static IQueryable<TEntity> WithPartitionKey<TEntity>(
113+
this IQueryable<TEntity> source,
114+
object partitionKeyValue1,
115+
object partitionKeyValue2,
116+
object partitionKeyValue3)
117+
where TEntity : class
118+
{
119+
Check.NotNull(partitionKeyValue1, nameof(partitionKeyValue1));
120+
Check.NotNull(partitionKeyValue2, nameof(partitionKeyValue2));
121+
Check.NotNull(partitionKeyValue3, nameof(partitionKeyValue3));
63122

64123
return
65124
source.Provider is EntityQueryProvider
66125
? source.Provider.CreateQuery<TEntity>(
67126
Expression.Call(
68127
instance: null,
69-
method: WithPartitionKeyMethodInfo.MakeGenericMethod(typeof(TEntity)),
128+
method: WithPartitionKeyMethodInfo3.MakeGenericMethod(typeof(TEntity)),
70129
source.Expression,
71-
Expression.Constant(partitionKeyValue, typeof(object)),
72-
Expression.Constant(additionalPartitionKeyValues, typeof(object[]))))
130+
Expression.Constant(partitionKeyValue1, typeof(object)),
131+
Expression.Constant(partitionKeyValue2, typeof(object)),
132+
Expression.Constant(partitionKeyValue3, typeof(object))))
73133
: source;
74134
}
75135

src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EFCore.Cosmos/Properties/CosmosStrings.resx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,6 @@
174174
<data name="IdNonStringStoreType" xml:space="preserve">
175175
<value>The type of the '{idProperty}' property on '{entityType}' is '{propertyType}'. All 'id' properties must be strings or have a string value converter.</value>
176176
</data>
177-
<data name="IncorrectPartitionKeyNumber" xml:space="preserve">
178-
<value>{actual} partition key values were provided, but the entity type '{entityType}' has {expected} partition key values defined.</value>
179-
</data>
180177
<data name="IndexesExist" xml:space="preserve">
181178
<value>The entity type '{entityType}' has an index defined over properties '{properties}'. The Azure Cosmos DB provider for EF Core currently does not support index definitions.</value>
182179
</data>

src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -172,23 +172,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
172172

173173
var innerQueryable = Visit(methodCallExpression.Arguments[0]);
174174

175-
var firstValue = _sqlTranslator.Translate(methodCallExpression.Arguments[1], applyDefaultTypeMapping: false);
176-
if (firstValue is not SqlConstantExpression and not SqlParameterExpression)
175+
for (var i = 1; i < methodCallExpression.Arguments.Count; i++)
177176
{
178-
throw new InvalidOperationException(CosmosStrings.WithPartitionKeyNotConstantOrParameter);
179-
}
180-
181-
_queryCompilationContext.PartitionKeyPropertyValues.Add(firstValue);
182-
183-
if (methodCallExpression.Arguments.Count == 3)
184-
{
185-
var remainingValuesArray = _sqlTranslator.Translate(methodCallExpression.Arguments[2], applyDefaultTypeMapping: false);
186-
if (remainingValuesArray is not SqlParameterExpression)
177+
var value = _sqlTranslator.Translate(methodCallExpression.Arguments[i], applyDefaultTypeMapping: false);
178+
if (value is not SqlConstantExpression and not SqlParameterExpression)
187179
{
188180
throw new InvalidOperationException(CosmosStrings.WithPartitionKeyNotConstantOrParameter);
189181
}
190182

191-
_queryCompilationContext.PartitionKeyPropertyValues.Add(remainingValuesArray);
183+
_queryCompilationContext.PartitionKeyPropertyValues.Add(value);
192184
}
193185

194186
return innerQueryable;

0 commit comments

Comments
 (0)