Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src/EFCore.Cosmos/Properties/CosmosStrings.resx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
Comment thread
AndriySvyryd marked this conversation as resolved.
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -424,4 +424,7 @@
<data name="WithPartitionKeyNotConstantOrParameter" xml:space="preserve">
<value>'WithPartitionKey' only accepts simple constant or parameter arguments. See https://aka.ms/efdocs-cosmos-partition-keys for more information.</value>
</data>
<data name="WithPartitionKeyConflictingPartitionKeyPredicate" xml:space="preserve">
<value>The partition key value specified via 'WithPartitionKey' conflicts with a partition key predicate in the query. Remove the predicate or ensure both specify the same value.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ public virtual Expression ExtractPartitionKeysAndId(
var allIdPropertiesSpecified =
_jsonIdPropertyValues.Values.All(p => p is not null) && _jsonIdPropertyValues.Count > 0;

if (queryCompilationContext.PartitionKeyPropertyValues.Count > 0)
{
ValidateNoWithPartitionKeyConflict(queryCompilationContext, partitionKeyProperties);
}

Comment thread
lorenacr marked this conversation as resolved.
Outdated
// First, go over the partition key properties and lift them from the predicate to the query compilation context, as possible.
// We do this only as long as all partition key values are provided; the moment there's a gap we stop (so if PK1 and PK3 are
// provided but not PK2, only PK1 will be lifted out).
Expand Down Expand Up @@ -170,6 +175,49 @@ Expression Unwrap(Expression shaper)
}
}

private void ValidateNoWithPartitionKeyConflict(
CosmosQueryCompilationContext queryCompilationContext,
IReadOnlyList<IProperty> partitionKeyProperties)
{
// WithPartitionKey(...) already populated partition key values in the query compilation context.
// If the predicate also contains partition key comparisons, we must validate that they match; otherwise, a ReadItem
// optimization would execute with the WithPartitionKey partition key and ignore the conflicting predicate.
for (var i = 0; i < partitionKeyProperties.Count; i++)
{
var property = partitionKeyProperties[i];
var predicateValueExpression = _partitionKeyPropertyValues[property].ValueExpression;
if (predicateValueExpression is null)
{
continue;
}

if (queryCompilationContext.PartitionKeyPropertyValues.Count <= i)
{
// Shouldn't happen: WithPartitionKey doesn't specify enough PK components; let the existing missing-PK flow handle it.
break;
}

var withPartitionKeyValueExpression = queryCompilationContext.PartitionKeyPropertyValues[i];

if (!PartitionKeyValueExpressionsMatch(predicateValueExpression, withPartitionKeyValueExpression))
{
throw new InvalidOperationException(CosmosStrings.WithPartitionKeyConflictingPartitionKeyPredicate);
}
}

static bool PartitionKeyValueExpressionsMatch(Expression left, Expression right)
=> (left, right) switch
{
(SqlConstantExpression { Value: var leftValue }, SqlConstantExpression { Value: var rightValue })
=> Equals(leftValue, rightValue),

(SqlParameterExpression { Name: var leftName }, SqlParameterExpression { Name: var rightName })
=> leftName == rightName,

_ => false
};
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query;
Expand Down Expand Up @@ -285,6 +285,28 @@ public virtual Task ReadItem_with_WithPartitionKey()
ss => ss.Set<SinglePartitionKeyEntity>().Where(e => e.PartitionKey == "PK1")
.Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")));

[ConditionalFact] // Issue #38238
public virtual async Task ReadItem_with_WithPartitionKey_and_conflicting_partition_key_predicate_throws()
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => AssertQuery(
async: true,
ss => ss.Set<SinglePartitionKeyEntity>().WithPartitionKey("PK1")
.Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == "PK2")));

Assert.Equal(CosmosStrings.WithPartitionKeyConflictingPartitionKeyPredicate, exception.Message);
}

[ConditionalFact] // Issue #38238
public virtual async Task ReadItem_with_WithPartitionKey_and_partition_key_predicate_same_parameter_does_not_throw()
{
var partitionKey = "PK1";

await AssertQuery(
async: true,
ss => ss.Set<SinglePartitionKeyEntity>().WithPartitionKey(partitionKey)
.Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == partitionKey));
Comment thread
lorenacr marked this conversation as resolved.
}

[ConditionalFact]
public virtual Task ReadItem_with_WithPartitionKey_with_only_partition_key()
=> AssertQuery(
Expand Down
Loading