简体   繁体   English

如何在某些条件下使用 LINQ 中的多个 where 键

[英]How can I use multiple where on keys in LINQ with certain condition

I am working on a query where I want to retrieve data including multiple int?我正在处理一个要检索包括多个 int 的数据的查询? keys.键。 My output Dto:我的 output Dto:

 public class FilterParamsDto {
    public int? StudentId { get; set; }
    public int? NationalityId { get; set; }
    public int? CountryId { get; set; }
    public int? SchoolCountryId { get; set; }
    public int? SchoolStateId { get; set; }
    public int? SchoolCityId { get; set; }

    .... More keys
}

I used following queries我使用了以下查询

var value = from y in data
            where dto.CountryId == null
                ? y.CountryId != null
                : y.IntakeId == dto.CountryId && dto.StudentId == null
                    ? y.StudentId != null
                    : y.StudentId == dto.StudentId && dto.SchoolCityId == null
                        ? y.SchoolCityId != null
                        : y.SchoolCityId == dto.SchoolCityId
            select y;

What I want to Achieve:我想要实现的目标:

I want to make a method where if any property have some value I want to filter data based on that particular property and if there is not any value I want to filter data based on another properties who do have some value.我想创建一个方法,如果任何属性具有某些值,我想根据该特定属性过滤数据,如果没有任何值,我想根据另一个具有某些值的属性过滤数据。 if any property have 0 value I want to skip filter because if any property have 0 value so the data wont' match and i am not going to receive any data using ||如果任何属性的值为 0,我想跳过过滤器,因为如果任何属性的值为 0,那么数据将不会匹配,并且我不会使用 || 接收任何数据the data is not getting filtered as per required condition.数据未根据所需条件进行过滤。

EDIT 1 There are three possibilities either all properties have some values, some properties caring values, all the properties caring values.编辑 1 有三种可能性,要么所有属性都有一些值,一些属性关心值,所有属性都有关心值。 the required logic should be like if first where executed then another where should be executed on updated values and so on...所需的逻辑应该是如果首先在哪里执行,然后另一个应该在更新的值上执行,依此类推......

To give you just one example, do it like this举一个例子,这样做

.Where(y => (dto.CountryId == null || y.CountryId == dto.CountryId))

Add as many conditions as you want.根据需要添加任意数量的条件。

Well, if you have many properties like the above and have multiple versions of this kind of filter operation I would suggest building a dynamic expression to filter your collections.好吧,如果你有很多像上面这样的属性并且有多个版本的这种过滤操作,我建议构建一个动态表达式来过滤你的 collections。 This approach has one assumption: the entity that is queried and the dto has the same property names and similar types.这种方法有一个假设:被查询的实体和 dto 具有相同的属性名称和相似的类型。

 public static void Main()
    {
        var dto = new FilterParamsDto { CountryId = 3, StudentId = 5 };
        var data = new List<Dummy> { new() { CountryId = 3, StudentId = 1 }, new() { CountryId = 4, StudentId = 5 }, new() { CountryId = 1, StudentId = 2 }, new() { CountryId = 3, StudentId = 5 } };
        var expAnd = GenerateFilterExpression<Dummy, FilterParamsDto>(dto);
        var expOr = GenerateFilterExpression<Dummy, FilterParamsDto>(dto, false);

        var filteredWithAnd = data.AsQueryable().Where(expAnd).ToArray();
        var filteredWithOr = data.AsQueryable().Where(expOr).ToArray();
    }

    public static PropertyInfo[] GetNonNullProperties<T>(T item) where T : class
    {
        var properties = typeof(T)
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(r => r.GetValue(item) != null)
            .Select(r => r).ToArray();

        return properties;
    }

    public static Expression<Func<T, bool>> GenerateFilterExpression<T, R>(R dto, bool and = true) where T : class where R : class
    {
        var p = Expression.Parameter(typeof(T), "p");
        var nonnullProps = GetNonNullProperties(dto);
        var constExp = Expression.Constant(dto);

        // here we decide how to join conditions
        Func<Expression, Expression, BinaryExpression> operatorExp = and ? Expression.AndAlso : Expression.OrElse;
        Expression? exp = null;

        var sourceType = typeof(T);

        foreach (var item in nonnullProps)
        {
            var sourceProp = sourceType.GetProperty(item.Name);
            var prop = Expression.Property(p, sourceProp!);
            Expression dtoProp = Expression.Property(constExp, item);

            // we need this trick otherwise we will have runtime error that says you can not have an expression like : int? == int
            if (sourceProp!.PropertyType != item.PropertyType)
            {
                var underlyingType = Nullable.GetUnderlyingType(item.PropertyType);
                dtoProp = Expression.Convert(dtoProp, underlyingType!);
            }

            if (exp == null)
            {
                exp = Expression.Equal(prop, dtoProp);
            }
            else
            {
                exp = operatorExp(exp, Expression.Equal(prop, dtoProp));
            }
        }

        var result = Expression.Lambda<Func<T, bool>>(exp!, p);

        return result;
    }

Fiddle小提琴

To filter by the first available filter property, you can write (I replaced dto by filter and y by d to make it clearer):要按第一个可用的过滤器属性进行过滤,您可以编写(我将dto替换为filter并将y替换为d以使其更清晰):

var value = from d in data
   where
      filter.CountryId != null && (d.CountryId ?? d.IntakeId) == filter.CountryId ||
      filter.StudentId != null && d.StudentId == filter.StudentId ||
      filter.SchoolCityId != null && d.SchoolCityId == filter.SchoolCityId
   select d;

To filter by all available filters:所有可用过滤器过滤:

var value = from d in data
   where
     (filter.CountryId == null || (d.CountryId ?? d.IntakeId) == filter.CountryId) &&
     (filter.StudentId == null || d.StudentId == filter.StudentId) &&
     (filter.SchoolCityId == null || d.SchoolCityId == filter.SchoolCityId)
   select d;

The test (d.CountryId?? d.IntakeId) == filter.CountryId compares d.CountryId if it is not null and otherwise d.IntakeId with filter.CountryId .测试(d.CountryId?? d.IntakeId) == filter.CountryId比较d.CountryId如果不是 null ,否则d.IntakeIdfilter.CountryId

According to the documentation of ??根据??的文件and??= operators (C# reference) : and??= 运算符(C# 参考)

The null-coalescing operator??空合并运算符?? returns the value of its left-hand operand if it isn't null;如果不是 null,则返回其左侧操作数的值; otherwise, it evaluates the right-hand operand and returns its result.否则,它计算右手操作数并返回其结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM