【问题标题】:The filter expression cannot be specified for entity type. A filter may only be applied to the root entity type in a hierarchy不能为实体类型指定过滤器表达式。过滤器只能应用于层次结构中的根实体类型
【发布时间】:2020-03-22 00:00:24
【问题描述】:

我在添加新迁移时遇到此错误。 无法为实体类型“Babysitter”指定过滤表达式“e => Not(e.IsDeleted)”。过滤器只能应用于层次结构中的根实体类型。

我正在做的是我有 2 个类 Babysitter 和 Parent 都需要是 ApplicationUsers,因为它们具有不同的属性。所以我让它们继承ApplicationUser类并扩展它们。

这是 ApplicationUser 类。

public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
    public ApplicationUser()
    {
        this.Id = Guid.NewGuid().ToString();
        this.Roles = new HashSet<IdentityUserRole<string>>();
        this.Claims = new HashSet<IdentityUserClaim<string>>();
        this.Logins = new HashSet<IdentityUserLogin<string>>();
    }

    // Audit info
    public DateTime CreatedOn { get; set; }

    public DateTime? ModifiedOn { get; set; }

    // Deletable entity
    public bool IsDeleted { get; set; }

    public DateTime? DeletedOn { get; set; }

    public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }

    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }

    public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
}

这些是 Babysitter 和 Parent 类。

public class Babysitter : ApplicationUser
{
    public Babysitter()
    {
        this.Appointments = new HashSet<Appointment>();
        this.Comments = new HashSet<Comment>();
    }

    public string Name { get; set; }

    public int Age { get; set; }

    public Gender Gender { get; set; }

    public DateTime DateOfBirth { get; set; }

    public string ImageUrl { get; set; }

    public string Description { get; set; }

    public decimal WageRate { get; set; }

    public string Address { get; set; }

    public decimal Rating { get; set; }

    public ICollection<Comment> Comments { get; set; }

    public ICollection<Appointment> Appointments { get; set; }
}



public class Parent : ApplicationUser
{
    public Parent()
    {
        this.Comments = new HashSet<Comment>();
        this.Kids = new HashSet<Kid>();
        this.Appointments = new HashSet<Appointment>();
    }

    public string Name { get; set; }

    public string ImageUrl { get; set; }

    public decimal Rating { get; set; }

    public string Address { get; set; }

    public ICollection<Comment> Comments { get; set; }

    public ICollection<Kid> Kids { get; set; }

    public ICollection<Appointment> Appointments { get; set; }
}

因此,当我尝试添加迁移初始时,我收到此错误:无法为实体类型“Babysitter”指定过滤器表达式“e => Not(e.IsDeleted)”。过滤器只能应用于层次结构中的根实体类型。

这是 ApplicationDbContext.cs

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
    private static readonly MethodInfo SetIsDeletedQueryFilterMethod =
        typeof(ApplicationDbContext).GetMethod(
            nameof(SetIsDeletedQueryFilter),
            BindingFlags.NonPublic | BindingFlags.Static);

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<Babysitter> Babysitters{ get; set; }

    public DbSet<Parent> Parents { get; set; }

    public DbSet<Comment> Comments { get; set; }

    public DbSet<Kid> Kids{ get; set; }

    public DbSet<Appointment> Appointments { get; set; }

    public DbSet<Setting> Settings { get; set; }

    public override int SaveChanges() => this.SaveChanges(true);

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        this.ApplyAuditInfoRules();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) =>
        this.SaveChangesAsync(true, cancellationToken);

    public override Task<int> SaveChangesAsync(
        bool acceptAllChangesOnSuccess,
        CancellationToken cancellationToken = default)
    {
        this.ApplyAuditInfoRules();
        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Needed for Identity models configuration
        base.OnModelCreating(builder);

        ConfigureUserIdentityRelations(builder);

        EntityIndexesConfiguration.Configure(builder);

        var entityTypes = builder.Model.GetEntityTypes().ToList();

        // Set global query filter for not deleted entities only
        var deletableEntityTypes = entityTypes
            .Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
        foreach (var deletableEntityType in deletableEntityTypes)
        {
            var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType);
            method.Invoke(null, new object[] { builder });
        }

        // Disable cascade delete
        var foreignKeys = entityTypes
            .SelectMany(e => e.GetForeignKeys().Where(f => f.DeleteBehavior == DeleteBehavior.Cascade));
        foreach (var foreignKey in foreignKeys)
        {
            foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
        }
    }

    private static void ConfigureUserIdentityRelations(ModelBuilder builder)
    {
        builder.Entity<ApplicationUser>()
            .HasMany(e => e.Claims)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<ApplicationUser>()
            .HasMany(e => e.Logins)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);

        builder.Entity<ApplicationUser>()
            .HasMany(e => e.Roles)
            .WithOne()
            .HasForeignKey(e => e.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);
    }

    private static void SetIsDeletedQueryFilter<T>(ModelBuilder builder)
        where T : class, IDeletableEntity
    {
        builder.Entity<T>().HasQueryFilter(e => !e.IsDeleted);
    }

    private void ApplyAuditInfoRules()
    {
        var changedEntries = this.ChangeTracker
            .Entries()
            .Where(e =>
                e.Entity is IAuditInfo &&
                (e.State == EntityState.Added || e.State == EntityState.Modified));

        foreach (var entry in changedEntries)
        {
            var entity = (IAuditInfo)entry.Entity;
            if (entry.State == EntityState.Added && entity.CreatedOn == default)
            {
                entity.CreatedOn = DateTime.UtcNow;
            }
            else
            {
                entity.ModifiedOn = DateTime.UtcNow;
            }
        }
    }
}

【问题讨论】:

    标签: c# database authentication asp.net-core entity-framework-core


    【解决方案1】:

    所以您尝试按惯例添加过滤器;

            // Set global query filter for not deleted entities only
            var deletableEntityTypes = entityTypes
                .Where(et => et.ClrType != null && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType));
            foreach (var deletableEntityType in deletableEntityTypes)
            {
                var method = SetIsDeletedQueryFilterMethod.MakeGenericMethod(deletableEntityType.ClrType);
                method.Invoke(null, new object[] { builder });
            }
    

    但这与所有三种类型都匹配; BabysitterParentApplicationUser。错误消息告诉您,在表层次结构中,仅将过滤器应用于基本类型;

        .Where(et => et.ClrType != null 
            && typeof(IDeletableEntity).IsAssignableFrom(et.ClrType)
            && et.BaseType == null)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-05
      • 2015-01-03
      • 1970-01-01
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2013-08-22
      相关资源
      最近更新 更多