我有一块Linq在我的Web控制器中查询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。所有其他属性都是过滤器中的选项。
我需要编写一个新的控制器方法来使用它,例如:
[HttpPost("filtered")]
public IActionResult GetFilteredRoutingRules([FromBody] RoutingSearchFilterDto filter)
{
...
}
如何对可能为null的属性进行linq查询?本质上,动态查询取决于过滤器对象中设置的属性。
注意:我希望这会影响EF运行的select语句,而不仅仅是让EF获取所有数据,然后过滤数据集 - 这一点是为了使db调用更有效。
过滤器对象可能会在BrandId = 1,IsRuleActive = 1的情况下发送。同样,它可能是BrandId = 1,ServiceType = 3(因此IsRuleActive为null所以不应该在linq where子句中)。< / p>
我试过这个:
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,它似乎会撤回所有记录,然后应用内存中的位置,这不是我需要的。
提前感谢任何指示!!
答案 0 :(得分:4)
您可以为此构建表达式树,但您是否考虑过:
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)构造函数参数有点警惕。
答案 1 :(得分:2)
如果您使用流畅的API for LINQ,您可以有条件地添加Where
子句。
var query = _content.RoutingRules.Where(r => r.BrandId == filter.BrandId);
if (filter.OriginZoneId != null) {
query = query.Where(r => r.OriginZoneId == filter.OriginZoneId);
}
if (filter.EshopServiceType != null) {
query = query.Where(r => r.EshopServiceType == filter.EshopServiceType);
}
// etc...
var result = query.ToArray();
答案 2 :(得分:1)
我的最终解决方案是黑色和白色,这就是我最终的结果:
[HttpPost("filtered")]
public IActionResult GetFilteredRoutingRules([FromBody] RoutingSearchFilterDto filter)
{
// Query to be build on the routing rules table.
IQueryable<RoutingRules> query = _context.RoutingRules;
// Populate the linked foreign key entities.
query.Include(x => x.Hub).Include(y => y.DestinationZone).Include(z => z.OriginZone);
// Build dynamic where statements.
if (filter.BrandId != null)
query = query.Where(r => r.BrandId == filter.BrandId);
if (filter.OriginZoneId != null)
query = query.Where(r => r.OriginZoneId == filter.OriginZoneId);
if (filter.DestinationZoneId != null)
query = query.Where(r => r.DestinationZoneId == filter.DestinationZoneId);
if (filter.IsRuleActive != null)
query = query.Where(r => r.IsRuleActive == filter.IsRuleActive);
if (filter.RuleRanking != null)
query = query.Where(r => r.RuleRanking == filter.RuleRanking);
// If you want to add paging:
query = query.Skip(filter.PageSize * filter.PageNumber).Take(filter.PageSize);
// Perform select on the table and map the results.
var result = query.Select(r => new RoutingRulesDto
{
RoutingRuleId = r.RoutingRuleId,
BrandId = r.BrandId,
LastMileCarrierCode = r.LastMileCarrierCode,
CashOnDelivery = r.CashOnDelivery,
CreationTime = r.CreationTime,
CurrencyCode = r.CurrencyCode,
CurrencyDescription = Enum.Parse(typeof(Enumerations.CurrencyCode), r.CurrencyCode),
DestinationZoneId = r.DestinationZoneId,
EddFromDay = r.EddFromDay,
EddToDay = r.EddToDay,
ServiceType = r.ServiceType,
ServiceTypeName = Enum.Parse(typeof(Enumerations.ServiceType), r.EshopServiceType),
IsPickUpAvailable = r.IsPickUpAvailable,
LastUpdateTime = r.LastUpdateTime,
LastUpdateUser = r.LastUpdateUser,
OriginZoneId = r.OriginZoneId,
RuleRanking = r.RuleRanking,
SignOnDelivery = r.SignOnDelivery,
TermsOfDelivery = r.TermsOfDelivery,
TermsOfDeliveryName = Enum.Parse(typeof(Enumerations.TermsOfDelivery), r.TermsOfDelivery),
ValueOfGoods = r.ValueOfGoods,
WeightLowerLimit = r.WeightLowerLimit,
WeightUpperLimit = r.WeightUpperLimit,
FirstMileCarrierCode = r.FirstMileCarrierCode,
HubId = r.HubId,
IsInsuranceAvailable = r.IsInsuranceAvailable,
IsRuleActive = r.IsRuleActive,
HubName = r.Hub.HubName,
DestinationZoneName = r.DestinationZone.ZoneName,
OriginZoneName = r.OriginZone.ZoneName,
});
// The SQL produced includes the joins and where clauses as well as only
// selecting the column names that are required in the flattened return object.
return Ok(result);
}
感谢帮助人员!