如何生成从搜索页面模型查询所有非空属性的 LINQ 查询
How to generate a LINQ query that queries all non-null properties from a search page model
我有两个模型:TestRecord
和 Part
其中 TestRecord
包含对 Part
的引用:
TestRecord
----
public string TestRecordId
public int PartId <-- foreign key to parts table
public string Name
public string TestType
public virtual Part Part
Part
----
int PartId
string Name
string Description
public virtual ICollection<TestRecord> TestRecords
我有一个搜索页面,显示每个 属性 测试记录的输入,包括其相关属性,例如:
@model TestRecord
<!-- From TestRecord -->
<input asp-for="TestType" type="text" />
<!-- From TestRecord.Part assoc. prop -->
<input asp-for="Part.Name" type="text" />
....
And so on...
当我 post 向我的控制器发送 运行 查询时,处理此查询的最佳方式是什么?我有 20 多个属性,这些属性可能会或可能不会在页面上填写,这些属性有助于将查询过滤到 return a List<TestRecord>
.
如果我只有几个属性要查询,并且我知道它们肯定会被填充,我可以这样做:
[HttpPost]
public List<TestRecord> Search(TestRecord testRecord){
List<TestRecord> records = _db.TestRecords
.Where(tr => tr.TestType == testRecord.TestType)
.Where(tr => tr.Part.Name == testRecord.Part.Name).ToList();
return records;
}
如何生成 LINQ 查询,如上文所述,查询模型中所有非 null/empty 的属性?将所有属性硬编码到我的查询中是我唯一的选择吗?
你就不能做这样的事情吗?
[HttpPost]
public List<TestRecord> Search(TestRecord testRecord){
List<TestRecord> records = _db.TestRecords
.Where(tr => String.IsNullOrEmpty(testRecord.TestType) ? true : tr.TestType == testRecord.TestType)
.Where(tr => String.IsNullOrEmpty(testRecord.Part.Name) ? true : tr.Part.Name == testRecord.Part.Name)
//etc...
.ToList();
return records;
}
基本上只有在 1 个大查询中每个字段都有输入时才进行过滤?
查询不必是一个链,您可以将其拆分并插入一些 if
:
var query = _db.TestRecords.AsQueryable();
if (string.IsNullOrEmpty(testRecord.TestType))
{
query = query.Where(x => x.TestType == testRecord.TestType);
}
if (string.IsNullOrEmpty(testRecord.Part.Name))
{
query = query.Where(x => x.Part.Name == testRecord.Part.Name);
}
// or, you can use an intermediate variable before returning to debug
return query.ToList();
我通常使用这样的扩展方法:
public static IQueryable<T> Where<T>(this IQueryable<T> that, object notNull, Expression<Func<T, bool>> predicate)
{
if (!string.IsNullOrWhiteSpace(notNull?.ToString()))
{
return that.Where(predicate);
}
return that;
}
然后您可以像这样编写 LINQ 查询:
return s.Query()
.Where(onlyStatus, p => p.Status == onlyStatus)
.OrderByDescending(p => p.CreatedDate)
.ToList();
如果您只有一个 class 满足此要求且属性数量有限,比如少于 20 个,我不会费心为此创建一个通用解决方案。编写检查所有属性的 Where
代码。这样做的好处是,如果将来有人更改或删除 属性 你的编译器会抱怨。
一个不错的解决方案是给你的 class 一个扩展函数:
public static bool HasNullProperties(this MyClass x)
{
return x.Name == null
&& x.Location == null
&& x.OrderSize == null
...;
}
public static IEnumerable<MyClass> WhereHasNullProperties(this IEnumerable<MyClass> source)
{
return source.Where(item => item.HasNullProperties();
}
LINQ 语句中某处的用法
var result = dbContext.MyItems.WhereHasNullProperties()
.GroupBy(...)
.Select(...);
如果您想要一个适用于多个 classes 的完整证明解决方案,请考虑设计一个接口:
interface IHasNullProperties
{
bool HasNullProperties {get;}
}
您的 LINQ 函数将是:
public static IEnumerable<TSource> WhereHasNullProperties<TSource>(
this IEnumerable<TSource> source)
where TSource : IHasNullProperties
{
return source.Where(item => item.HasNullProperties();
}
最后一个相对较慢的方法是使用反射:对于任何 class,获取其所有可为空的 get-properties 并查看是否有任何一个具有 null 值:
static bool HasNullPrperties<TSource>(this TSource source)
where TSource : class
{
// Take the type of the source, and get all properties of this type
var result = source.GetType().GetProperties()
// keep only the readable properties (so you can do GetValue)
// and those properties that have a nullable type
.Where(property => property.CanRead
&& Nullable.GetUnderlyingType(property.Type) != null)
// for every of this properties, ask the source object for the property value:
.Select(property => property.GetValue(source))
// and keep only the properties that have a null value
.Where(value => value == null);
// return true if source has any property with a null value
// = if there is any value left in my sequence
.Any();
return result;
}
我有两个模型:TestRecord
和 Part
其中 TestRecord
包含对 Part
的引用:
TestRecord
----
public string TestRecordId
public int PartId <-- foreign key to parts table
public string Name
public string TestType
public virtual Part Part
Part
----
int PartId
string Name
string Description
public virtual ICollection<TestRecord> TestRecords
我有一个搜索页面,显示每个 属性 测试记录的输入,包括其相关属性,例如:
@model TestRecord
<!-- From TestRecord -->
<input asp-for="TestType" type="text" />
<!-- From TestRecord.Part assoc. prop -->
<input asp-for="Part.Name" type="text" />
....
And so on...
当我 post 向我的控制器发送 运行 查询时,处理此查询的最佳方式是什么?我有 20 多个属性,这些属性可能会或可能不会在页面上填写,这些属性有助于将查询过滤到 return a List<TestRecord>
.
如果我只有几个属性要查询,并且我知道它们肯定会被填充,我可以这样做:
[HttpPost]
public List<TestRecord> Search(TestRecord testRecord){
List<TestRecord> records = _db.TestRecords
.Where(tr => tr.TestType == testRecord.TestType)
.Where(tr => tr.Part.Name == testRecord.Part.Name).ToList();
return records;
}
如何生成 LINQ 查询,如上文所述,查询模型中所有非 null/empty 的属性?将所有属性硬编码到我的查询中是我唯一的选择吗?
你就不能做这样的事情吗?
[HttpPost]
public List<TestRecord> Search(TestRecord testRecord){
List<TestRecord> records = _db.TestRecords
.Where(tr => String.IsNullOrEmpty(testRecord.TestType) ? true : tr.TestType == testRecord.TestType)
.Where(tr => String.IsNullOrEmpty(testRecord.Part.Name) ? true : tr.Part.Name == testRecord.Part.Name)
//etc...
.ToList();
return records;
}
基本上只有在 1 个大查询中每个字段都有输入时才进行过滤?
查询不必是一个链,您可以将其拆分并插入一些 if
:
var query = _db.TestRecords.AsQueryable();
if (string.IsNullOrEmpty(testRecord.TestType))
{
query = query.Where(x => x.TestType == testRecord.TestType);
}
if (string.IsNullOrEmpty(testRecord.Part.Name))
{
query = query.Where(x => x.Part.Name == testRecord.Part.Name);
}
// or, you can use an intermediate variable before returning to debug
return query.ToList();
我通常使用这样的扩展方法:
public static IQueryable<T> Where<T>(this IQueryable<T> that, object notNull, Expression<Func<T, bool>> predicate)
{
if (!string.IsNullOrWhiteSpace(notNull?.ToString()))
{
return that.Where(predicate);
}
return that;
}
然后您可以像这样编写 LINQ 查询:
return s.Query()
.Where(onlyStatus, p => p.Status == onlyStatus)
.OrderByDescending(p => p.CreatedDate)
.ToList();
如果您只有一个 class 满足此要求且属性数量有限,比如少于 20 个,我不会费心为此创建一个通用解决方案。编写检查所有属性的 Where
代码。这样做的好处是,如果将来有人更改或删除 属性 你的编译器会抱怨。
一个不错的解决方案是给你的 class 一个扩展函数:
public static bool HasNullProperties(this MyClass x)
{
return x.Name == null
&& x.Location == null
&& x.OrderSize == null
...;
}
public static IEnumerable<MyClass> WhereHasNullProperties(this IEnumerable<MyClass> source)
{
return source.Where(item => item.HasNullProperties();
}
LINQ 语句中某处的用法
var result = dbContext.MyItems.WhereHasNullProperties()
.GroupBy(...)
.Select(...);
如果您想要一个适用于多个 classes 的完整证明解决方案,请考虑设计一个接口:
interface IHasNullProperties
{
bool HasNullProperties {get;}
}
您的 LINQ 函数将是:
public static IEnumerable<TSource> WhereHasNullProperties<TSource>(
this IEnumerable<TSource> source)
where TSource : IHasNullProperties
{
return source.Where(item => item.HasNullProperties();
}
最后一个相对较慢的方法是使用反射:对于任何 class,获取其所有可为空的 get-properties 并查看是否有任何一个具有 null 值:
static bool HasNullPrperties<TSource>(this TSource source)
where TSource : class
{
// Take the type of the source, and get all properties of this type
var result = source.GetType().GetProperties()
// keep only the readable properties (so you can do GetValue)
// and those properties that have a nullable type
.Where(property => property.CanRead
&& Nullable.GetUnderlyingType(property.Type) != null)
// for every of this properties, ask the source object for the property value:
.Select(property => property.GetValue(source))
// and keep only the properties that have a null value
.Where(value => value == null);
// return true if source has any property with a null value
// = if there is any value left in my sequence
.Any();
return result;
}