Efcore: Index on a field of owned type

Created on 5 Dec 2019  ยท  6Comments  ยท  Source: dotnet/efcore

Is it possible to have a non clustered index on a field of a owned type? I have a value object that is being used in multiple entities and in one of them I want to create a index in one of the fields.

Mapped using the following implementation https://github.com/aspnet/EntityFrameworkCore/issues/15681#issuecomment-537789913

Value Object:

public class TaxesPercentage : ValueObject
{
    public int Value { get; private set; }
    public decimal Multiplier { get; private set; }

    //โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– 

    public TaxesPercentage(int value)
    {
        Value = value >= 0 && value <= 100 ? value : throw new ArgumentOutOfRangeException($"{nameof(Value)} must be between 0 and 100.");
        Multiplier = 1 + value / 100;
    }

    //โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– 

    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Value;
        yield return Multiplier;
    }

    //โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– โ– 

    public override string ToString() => $"{Value}";
}

Configuration:

public class PercentageConfiguration<TEntity> : IOwnedNavigationConfiguration<TEntity, TaxesPercentage>
    where TEntity : class
{
    public void Configure(OwnedNavigationBuilder<TEntity, TaxesPercentage> percentageConfiguration)
    {
        percentageConfiguration.Property(v => v.Value)
            .HasColumnName("Percentage")
            .IsRequired();

        percentageConfiguration.Property(v => v.Multiplier)
            .HasColumnType("decimal(3,2)")
            .HasColumnName(nameof(TaxesPercentage.Multiplier))
            .HasComputedColumnSql("(CONVERT([decimal](3,2),CONVERT([decimal](3,0),[Percentage])/(100.00)+(1))) PERSISTED");
    }
}

Main entity configuration

class IvaEntityConfiguration : AuditableEntityTypeConfiguration<Iva>
{
    public override void Configure(EntityTypeBuilder<Iva> ivaConfiguration)
    {
        //...

        ivaConfiguration.ApplyOwnsOneConfiguration(i => i.Percentage, new PercentageConfiguration<Iva>());

        //โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Indexes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

        ivaConfiguration.HasIndex(l => l.Percentage.Value)
            .IsClustered(false)
            .HasName("IX_Iva_Percentage")
            .IsUnique();

        //โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

        base.Configure(ivaConfiguration);
    }
}

Error:
The properties expression 'l => Convert(l.Percentage.Value, Object)' is not valid. The expression should represent a simple property access: 't => t.MyProperty'. When specifying multiple properties use an anonymous type: 't => new { t.MyProperty1, t.MyProperty2 }'. (Parameter 'propertyAccessExpression')

Stack trace
at Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.GetPropertyAccessList(LambdaExpression propertyAccessExpression) at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.HasIndex(Expression`1 indexExpression) at Catalog.Persistence.Configurations.Taxes.IvaEntityConfiguration.Configure(EntityTypeBuilder`1 ivaConfiguration) in C:\Users\AdrianoEscalante\source\repos\adminbeta\Catalog.Persistence\Configurations\Taxes\IvaEntityConfiguration.cs:line 30 at Microsoft.EntityFrameworkCore.ModelBuilder.ApplyConfiguration[TEntity](IEntityTypeConfiguration`1 configuration)
EF Core 3.1

closed-question customer-reported

All 6 comments

Since index is on the property of the owned entity, it should be defined on owned entity too.
Try this
C# percentageConfiguration.HasIndex(l => l.Value) .IsClustered(false) .HasName("IX_Iva_Percentage") .IsUnique();

The problem is this index is specific to this table, no other table that uses this owned type will have it.

In that case you need to get the reference to owned type again.
C# ivaConfiguration.OwnsOne(i => i.Percentage).HasIndex(l => l.Value) .IsClustered(false) .HasName("IX_Iva_Percentage") .IsUnique();

It gives the same error.

image

Probably the problem lies with the way the extension method works.

@abcdma - There are few points to remember here

  • PercentageConfiguration applies configuration of all Percentage owned entities. If you want to configure the index on specific owned entity only then either you need to create a another derived configuration and apply that where applicable or configure index in the configuration of owner (IvaEntityConfiguration in this case)
  • ApplyOwnsOneConfiguration returns owner's entity type builder. You need owned entity's builder to configure the index. Your last code snippet is no different than your first one hence it throws same exception.
  • I have posted a snippet which allows you to configure index in IvaEntityConfiguration. Please use the snippet I posted as is.

Thank you, was in a hurry and didn't even noticed that I forgot to change the extension method to the OwnsOne. It's working now.

Was this page helpful?
0 / 5 - 0 ratings