如何为 Nullable<Int32> 等可空属性创建动态 C# 表达式 func<tentity, bool>

How to make dynamic C# expression func<tentity, bool> for nullable properties like Nullable<Int32>

我在生成用于 EF Core 查询的动态函数时遇到问题。

这是函数生成器

public static Expression<Func<TEntity, bool>> WhereFunc<TEntity>(this string propertyName, 
object? propertyValue)
        where TEntity : class, IEntity
{
    var info = typeof(TEntity).GetProperty(propertyName);
    var parameter = Expression.Parameter(typeof(TEntity), $"{nameof(TEntity).Substring(0, 
2)}");
    var property = Expression.Property(parameter, propertyName);
    var value = Expression.Constant(propertyValue);
    var clause = Expression.Equal(property, Expression.Constant(propertyValue));
    return Expression.Lambda<Func<TEntity, bool>>(clause, parameter);
}

这是所有需要的属性的动态查询生成器

public static IQueryable<TEntity> SetWhere<TEntity, TSearch>(this IEnumerable<TEntity> 
 entities, IEnumerable<PropertyInfo> fields, TSearch entity)
        where TEntity : class, IEntity
        where TSearch : class, ISearchEntity
{
     var query = entities.AsQueryable();

     if (fields != null && fields.Any())
         foreach (var item in fields)
             query = query.Where(EntityFuncs.WhereFunc<TEntity>(item.Name, item.GetValue(entity)));

     return query;
}

这是我使用它的代码:

var query = Entities
              .Skip(filter.Total)
              .Take(filter.More)
              .AsQueryable();
query = query.SetWhere<TEntity, TSearchEntity>(properties, filter.Entity);

对于字符串属性,它可以正常工作。

但在 nullable<int> 属性的示例中,我收到此错误:

The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int32]' and 'System.Int32

您必须显式测试 propertyValue 的类型或 属性 的类型,并相应地调用 Nullable.Equals

Nullable.GetUnderlyingType 如果给定类型是 Nullable<> 构造的类型(例如 int?);否则将 return null.

public static Expression<Func<TEntity, bool>> WhereFunc<TEntity>(this string propertyName, object? propertyValue)
    where TEntity : class, IEntity
{
    var info = typeof(TEntity).GetProperty(propertyName);
    var parameter = Expression.Parameter(typeof(TEntity), $"{nameof(TEntity).Substring(0, 2)}");
    var property = Expression.Property(parameter, propertyName);
    var value = Expression.Constant(propertyValue);

    var propertyType = info?.PropertyType ?? propertyValue?.GetType();
    var underlyingType = propertyType is not null ? Nullable.GetUnderlyingType(propertyType) : null;

    Expression clause;
    if (underlyingType is not null)
    {
        var methodOpen = typeof(Nullable).GetMethod(nameof(Nullable.Equals), BindingFlags.Static | BindingFlags.Public);
        var method = methodOpen.MakeGenericMethod(underlyingType);
        clause = Expression.Call(method, property, Expression.Convert(value, propertyType));
    }
    else
    {
        clause = Expression.Equal(property, value);
    }

    return Expression.Lambda<Func<TEntity, bool>>(clause, parameter);
}