Skip to content

Commit 0675a89

Browse files
committed
Make filtered unique index update dependencies soft
Fixes #28065
1 parent 99f03c7 commit 0675a89

File tree

5 files changed

+52
-13
lines changed

5 files changed

+52
-13
lines changed

src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
108108
/// <param name="index">The index.</param>
109109
/// <returns>The index filter expression.</returns>
110110
public static string? GetFilter(this IReadOnlyIndex index)
111-
=> (index is RuntimeIndex)
112-
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
113-
: (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;
111+
=> (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value;
114112

115113
/// <summary>
116114
/// Returns the index filter expression.
@@ -120,11 +118,6 @@ public static void SetDatabaseName(this IMutableIndex index, string? name)
120118
/// <returns>The index filter expression.</returns>
121119
public static string? GetFilter(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
122120
{
123-
if (index is RuntimeIndex)
124-
{
125-
throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData);
126-
}
127-
128121
var annotation = index.FindAnnotation(RelationalAnnotationNames.Filter);
129122
if (annotation != null)
130123
{

src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,10 @@ protected override void ProcessIndexAnnotations(
433433
{
434434
base.ProcessIndexAnnotations(annotations, index, runtimeIndex, runtime);
435435

436-
annotations.Remove(runtime ? RelationalAnnotationNames.TableIndexMappings : RelationalAnnotationNames.Filter);
436+
if (runtime)
437+
{
438+
annotations.Remove(RelationalAnnotationNames.TableIndexMappings);
439+
}
437440
}
438441

439442
/// <summary>

src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,11 @@ public virtual IReadOnlyList<List<IReadOnlyModificationCommand>> TopologicalSort
306306

307307
AddSameTableEdges(_modificationCommandGraph);
308308

309-
return _modificationCommandGraph.BatchingTopologicalSort(static (_, _, edges) => edges.All(e => e is ITable), FormatCycle);
309+
return _modificationCommandGraph.BatchingTopologicalSort(
310+
static (_, _, edges) => edges.All(e =>
311+
e is ITable
312+
|| (e is ITableIndex index && index.Filter != null)),
313+
FormatCycle);
310314
}
311315

312316
private string FormatCycle(

test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,9 +1870,7 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
18701870
Assert.Equal("AlternateIndex", alternateIndex.Name);
18711871
Assert.Equal("AIX", alternateIndex.GetDatabaseName());
18721872
Assert.Null(alternateIndex[RelationalAnnotationNames.Filter]);
1873-
Assert.Equal(
1874-
CoreStrings.RuntimeModelMissingData,
1875-
Assert.Throws<InvalidOperationException>(() => alternateIndex.GetFilter()).Message);
1873+
Assert.Null(alternateIndex.GetFilter());
18761874

18771875
Assert.Equal(new[] { compositeIndex, alternateIndex }, principalAlternateId.GetContainingIndexes());
18781876

test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,45 @@ public virtual void Save_with_shared_foreign_key()
108108
});
109109
}
110110

111+
[ConditionalFact]
112+
public virtual void Swap_filtered_unique_index_values()
113+
{
114+
var productId1 = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146");
115+
var productId2 = new Guid("0edc9136-7eed-463b-9b97-bdb9648ab877");
116+
117+
ExecuteWithStrategyInTransaction(
118+
context =>
119+
{
120+
var product1 = context.Products.Find(productId1)!;
121+
var product2 = context.Products.Find(productId2)!;
122+
123+
product2.Name = null;
124+
product2.Price = product1.Price;
125+
126+
context.SaveChanges();
127+
},
128+
context =>
129+
{
130+
var product1 = context.Products.Find(productId1)!;
131+
var product2 = context.Products.Find(productId2)!;
132+
133+
product2.Name = product1.Name;
134+
product1.Name = null;
135+
136+
context.SaveChanges();
137+
},
138+
context =>
139+
{
140+
var product1 = context.Products.Find(productId1)!;
141+
var product2 = context.Products.Find(productId2)!;
142+
143+
Assert.Equal(1.49M, product1.Price);
144+
Assert.Null(product1.Name);
145+
Assert.Equal(1.49M, product2.Price);
146+
Assert.Equal("Apple Cider", product2.Name);
147+
});
148+
}
149+
111150
[ConditionalFact]
112151
public abstract void Identifiers_are_generated_correctly();
113152

@@ -133,6 +172,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
133172
modelBuilder.Entity<ProductTableWithView>().HasBaseType((string)null).ToView("ProductView").ToTable("ProductTable");
134173
modelBuilder.Entity<ProductTableView>().HasBaseType((string)null).ToView("ProductTable");
135174

175+
modelBuilder.Entity<Product>().HasIndex(p => new { p.Name, p.Price }).IsUnique().HasFilter("Name IS NOT NULL");
176+
136177
modelBuilder
137178
.Entity<
138179
LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectlyDetails

0 commit comments

Comments
 (0)