c# - 使用过滤器对象的 Linq where 子句

标签 c# entity-framework linq expression

我有一段 Linq 查询我的网络 Controller 中的 EntityFramework 上下文并返回结果,如下所示:

[HttpGet]
public IActionResult GetRoutingRules()
{
     var query = (from rr in _context.RoutingRules
          join dest in _context.RoutingZones on rr.DestinationZoneId equals dest.ZoneId
          join origin in _context.RoutingZones on rr.OriginZoneId equals origin.ZoneId
          join hub in _context.RoutingHub on rr.HubId equals hub.HubId
          select new RoutingRulesDto(rr) { DestinationZoneName = dest.ZoneName, OriginZoneName = origin.ZoneName, HubName = hub.HubName });

     return Ok(query);
}

我想要一个新的方法,它将采用一个“过滤器”对象,我可以在其中缩小上述结果的范围。我的过滤器对象如下所示:

public class RoutingSearchFilterDto
{
     public int BrandId { get; set; }
     public int? ServiceType { get; set; }
     public long? OriginZoneId { get; set; }
     public long? DestinationZoneId { get; set; }
     public int? RuleRanking { get; set; }
     public bool? IsRuleActive { get; set; }
}

该类最少需要设置的信息是BrandId。所有其他属性都是过滤器中的选项。

我需要编写一个新的 Controller 方法来利用它,例如:

[HttpPost("filtered")]
public IActionResult GetFilteredRoutingRules([FromBody] RoutingSearchFilterDto filter)
{
    ...
}

如何对可能为空的属性进行 linq 查询?本质上,动态查询取决于过滤器对象中设置的属性。

注意:我希望这会影响 EF 运行的 select 语句,而不仅仅是让 EF 获取所有数据,然后过滤数据集 - 这样做的目的是提高 db 调用的效率。

过滤器对象可能会发送到 BrandId = 1、IsRuleActive = 1 的位置。同样,它可能是 BrandId = 1、ServiceType = 3(因此 IsRuleActive 为空,因此不应出现在 linq where 子句中)。

我已经试过了:

var param = (Expression.Parameter(typeof(RoutingRules), "rr"));

Expression combinedExpr = null;
if (filter.BrandId != null)
{
    var exp = Expression.Equal(Expression.Property(param, "BrandId"), Expression.Constant(filter.BrandId));
    combinedExpr = exp;
}

if (filter.DestinationZoneId != null)
{
    var exp = Expression.Equal(Expression.Property(param, "DestinationZoneId"), Expression.Constant(filter.DestinationZoneId));
    combinedExpr = (combinedExpr == null ? exp : Expression.AndAlso(combinedExpr, exp));
}

if (filter.OriginZoneId != null)
{
    var exp = Expression.Equal(Expression.Property(param, "OriginZoneId"), Expression.Constant(filter.OriginZoneId));
    combinedExpr = (combinedExpr == null ? exp : Expression.AndAlso(combinedExpr, exp));
}

if (filter.EshopServiceType != null)
{
    var exp = Expression.Equal(Expression.Property(param, "EshopServiceType"), Expression.Constant(filter.EshopServiceType));
    combinedExpr = (combinedExpr == null ? exp : Expression.AndAlso(combinedExpr, exp));
}

if (filter.IsRuleActive != null)
{
    var exp = Expression.Equal(Expression.Property(param, "IsRuleActive"), Expression.Constant(filter.IsRuleActive, typeof(bool?)));
    combinedExpr = (combinedExpr == null ? exp : Expression.AndAlso(combinedExpr, exp));
}

if (filter.RuleRanking != null)
{
    var exp = Expression.Equal(Expression.Property(param, "RuleRanking"), Expression.Constant(filter.RuleRanking));
    combinedExpr = (combinedExpr == null ? exp : Expression.AndAlso(combinedExpr, exp));
}

if (combinedExpr == null)
    combinedExpr = Expression.Default(typeof(bool));

var compiled = Expression.Lambda<Func<RoutingRules, bool>>(combinedExpr, param).Compile();


var results = (from rr in _context.RoutingRules.Where(compiled)
                join dest in _context.RoutingZones on rr.DestinationZoneId equals dest.ZoneId
                join origin in _context.RoutingZones on rr.OriginZoneId equals origin.ZoneId
                join hub in _context.RoutingHub on rr.HubId equals hub.HubId
                where rr.BrandId == 21
                select new RoutingRulesDto(rr) { DestinationZoneName = dest.ZoneName, OriginZoneName = origin.ZoneName, HubName = hub.HubName });

但是Where子句并没有应用到生成的Sql中,好像是拉回所有记录,然后应用内存中的where,这不是我需要的。

在此先感谢您的指点!!

最佳答案

可以为此构建一个表达式树,但您是否考虑过:

IQueryable<...> query = ...;

if (routingSearchFilter.ServiceType != null)
  query = query.Where(e => e.ServiceType == routingSearchFilter.ServiceType);

if (...) 
   query = query.Where(....);

EF 引擎足够智能,可以组合 Where 子句(当然还有 AND)。

编辑:

不清楚您是想过滤连接结果还是只过滤第一个表。在那种情况下它会继续像

var result = (from rr in query
          join dest in _context.RoutingZones on rr.DestinationZoneId equals dest.ZoneId
          join ...
          select new RoutingRulesDto(rr) .... ).ToSometing();

但我对 RoutingRulesDto(rr) 构造函数参数有点担心。

关于c# - 使用过滤器对象的 Linq where 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46469348/

相关文章:

c# - 当 ReturnResult 是自定义对象时 Mock 返回 null 值,但当它是基本类型 bool 时按预期工作

c# - 如何使用emgu cv在图像中找到具有任意旋转角度的黑色方 block

c# - 在运行时使用 Serialize.Linq 编译表达式

c# - c++ native 代码 DLL 使用抽象类到包装器中以在 C# 模块中使用

c# - 我应该如何本地化静态 ComboBox 项目?

c# - 定义了重复的 'entityFramework' 部分 - EntityFramework6 升级

c# - 如何使我的 DAL 内部的 EF 代码优先类的某些属性?

c# - 使用常量参数在 Entity Framework 中映射 SQL 函数

c# - LINQ 多对多关系 - 获取分组对象的列表

c# - LINQ 按时间段聚合和分组