diff --git a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
index 036729feee6..853a48d8c08 100644
--- a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
@@ -108,9 +108,7 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
/// The index.
/// The index filter expression.
public static string? GetFilter(this IReadOnlyIndex index)
- => (index is RuntimeIndex)
- ? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
- : (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;
+ => (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;
///
/// Returns the index filter expression.
@@ -120,11 +118,6 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
/// The index filter expression.
public static string? GetFilter(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
{
- if (index is RuntimeIndex)
- {
- throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData);
- }
-
var annotation = index.FindAnnotation(RelationalAnnotationNames.Filter);
if (annotation != null)
{
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
index 85cffeec6fd..4a0eafeb4c9 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
@@ -433,7 +433,10 @@ protected override void ProcessIndexAnnotations(
{
base.ProcessIndexAnnotations(annotations, index, runtimeIndex, runtime);
- annotations.Remove(runtime ? RelationalAnnotationNames.TableIndexMappings : RelationalAnnotationNames.Filter);
+ if (runtime)
+ {
+ annotations.Remove(RelationalAnnotationNames.TableIndexMappings);
+ }
}
///
diff --git a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
index 9883b75e8a6..f668f2b3ae6 100644
--- a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
+++ b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
@@ -306,7 +306,11 @@ public virtual IReadOnlyList> TopologicalSort
AddSameTableEdges(_modificationCommandGraph);
- return _modificationCommandGraph.BatchingTopologicalSort(static (_, _, edges) => edges.All(e => e is ITable), FormatCycle);
+ return _modificationCommandGraph.BatchingTopologicalSort(
+ static (_, _, edges) => edges.All(e =>
+ e is ITable
+ || (e is ITableIndex index && index.Filter != null)),
+ FormatCycle);
}
private string FormatCycle(
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
index a3f20003b8e..257cff9a12a 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
@@ -1870,9 +1870,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
Assert.Equal("AlternateIndex", alternateIndex.Name);
Assert.Equal("AIX", alternateIndex.GetDatabaseName());
Assert.Null(alternateIndex[RelationalAnnotationNames.Filter]);
- Assert.Equal(
- CoreStrings.RuntimeModelMissingData,
- Assert.Throws(() => alternateIndex.GetFilter()).Message);
+ Assert.Null(alternateIndex.GetFilter());
Assert.Equal(new[] { compositeIndex, alternateIndex }, principalAlternateId.GetContainingIndexes());
diff --git a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
index f14a6dbed80..2553227b87e 100644
--- a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
@@ -108,6 +108,45 @@ public virtual void Save_with_shared_foreign_key()
});
}
+ [ConditionalFact]
+ public virtual void Swap_filtered_unique_index_values()
+ {
+ var productId1 = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146");
+ var productId2 = new Guid("0edc9136-7eed-463b-9b97-bdb9648ab877");
+
+ ExecuteWithStrategyInTransaction(
+ context =>
+ {
+ var product1 = context.Products.Find(productId1)!;
+ var product2 = context.Products.Find(productId2)!;
+
+ product2.Name = null;
+ product2.Price = product1.Price;
+
+ context.SaveChanges();
+ },
+ context =>
+ {
+ var product1 = context.Products.Find(productId1)!;
+ var product2 = context.Products.Find(productId2)!;
+
+ product2.Name = product1.Name;
+ product1.Name = null;
+
+ context.SaveChanges();
+ },
+ context =>
+ {
+ var product1 = context.Products.Find(productId1)!;
+ var product2 = context.Products.Find(productId2)!;
+
+ Assert.Equal(1.49M, product1.Price);
+ Assert.Null(product1.Name);
+ Assert.Equal(1.49M, product2.Price);
+ Assert.Equal("Apple Cider", product2.Name);
+ });
+ }
+
[ConditionalFact]
public abstract void Identifiers_are_generated_correctly();
@@ -133,6 +172,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity().HasBaseType((string)null).ToView("ProductView").ToTable("ProductTable");
modelBuilder.Entity().HasBaseType((string)null).ToView("ProductTable");
+ modelBuilder.Entity().HasIndex(p => new { p.Name, p.Price }).IsUnique().HasFilter("Name IS NOT NULL");
+
modelBuilder
.Entity<
LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectlyDetails