Base solution for your next web application
Ends in:
01 DAYS
01 HRS
01 MIN
01 SEC
Open Closed

ANZ V6.6.1 Data filters issue #6473


User avatar
0
exlnt created

I had posted this question on Stackoverflow few months ago.. The accepted answer on that question was working for me and my app in ANZ Version 6.3.1. Yesterday I updated my entire ANZ solution to V6.6.1. That code is not working now.

On the documentation for data filters it shows the code example below.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Filter("PersonFilter", (IHasPerson entity, int personId) => entity.PersonId == personId, 0);
}

Do I need to use the code shown in the documentation? And does it apply to EF Core?


15 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    https://aspnetboilerplate.com/Pages/Documents/Articles\How-To\add-custom-data-filter-ef-core You can refer to this article to see if it can't be solved, please share some code that can reproduce the problem.

  • User Avatar
    0
    exlnt created

    DB Context code:

    protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
    {
        if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
        {
            return true;
        }
        return false;
    }
    
    protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
    {
        Expression<Func<TEntity, bool>> expression = null;
    
        if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
        {
            Expression<Func<TEntity, bool>> companyFilter = e => ((IHasCompany)e).CompanyId == CurrentCompanyId || (((IHasCompany)e).CompanyId == CurrentCompanyId) == IsCompanyFilterEnabled;
            expression = expression == null ? companyFilter : CombineExpressions(expression, companyFilter);
        }
        return base.CreateFilterExpression<TEntity>();
    }
    

    App service method, where filter is enabled.

    public async Task<PagedResultDto<GetHomeForView>> GetAll(GetAllHomesInput input)
    {
    using (CurrentUnitOfWork.EnableFilter(AppConsts.CompanyFilter))
    {
        using (CurrentUnitOfWork.SetFilterParameter(AppConsts.CompanyFilter, AppConsts.CompanyFilterParameter_CompanyId, GetCurrentUserCompany()))
        {
            //code removed
        }
    }
    }
    

    When I run my code in debug (see image below), I can see the filter being applied in the using statement and there is a value as well. Yet once it goes inside the method code block and query executes, its not filtering the data. All rows in the table are being returned. All this code works perfectly in V6.3.1 solution.

  • User Avatar
    0
    maliming created
    Support Team

    I am using ASP.NET CORE MVC & jQuery .NET Core 2.2 v6.6.1 to test the filter and everything works fine.

    Can you reproduce your problem using the 6.6.1 Demo project? If you can, please send the Demo project to: [email protected]

  • User Avatar
    0
    exlnt created

    It's going to take some time and effort for me to recreate in the demo project. I dont have time to do that right now.

    Today, I went back to my V631 solution and retested my data filters there and it seems to be broken there too. Its working on one app service and MVC view but not on another. I dont know how this broke, I had tested all of my pages and services after I applied the changes. Now I'm more confused as to what the issue could be!

    • Can you confirm that my DB context code and my app service code shown in previous post is valid?
    • Can you confirm if the changes to the UserClaimsPrincipalFactory class required to make the filtering work?
      • Today I even added the changes to UserClaimsPrincipalFactory but its still not working!
  • User Avatar
    0
    exlnt created

    I did some more testing/debugging.

    I applied the code to the UserClaimsPrincipalFactory with two claims.

    public override async Task<ClaimsPrincipal> CreateAsync(User user)
    {
        var claim = await base.CreateAsync(user);
        claim.Identities.First().AddClaim(new Claim("CompanyFilter", user.CompanyId.HasValue ?  user.CompanyId.Value.ToString() : ""));
        claim.Identities.First().AddClaim(new Claim("NurseHomeFilter", user.NurseHomeId.HasValue ? user.NurseHomeId.Value.ToString() : ""));
        return claim;
    }
    

    In my DB Context, I have added these two methods. In debug, I dont see these methods execute.

    protected virtual int? GetCurrentUsersCompanyIdOrNull()
    {
        var userOuClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "CompanyFilter");
        if (string.IsNullOrEmpty(userOuClaim?.Value))
        {
            return null;
        }
    
        return Convert.ToInt32(userOuClaim.Value);
    }
    
    protected virtual int? GetCurrentUsersNurseHomeIdOrNull()
    {
        var userOuClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "NurseHomeFilter");
        if (string.IsNullOrEmpty(userOuClaim?.Value))
        {
            return null;
        }
    
        return Convert.ToInt32(userOuClaim.Value);
    }
    

    In debug, i then checked the values of the filters when the "GetAll" repository method is called. They both have nothing?

    > ? CurrentUnitOfWork.Filters
    Count = 5
        [0]: {Abp.Domain.Uow.DataFilterConfiguration}
        [1]: {Abp.Domain.Uow.DataFilterConfiguration}
        [2]: {Abp.Domain.Uow.DataFilterConfiguration}
        [3]: {Abp.Domain.Uow.DataFilterConfiguration}
        [4]: {Abp.Domain.Uow.DataFilterConfiguration}
    > ? CurrentUnitOfWork.Filters[3]
    {Abp.Domain.Uow.DataFilterConfiguration}
        FilterName: "CompanyFilter"
        FilterParameters: Count = 0
        IsEnabled: true
    > ? CurrentUnitOfWork.Filters[4]
    {Abp.Domain.Uow.DataFilterConfiguration}
        FilterName: "NurseHomeFilter"
        FilterParameters: Count = 0
        IsEnabled: true
    
  • User Avatar
    0
    aaron created
    Support Team

    Can you confirm if the changes to the UserClaimsPrincipalFactory class required to make the filtering work?

    What gave you that impression?

  • User Avatar
    0
    exlnt created

    @aaron - The example document cited by @maliming in his first reply shows that code. I was NOT using that in version 6.3.1. That's what gave me that impression. Plus at this point I am just looking for help/direction to solve my issue. Is that too much to ask?

  • User Avatar
    0
    aaron created
    Support Team

    That shows how to store a value in claims. The value is used in GetCurrentUsersOuIdOrNull.

    It is not required for data filters to work if you get the value from elsewhere (e.g. DB).

  • User Avatar
    0
    exlnt created

    I have just updated my solution V6.8 now and this data filtering is still not working for me.

    Here on the Index view app service method, at the very first using statement, you can see the immediate window shows that both filters are enabled.

    Here are the methods, which are in my base app service class, that get the current user company and home values. In this test, you can see both values are populated.

    Yet when the code in the method executes, the query still returns all rows in the result?

  • User Avatar
    0
    aaron created
    Support Team

    You are discarding the filter expression, do these:

    protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
    {
        // Expression<Func<TEntity, bool>> expression = null;
        var expression = base.CreateFilterExpression<TEntity>(); // Change to this
    
        if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
        {
            Expression<Func<TEntity, bool>> companyFilter = e => ((IHasCompany)e).CompanyId == CurrentCompanyId || (((IHasCompany)e).CompanyId == CurrentCompanyId) == IsCompanyFilterEnabled;
            expression = expression == null ? companyFilter : CombineExpressions(expression, companyFilter);
        }
        // return base.CreateFilterExpression<TEntity>();
        return expression; // Change to this
    }
    
  • User Avatar
    0
    exlnt created

    @aaron - Thanks! I noticed those differences just after I posted my last message. I applied those changes, but the filtering is still not working.

            protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
            {
                if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
                {
                    return true;
                }
                if (typeof(IHasNurseHome).IsAssignableFrom(typeof(TEntity)))
                {
                    return true;
                }
                return base.ShouldFilterEntity<TEntity>(entityType);
            }
    
            protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
            {
                var expression = base.CreateFilterExpression<TEntity>();
    
                if (typeof(IHasCompany).IsAssignableFrom(typeof(TEntity)))
                {
                    Expression<Func<TEntity, bool>> companyFilter = e => ((IHasCompany)e).CompanyId == CurrentCompanyId || (((IHasCompany)e).CompanyId == CurrentCompanyId) == IsCompanyFilterEnabled;
                    expression = expression == null ? companyFilter : CombineExpressions(expression, companyFilter);
                }
    
                if (typeof(IHasNurseHome).IsAssignableFrom(typeof(TEntity)))
                {
                    Expression<Func<TEntity, bool>> nurseHomeFilter = e => ((IHasNurseHome)e).NurseHomeId == CurrentNurseHomeId || (((IHasNurseHome)e).NurseHomeId == CurrentNurseHomeId) == IsCompanyFilterEnabled;
                    expression = expression == null ? nurseHomeFilter : CombineExpressions(expression, nurseHomeFilter);
                }
    
                return expression;
            }
    

    Here in debug you can see that both filter are enabled and have values.

  • User Avatar
    0
    aaron created
    Support Team

    Show code for CurrentCompanyId.

  • User Avatar
    0
    exlnt created

    Here you go.

    protected int? CurrentCompanyId = null;
    protected int? CurrentNurseHomeId = null;
    protected bool IsCompanyFilterEnabled => CurrentCompanyId != null && CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled("CompanyFilter") == true;
    protected bool IsNurseHomeFilterEnabled => CurrentNurseHomeId != null && CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled("NurseHomeFilter") == true;
    
  • User Avatar
    1
    aaron created
    Support Team

    CurrentCompanyId and CurrentNurseHomeId should be getter properties instead of null fields.

    protected int? CurrentCompanyId => GetCurrentCompanyIdOrNull();
    protected int? CurrentNurseHomeId => GetCurrentNurseHomeIdOrNull();
    

    See AbpDbContext.cs#L77.

  • User Avatar
    0
    exlnt created

    @aaron - Thanks! Your last answer along with the original article helped solve my issue!