Skip to content

Commit e29487e

Browse files
committed
Add AllDescending to IndexAttribute
1 parent 15800e3 commit e29487e

File tree

7 files changed

+128
-7
lines changed

7 files changed

+128
-7
lines changed

src/EFCore.Abstractions/IndexAttribute.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public sealed class IndexAttribute : Attribute
1818
private string? _name;
1919
private bool? _isUnique;
2020
private bool[]? _isDescending;
21+
private bool _allDescending;
2122

2223
/// <summary>
2324
/// Initializes a new instance of the <see cref="IndexAttribute" /> class.
@@ -63,16 +64,41 @@ public bool[]? IsDescending
6364
get => _isDescending;
6465
set
6566
{
66-
if (value is not null && value.Length != PropertyNames.Count)
67+
if (value is not null)
6768
{
68-
throw new ArgumentException(
69-
AbstractionsStrings.InvalidNumberOfIndexSortOrderValues(value.Length, PropertyNames.Count), nameof(IsDescending));
69+
if (value.Length != PropertyNames.Count)
70+
{
71+
throw new ArgumentException(
72+
AbstractionsStrings.InvalidNumberOfIndexSortOrderValues(value.Length, PropertyNames.Count), nameof(IsDescending));
73+
}
74+
75+
if (_allDescending)
76+
{
77+
throw new ArgumentException(AbstractionsStrings.CannotSpecifyBothIsDescendingAndAllDescending);
78+
}
7079
}
7180

7281
_isDescending = value;
7382
}
7483
}
7584

85+
/// <summary>
86+
/// Whether all index columns have descending sort order.
87+
/// </summary>
88+
public bool AllDescending
89+
{
90+
get => _allDescending;
91+
set
92+
{
93+
if (IsDescending is not null)
94+
{
95+
throw new ArgumentException(AbstractionsStrings.CannotSpecifyBothIsDescendingAndAllDescending);
96+
}
97+
98+
_allDescending = value;
99+
}
100+
}
101+
76102
/// <summary>
77103
/// Checks whether <see cref="IsUnique" /> has been explicitly set to a value.
78104
/// </summary>

src/EFCore.Abstractions/Properties/AbstractionsStrings.Designer.cs

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

src/EFCore.Abstractions/Properties/AbstractionsStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@
123123
<data name="ArgumentIsNegativeNumber" xml:space="preserve">
124124
<value>The number argument '{argumentName}' cannot be negative number.</value>
125125
</data>
126+
<data name="CannotSpecifyBothIsDescendingAndAllDescending" xml:space="preserve">
127+
<value>IsDescending and AllDescending cannot both be specified on the [Index] attribute.</value>
128+
</data>
126129
<data name="CollectionArgumentHasEmptyElements" xml:space="preserve">
127130
<value>The collection argument '{argumentName}' must not contain any empty elements.</value>
128131
</data>

src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ private void GenerateIndexAttributes(IEntityType entityType)
215215

216216
if (index.IsDescending is not null)
217217
{
218-
indexAttribute.AddParameter($"{nameof(IndexAttribute.IsDescending)} = {_code.UnknownLiteral(index.IsDescending)}");
218+
indexAttribute.AddParameter(
219+
index.IsDescending.Count == 0
220+
? $"{nameof(IndexAttribute.AllDescending)} = true"
221+
: $"{nameof(IndexAttribute.IsDescending)} = {_code.UnknownLiteral(index.IsDescending)}");
219222
}
220223

221224
_sb.AppendLine(indexAttribute.ToString());

src/EFCore/Metadata/Conventions/IndexAttributeConvention.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,16 @@ private static void CheckIndexAttributesAndEnsureIndex(
120120
indexBuilder = indexBuilder.IsUnique(indexAttribute.IsUnique, fromDataAnnotation: true);
121121
}
122122

123-
if (indexBuilder is not null && indexAttribute.IsDescending is not null)
123+
if (indexBuilder is not null)
124124
{
125-
indexBuilder.IsDescending(indexAttribute.IsDescending, fromDataAnnotation: true);
125+
if (indexAttribute.AllDescending)
126+
{
127+
indexBuilder.IsDescending(Array.Empty<bool>(), fromDataAnnotation: true);
128+
}
129+
else if (indexAttribute.IsDescending is not null)
130+
{
131+
indexBuilder.IsDescending(indexAttribute.IsDescending, fromDataAnnotation: true);
132+
}
126133
}
127134
}
128135
}

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,71 @@ public partial class EntityWithIndexes
303303
t => Assert.Equal("IndexOnBAndC", t.Name));
304304
});
305305

306+
[ConditionalFact]
307+
public void IndexAttribute_is_generated_with_ascending_descending()
308+
=> Test(
309+
modelBuilder => modelBuilder
310+
.Entity(
311+
"EntityWithAscendingDescendingIndexes",
312+
x =>
313+
{
314+
x.Property<int>("Id");
315+
x.Property<int>("A");
316+
x.Property<int>("B");
317+
x.HasKey("Id");
318+
x.HasIndex(new[] { "A", "B" }, "AllAscending");
319+
x.HasIndex(new[] { "A", "B" }, "PartiallyDescending").IsDescending(true, false);
320+
x.HasIndex(new[] { "A", "B" }, "AllDescending").IsDescending();
321+
}),
322+
new ModelCodeGenerationOptions { UseDataAnnotations = true },
323+
code =>
324+
{
325+
AssertFileContents(
326+
@"using System;
327+
using System.Collections.Generic;
328+
using System.ComponentModel.DataAnnotations;
329+
using System.ComponentModel.DataAnnotations.Schema;
330+
using Microsoft.EntityFrameworkCore;
331+
332+
namespace TestNamespace
333+
{
334+
[Index(""A"", ""B"", Name = ""AllAscending"")]
335+
[Index(""A"", ""B"", Name = ""AllDescending"", AllDescending = true)]
336+
[Index(""A"", ""B"", Name = ""PartiallyDescending"", IsDescending = new[] { true, false })]
337+
public partial class EntityWithAscendingDescendingIndexes
338+
{
339+
[Key]
340+
public int Id { get; set; }
341+
public int A { get; set; }
342+
public int B { get; set; }
343+
}
344+
}
345+
",
346+
code.AdditionalFiles.Single(f => f.Path == "EntityWithAscendingDescendingIndexes.cs"));
347+
},
348+
model =>
349+
{
350+
var entityType = model.FindEntityType("TestNamespace.EntityWithAscendingDescendingIndexes");
351+
var indexes = entityType.GetIndexes();
352+
Assert.Collection(
353+
indexes,
354+
i =>
355+
{
356+
Assert.Equal("AllAscending", i.Name);
357+
Assert.Null(i.IsDescending);
358+
},
359+
i =>
360+
{
361+
Assert.Equal("AllDescending", i.Name);
362+
Assert.Equal(Array.Empty<bool>(), i.IsDescending);
363+
},
364+
i =>
365+
{
366+
Assert.Equal("PartiallyDescending", i.Name);
367+
Assert.Equal(new[] { true, false }, i.IsDescending);
368+
});
369+
});
370+
306371
[ConditionalFact]
307372
public void Entity_with_indexes_generates_IndexAttribute_only_for_indexes_without_annotations()
308373
=> Test(

test/EFCore.Tests/Metadata/Conventions/IndexAttributeConventionTest.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ public void IndexAttribute_properties_cannot_include_whitespace(Type entityTypeW
9797
() => modelBuilder.Entity(entityTypeWithInvalidIndex)).Message);
9898
}
9999

100+
[ConditionalFact]
101+
public void IndexAttribute_AllDescending_is_applied()
102+
{
103+
var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder();
104+
var entityBuilder = modelBuilder.Entity<EntityWithTwoIndexes>();
105+
modelBuilder.Model.FinalizeModel();
106+
107+
var allDescendingIndex = entityBuilder.Metadata.FindIndex("IndexOnBAndC")!;
108+
Assert.Equal(Array.Empty<bool>(), allDescendingIndex.IsDescending);
109+
}
110+
100111
[ConditionalFact]
101112
public void IndexAttribute_can_be_applied_more_than_once_per_entity_type()
102113
{
@@ -332,7 +343,7 @@ private class EntityWithIndex
332343
}
333344

334345
[Index(nameof(A), nameof(B), Name = "IndexOnAAndB", IsUnique = true)]
335-
[Index(nameof(B), nameof(C), Name = "IndexOnBAndC", IsUnique = false)]
346+
[Index(nameof(B), nameof(C), Name = "IndexOnBAndC", IsUnique = false, AllDescending = true)]
336347
private class EntityWithTwoIndexes
337348
{
338349
public int Id { get; set; }

0 commit comments

Comments
 (0)