Skip to content

Commit

Permalink
Add AllDescending to IndexAttribute
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Jul 26, 2022
1 parent 15800e3 commit e29487e
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 7 deletions.
32 changes: 29 additions & 3 deletions src/EFCore.Abstractions/IndexAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public sealed class IndexAttribute : Attribute
private string? _name;
private bool? _isUnique;
private bool[]? _isDescending;
private bool _allDescending;

/// <summary>
/// Initializes a new instance of the <see cref="IndexAttribute" /> class.
Expand Down Expand Up @@ -63,16 +64,41 @@ public bool[]? IsDescending
get => _isDescending;
set
{
if (value is not null && value.Length != PropertyNames.Count)
if (value is not null)
{
throw new ArgumentException(
AbstractionsStrings.InvalidNumberOfIndexSortOrderValues(value.Length, PropertyNames.Count), nameof(IsDescending));
if (value.Length != PropertyNames.Count)
{
throw new ArgumentException(
AbstractionsStrings.InvalidNumberOfIndexSortOrderValues(value.Length, PropertyNames.Count), nameof(IsDescending));
}

if (_allDescending)
{
throw new ArgumentException(AbstractionsStrings.CannotSpecifyBothIsDescendingAndAllDescending);
}
}

_isDescending = value;
}
}

/// <summary>
/// Whether all index columns have descending sort order.
/// </summary>
public bool AllDescending
{
get => _allDescending;
set
{
if (IsDescending is not null)
{
throw new ArgumentException(AbstractionsStrings.CannotSpecifyBothIsDescendingAndAllDescending);
}

_allDescending = value;
}
}

/// <summary>
/// Checks whether <see cref="IsUnique" /> has been explicitly set to a value.
/// </summary>
Expand Down

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

3 changes: 3 additions & 0 deletions src/EFCore.Abstractions/Properties/AbstractionsStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@
<data name="ArgumentIsNegativeNumber" xml:space="preserve">
<value>The number argument '{argumentName}' cannot be negative number.</value>
</data>
<data name="CannotSpecifyBothIsDescendingAndAllDescending" xml:space="preserve">
<value>IsDescending and AllDescending cannot both be specified on the [Index] attribute.</value>
</data>
<data name="CollectionArgumentHasEmptyElements" xml:space="preserve">
<value>The collection argument '{argumentName}' must not contain any empty elements.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,10 @@ private void GenerateIndexAttributes(IEntityType entityType)

if (index.IsDescending is not null)
{
indexAttribute.AddParameter($"{nameof(IndexAttribute.IsDescending)} = {_code.UnknownLiteral(index.IsDescending)}");
indexAttribute.AddParameter(
index.IsDescending.Count == 0
? $"{nameof(IndexAttribute.AllDescending)} = true"
: $"{nameof(IndexAttribute.IsDescending)} = {_code.UnknownLiteral(index.IsDescending)}");
}

_sb.AppendLine(indexAttribute.ToString());
Expand Down
11 changes: 9 additions & 2 deletions src/EFCore/Metadata/Conventions/IndexAttributeConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,16 @@ private static void CheckIndexAttributesAndEnsureIndex(
indexBuilder = indexBuilder.IsUnique(indexAttribute.IsUnique, fromDataAnnotation: true);
}

if (indexBuilder is not null && indexAttribute.IsDescending is not null)
if (indexBuilder is not null)
{
indexBuilder.IsDescending(indexAttribute.IsDescending, fromDataAnnotation: true);
if (indexAttribute.AllDescending)
{
indexBuilder.IsDescending(Array.Empty<bool>(), fromDataAnnotation: true);
}
else if (indexAttribute.IsDescending is not null)
{
indexBuilder.IsDescending(indexAttribute.IsDescending, fromDataAnnotation: true);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,71 @@ public partial class EntityWithIndexes
t => Assert.Equal("IndexOnBAndC", t.Name));
});

[ConditionalFact]
public void IndexAttribute_is_generated_with_ascending_descending()
=> Test(
modelBuilder => modelBuilder
.Entity(
"EntityWithAscendingDescendingIndexes",
x =>
{
x.Property<int>("Id");
x.Property<int>("A");
x.Property<int>("B");
x.HasKey("Id");
x.HasIndex(new[] { "A", "B" }, "AllAscending");
x.HasIndex(new[] { "A", "B" }, "PartiallyDescending").IsDescending(true, false);
x.HasIndex(new[] { "A", "B" }, "AllDescending").IsDescending();
}),
new ModelCodeGenerationOptions { UseDataAnnotations = true },
code =>
{
AssertFileContents(
@"using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace TestNamespace
{
[Index(""A"", ""B"", Name = ""AllAscending"")]
[Index(""A"", ""B"", Name = ""AllDescending"", AllDescending = true)]
[Index(""A"", ""B"", Name = ""PartiallyDescending"", IsDescending = new[] { true, false })]
public partial class EntityWithAscendingDescendingIndexes
{
[Key]
public int Id { get; set; }
public int A { get; set; }
public int B { get; set; }
}
}
",
code.AdditionalFiles.Single(f => f.Path == "EntityWithAscendingDescendingIndexes.cs"));
},
model =>
{
var entityType = model.FindEntityType("TestNamespace.EntityWithAscendingDescendingIndexes");
var indexes = entityType.GetIndexes();
Assert.Collection(
indexes,
i =>
{
Assert.Equal("AllAscending", i.Name);
Assert.Null(i.IsDescending);
},
i =>
{
Assert.Equal("AllDescending", i.Name);
Assert.Equal(Array.Empty<bool>(), i.IsDescending);
},
i =>
{
Assert.Equal("PartiallyDescending", i.Name);
Assert.Equal(new[] { true, false }, i.IsDescending);
});
});

[ConditionalFact]
public void Entity_with_indexes_generates_IndexAttribute_only_for_indexes_without_annotations()
=> Test(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ public void IndexAttribute_properties_cannot_include_whitespace(Type entityTypeW
() => modelBuilder.Entity(entityTypeWithInvalidIndex)).Message);
}

[ConditionalFact]
public void IndexAttribute_AllDescending_is_applied()
{
var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder();
var entityBuilder = modelBuilder.Entity<EntityWithTwoIndexes>();
modelBuilder.Model.FinalizeModel();

var allDescendingIndex = entityBuilder.Metadata.FindIndex("IndexOnBAndC")!;
Assert.Equal(Array.Empty<bool>(), allDescendingIndex.IsDescending);
}

[ConditionalFact]
public void IndexAttribute_can_be_applied_more_than_once_per_entity_type()
{
Expand Down Expand Up @@ -332,7 +343,7 @@ private class EntityWithIndex
}

[Index(nameof(A), nameof(B), Name = "IndexOnAAndB", IsUnique = true)]
[Index(nameof(B), nameof(C), Name = "IndexOnBAndC", IsUnique = false)]
[Index(nameof(B), nameof(C), Name = "IndexOnBAndC", IsUnique = false, AllDescending = true)]
private class EntityWithTwoIndexes
{
public int Id { get; set; }
Expand Down

0 comments on commit e29487e

Please sign in to comment.