Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index on a field of owned type #19180

Closed
AdrianoAE-Surface opened this issue Dec 5, 2019 · 8 comments
Closed

Index on a field of owned type #19180

AdrianoAE-Surface opened this issue Dec 5, 2019 · 8 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@AdrianoAE-Surface
Copy link

AdrianoAE-Surface commented Dec 5, 2019

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 #15681 (comment)

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

@smitpatel
Copy link
Contributor

Since index is on the property of the owned entity, it should be defined on owned entity too.
Try this

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

@AdrianoAE-Surface
Copy link
Author

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

@smitpatel
Copy link
Contributor

In that case you need to get the reference to owned type again.

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

@AdrianoAE-Surface
Copy link
Author

It gives the same error.

image

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

@smitpatel
Copy link
Contributor

@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.

@AdrianoAE-Surface
Copy link
Author

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.

@smitpatel smitpatel added closed-no-further-action The issue is closed and no further action is planned. and removed type-bug labels Dec 6, 2019
@vindberg
Copy link

vindberg commented May 3, 2022

@smitpatel sorry to bring this up again, is it possible to do an index with a ivaConfiguration and Percentage property (from each)?

Example with "Image" as owned object in a Person:

            modelBuilder.Entity<PersonImage>()
                .HasIndex(p => new { p.PersonId, p.Image.UrlName })
                .IncludeProperties(p => p.Position)
                .IsUnique(true);

Gives this error:

The expression 'p => new <>f__AnonymousType71`2(PersonId = p.PersonId, UrlName = p.Image.UrlName)' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't => new { t.MyProperty, t.MyField }'. (Parameter 'memberAccessExpression')

Or is this not supported yet? Have you seen any workarounds?

Thanks.

@smitpatel
Copy link
Contributor

See #11336

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

4 participants