简体   繁体   中英

Exception on index data annotations from a base class

I'm starting with ASP.net Core and I'm creating my models.
I just have created two: one called dto which will be the base for all my database models, and look like this:

public abstract class Dto
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    [Clustered]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime LastUpdate { set; get; } 
}

the other one is called subscription and it look like this:

public class Subscription:Dto
{
    public string Name { get; set; } 
    public decimal Price { set; get; }
    public string MeliApiKey { get; set; }
    public int MaxQueries { get; set; }
    public int UsedQueries { get; set; }

}

I have the following configuration on the Fluent API :

modelBuilder.Entity<Dto.Dto>().Property(x => x.CreatedAt).HasDefaultValueSql("SYSUTCDATETIME()");
modelBuilder.Entity<Dto.Dto>().Property(x => x.LastUpdate).HasComputedColumnSql("SYSUTCDATETIME()");

foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
    foreach (var prop in entity.GetProperties())
    {
        var attr = prop.PropertyInfo?.GetCustomAttribute<ClusteredAttribute>(true);
        if (attr != null)
        {
            var index = entity.AddIndex(prop);
            index.IsUnique = true;
            index.SqlServer().IsClustered = true;
        }
    }
}

I have added only subscription as a dbset because I do not want Table Per Hierarchy inheritance with dto .

When I run add-migration, I get the following error:

"The index {'CreatedAt'} cannot be added to the entity type 'Subscription' because an index on the same properties already exists on entity type 'Dto'"

Now I have googled the error but I don't see anywhere how to solve this.

Should I remove the dto class and add the same properties to every object that will represent my model?

As the error states, and index is already defined on the property for the base class. Check that the property is declared in the same type of the entity that you're currently processing, something along the lines of:

var propertiesToProcess = entity.GetProperties()
    .Where( p => entity.ClrType == p.PropertyInfo.DeclaringType );

I ended up deleting these lines:

modelBuilder.Entity<Dto.Dto>().Property(x => x.CreatedAt).HasDefaultValueSql("SYSUTCDATETIME()");
modelBuilder.Entity<Dto.Dto>().Property(x => x.LastUpdate).HasComputedColumnSql("SYSUTCDATETIME()");

Because they for some reason included the Dto class in the Entity Framework Hierarchy, creating a Table per Hierarchy Inheritance (resulting in only one table database).

then i rewrited the code of the OnModelCreating like this:

foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            if (entity.ClrType.IsSubclassOf(typeof(Dto.Dto)))
            {

                modelBuilder.Entity(entity.ClrType)
                    .HasKey(nameof(Dto.Dto.Id)).ForSqlServerIsClustered(false);

                modelBuilder.Entity(entity.ClrType)
                    .Property(nameof(Dto.Dto.CreatedAt))
                    .HasDefaultValueSql("SYSUTCDATETIME()");

                modelBuilder.Entity(entity.ClrType)
                    .Property(nameof(Dto.Dto.LastUpdate))
                    .HasComputedColumnSql("SYSUTCDATETIME()");

                foreach (var prop in entity.GetProperties())
                {

                    var attr = prop.PropertyInfo?.GetCustomAttribute<ClusteredAttribute>(true);
                    if (attr != null)
                    {
                        var index = entity.AddIndex(prop);
                        index.IsUnique = true;
                        index.SqlServer().IsClustered = true;

                    }

                }
            }
        }

this if if (entity.ClrType.IsSubclassOf(typeof(Dto.Dto))) is to discard quickly the tables created by ASP.net Core, so the changes are made only for my objects that inherit from Dto.

This Code:

modelBuilder.Entity(entity.ClrType)
                .Property(nameof(Dto.Dto.CreatedAt))
                .HasDefaultValueSql("SYSUTCDATETIME()");

modelBuilder.Entity(entity.ClrType)
                .Property(nameof(Dto.Dto.LastUpdate))
                .HasComputedColumnSql("SYSUTCDATETIME()");

tells entity framework to create the column CreateAt with the current UTC time has default value and to update the LastUpdate column with the current UTC time for the actual entity in the for loop.

and Finally, this code got the error because the Dto class was being included in the Database Entities, so it create the index for it, and since including that class in the database entities triggers the TPH when the next entity was trying to create an index too, the error pops up (we re trying to create the same index in the same table because with TPH we only have one table).

foreach (var prop in entity.GetProperties())
        {

            var attr = prop.PropertyInfo?.GetCustomAttribute<ClusteredAttribute>(true);
            if (attr != null)
            {
                var index = entity.AddIndex(prop);
                index.IsUnique = true;
                index.SqlServer().IsClustered = true;
            }
        }

but now that we have separated tables for each entity it just works.

The Moho's answer helped a lot.

here other answers that helped me to achieve this:

How do I check if a type is a subtype OR the type of an object?

EF Core Add Migration Debugging

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM