如何实现实体框架核心数据过滤器?

时间:2018-10-04 18:17:01

标签: c# asp.net-core filter entity-framework-core aspnetboilerplate

在我的ASP.NET Zero应用程序中,我想通过CompanyId值设置数据过滤器。 因此,我阅读了here文档和此支持论坛thread上提供的信息。

在我的EF Core项目中,进行了以下更改。

public override void PreInitialize()
{
    if (!SkipDbContextRegistration)
    {
        // ...

        Configuration.UnitOfWork.RegisterFilter("CompanyFilter", false);
    }
}

然后,我遵循用于IMustHaveTenant数据过滤器的模式,并将以下更改应用于我的数据库上下文类:

protected int? CurrentCompanyId = null;
protected bool IsCompanyFilterEnabled => CurrentCompanyId != null && CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled("CompanyFilter") == true;    

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>();
}

然后,在我希望应用过滤器的应用服务方法中,添加了以下代码。

public async Task<PagedResultDto<EmployeeListDto>> GetEmployees(GetEmployeeInput input)
{
    using (CurrentUnitOfWork.EnableFilter("CompanyFilter"))
    {
        using (CurrentUnitOfWork.SetFilterParameter("CompanyFilter", "CompanyId", GetCurrentUserCompany()))
        {
            // ...
        }
    }
}

在数据库上下文类中,如果我在下面的行上硬代码一个CompanyId值,则过滤器可以正常工作

protected int? CurrentCompanyId = 123;

老实说,我不完全理解数据库上下文类的CreateFilterExpression方法中使用的代码。我直接从ABP GitHub存储库代码中借用了IMustHaveTenant过滤器。

我已经修改了ABP用户“我的设置”模式,使其包含CompanyId。这样可以将每个用户分配到公司,因此应用程序应按公司限制该用户的所有数据。

在我的应用程序服务基类中,我有一个名为GetCurrentUserCompany的方法。此方法获取当前用户的默认公司。这是公司的数据过滤器上应该使用的值。

问题:

  • 我需要在数据库上下文类中设置此公司值吗?
  • 如果是,那么如何调用应用程序服务或存储库方法来获取此数据库上下文类中的CompanyId

更新: 我添加了亚伦建议的代码,它仍然无法正常工作。我正在与分配了公司1的用户ID进行测试。然而,当数据加载时,它仍然显示公司1和2。

App service showing filter (line 71) has been set.

Debug cmd window showing company filter is set.

第77行执行并在存储库上执行GetAll调用时,它仍然向用户显示所有公司。

10月6日更新: 新代码有效,但仅在用户分配了公司ID时有效。如果没有为用户分配公司,则抛出下图中所示的错误。 DB context error

1 个答案:

答案 0 :(得分:1)

  
      
  • 我是否需要在Db上下文类中设置该公司的价值?
  •   
  • 如果是,那么如何调用应用程序服务或存储库方法来获取此数据库上下文类中的公司ID?
  •   

不。在您的DbContext类中实现吸气剂:

// using Abp.Collections.Extensions;

// protected int? CurrentCompanyId = null;
protected int? CurrentCompanyId => GetCurrentCompanyIdOrNull();

protected virtual int? GetCurrentCompanyIdOrNull()
{
    if (CurrentUnitOfWorkProvider != null &&
        CurrentUnitOfWorkProvider.Current != null)
    {
        return CurrentUnitOfWorkProvider.Current
            .Filters.FirstOrDefault(f => f.FilterName == "CompanyFilter")?
            .FilterParameters.GetOrDefault("CompanyId") as int?;
    }

    return null;
}

通过您的expression方法返回CreateFilterExpression

protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{
    // Expression<Func<TEntity, bool>> expression = null;
    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);
    }

    // return base.CreateFilterExpression<TEntity>();
    return expression;
}

一个类似的问题:Implement OrganizationUnit filter in ASP.NET Core