Entity Framework, LINQ 查询
Entity Framework, LINQ Query
如何循环遍历下面的项目并用值 (!)
错误:
Cannot implicitly convert type System.threading.task to
System.Collections.Generic.IEnumerable<Respositories.AssignmentMasterData>
using (SUPEntities db = new SUPEntities())
{
IEnumerable<AssignementMasterData> masterDatas = null;
masterDatas = db.AssignementMasterDatas
.Where(m => DbFunctions.TruncateTime(m.CreatedDateTime) >= DbFunctions.TruncateTime(criteria.FilterStartDate)
&& DbFunctions.TruncateTime(m.CreatedDateTime) <= DbFunctions.TruncateTime(criteria.FilterEndDate)
&& (m.AssignmentNoteNumber == criteria.AssigmentNumber || criteria.AssignmentNumber == null)
&& (m.BaseCourseId == criteria.courseId || criteria.CourseId == 0)
&& (m.AccountNumber == criteria.AccountNumber || criteria.AccountNumber == null)
&& (m.ReferenceNumber == criteria.ReferenceNumber || criteria.ReferenceNumber == null)
&& (m.FacultyCode == criteria.FAcultyCode || criteria.FacultyCode == null)
&& (m.Processed == criteria.Processed)
&& (m.ClassNumber == criteria.ClassNumber || criteria.ClassNumber == null))
.ForEachAsync(t => t.AssignmentNoteIdentifiedClasses.Select(e => String.IsNullOrEmpty(e.Category)? "(!)": e.Category));
}
首先,关于错误信息:
您正试图将错误的类型分配给您的 masterDatas
变量。
您将其声明为 IEnumerable<Respositories.AssignmentMasterData>
,但最后一行的 ForEachAsync
将 return 为 Task
,因此出现错误消息。
查看ForEachAsync
签名:
public static System.Threading.Tasks.Task ForEachAsync (this
System.Linq.IQueryable source, Action action);
其次。你想要 return 一个 IEnumerable<Respositories.AssignmentMasterData>
如果您可以满足于同步方法,您可以这样做:
您需要在某个时候将您的 IQueryable
转换为 IEnumerable
。调用 AsEnumerable()
即可。然后你需要替换一些值。所以你需要使用 Select
.
投影你的 collection
using (SUPEntities db = new SUPEntities())
{
var masterDatas = db.AssignementMasterDatas
.Where(m => DbFunctions.TruncateTime(m.CreatedDateTime) >= DbFunctions.TruncateTime(criteria.FilterStartDate)
&& DbFunctions.TruncateTime(m.CreatedDateTime) <= DbFunctions.TruncateTime(criteria.FilterEndDate)
&& (m.AssignmentNoteNumber == criteria.AssigmentNumber || criteria.AssignmentNumber == null)
&& (m.BaseCourseId == criteria.courseId || criteria.CourseId == 0)
&& (m.AccountNumber == criteria.AccountNumber || criteria.AccountNumber == null)
&& (m.ReferenceNumber == criteria.ReferenceNumber || criteria.ReferenceNumber == null)
&& (m.FacultyCode == criteria.FAcultyCode || criteria.FacultyCode == null)
&& (m.Processed == criteria.Processed)
&& (m.ClassNumber == criteria.ClassNumber || criteria.ClassNumber == null))
.AsEnumerable()
.Select(a =>
{
a.AssignmentNoteIdentifiedClasses = a.AssignmentNoteIdentifiedClasses
.Select(e =>
{
e.Category = string.IsNullOrWhiteSpace(e.Category) ? "(!)" : e.Category;
return e;
})
.ToList(); // Depending on the type of AssignmentNoteIdentifiedClasses, ToList() might be replaced.
return a;
});
return masterDatas;
}
- 使用
.Include( m => m.AssignmentNoteIdentifiedClasses )
在单个查询中引入相关数据,这比在 for 中加载每组相关 AssignmentNoteIdentifiedClasses
快得多 - 每行循环。
- 您不需要使用
TruncateTime
。
- 事实上,您不应该,因为那将意味着您的查询不可搜索。
- 避免 SQL 谓词中的函数。
- 相反,只需将
criteria.FilterStartDate
舍入到应用程序代码中的一天的开始,然后将其与 m => m.CreatedDateTime >= filterStart
进行正常比较。
- 同样,
FilterEndDate
应该四舍五入,然后像这样进行比较:m => m.CreatedDateTime < filterEnd
- 始终使用独占上限。它使一切,尤其是日期范围谓词,更容易处理。
- 您的
Where
中不需要内联 &&
。请改用额外的单独 .Where()
子句。它们将作为单独的 AND
个条款添加到相同的(单个)WHERE
子句中。
- 我假设 EF Core 不够复杂,无法识别可选搜索谓词的“
NULL
-means-ignore”反模式,在这种情况下不要使用“ NULL
-means-ignore" anti-pattern IN AN IQUERYABLE<T>
PREDICATE!
- 这很糟糕,原因有很多:即因为查询执行计划是基于 结构 (“形状”)的 SQL 查询和 not 参数值,因此当某些甚至所有参数为
NULL
时,将使用非 NULL
参数的相同缓存执行计划 - 这是一个问题.另外,请务必阅读 parameter sniffing.
- 相反,通过使用
IQueryable<T>
的 Linq 扩展并重新分配给自身来构建您的查询。
- 例如
IQueryable<T> query = db.Etc; query = query.Where( e => etc );
- 每个
.Where()
添加为一个 AND
条件。如果你想建立一个 OR
条件 then use PredicateBuilder
.
DateTime filterStart = criteria.FilterStartDate.Date;
DateTime filterEndExcl = criteria.FilterEndDate .Date.AddDays(1);
using (SUPEntities db = new SUPEntities())
{
IQueryable<AssignementMasterData> query = db.AssignementMasterDatas
.Include( m => m.AssignmentNoteIdentifiedClasses )
.Where( m => m.CreatedDateTime >= filterStart )
.Where( m => m.CreatedDateTime < filterEndExcl ) // Exclusive upper-bound.
.Where( m => m.Processed == criteria.Processed )
.Where( m => m.ClassNumber == criteria.ClassNumber )
;
if( criteria.AssigmentNumber != null )
{
query = query.Where( m => m.AssignmentNoteNumber == criteria.AssigmentNumber );
}
if( criteria.AccountNumber != null )
{
query = query.Where( m => m.AccountNumber == criteria.AccountNumber );
}
if( criteria.CourseId != null && criteria.CourseId.Value > 0 )
{
query = query.Where( m => m.BaseCourseId == criteria.CourseId );
}
if( criteria.ReferenceNumber != null )
{
query = query.Where( m => m.ReferenceNumber == criteria.ReferenceNumber );
}
if( criteria.FacultyCode != null )
{
query = query.Where( m => m.FacultyCode == criteria.FacultyCode );
}
if( criteria.ClassNumber != null )
{
query = query.Where( m => m.ClassNumber == criteria.ClassNumber );
}
List<AssignementMasterData> rows = await query.ToListAsync().ConfigureAwait(false);
List<String> categories = rows
.SelectMany( r => r.AssignmentNoteIdentifiedClasses )
.Select( String.IsNullOrEmpty(e.Category)? "(!)": e.Category) )
.ToList();
return categories;
}
可以通过添加新的扩展方法来简化上述内容(确保使用 Expression<Func<...>>
而不仅仅是 Func<>
,这样 EF 仍然可以解释查询:
public static class MyQueryableExtensions
{
public static IQueryable<T> WhereIfNotNull<T,TValue>( this IQueryable<T> query, TValue? value, Expression<Func<T,Boolean>> predicate )
where TValue : struct
{
if( value.HasValue && value.Value != default(TValue) )
{
return query.Where( predicate );
}
else
{
return query;
}
}
}
这样使用:
// `criteria` is now named `c` for brevity.
DateTime filterStart = c.FilterStartDate.Date;
DateTime filterEndExcl = c.FilterEndDate .Date.AddDays(1);
using (SUPEntities db = new SUPEntities())
{
IQueryable<AssignementMasterData> query = db.AssignementMasterDatas
.Include( m => m.AssignmentNoteIdentifiedClasses )
.Where( m => m.CreatedDateTime >= filterStart )
.Where( m => m.CreatedDateTime < filterEndExcl ) // Exclusive upper-bound.
.Where( m => m.Processed == c.Processed )
.Where( m => m.ClassNumber == c.ClassNumber )
.WhereIfNotNull( c.AssigmentNumber, m => m.AssignmentNoteNumber == c.AssigmentNumber )
.WhereIfNotNull( c.AccountNumber , m => m.AccountNumber == c.AccountNumber )
.WhereIfNotNull( c.CourseId , m => m.BaseCourseId == c.CourseId )
.WhereIfNotNull( c.ReferenceNumber, m => m.ReferenceNumberr == c.ReferenceNumber )
.WhereIfNotNull( c.FacultyCode , m => m.FacultyCoder == c.FacultyCode )
.WhereIfNotNull( c.ClassNumber , m => m.ClassNumber == c.ClassNumber )
;
List<AssignementMasterData> rows = await query.ToListAsync().ConfigureAwait(false);
List<String> categories = rows
.SelectMany( r => r.AssignmentNoteIdentifiedClasses )
.Select( String.IsNullOrEmpty(e.Category)? "(!)": e.Category) )
.ToList();
return categories;
}
如何循环遍历下面的项目并用值 (!) 错误:
Cannot implicitly convert type System.threading.task to System.Collections.Generic.IEnumerable<Respositories.AssignmentMasterData>
using (SUPEntities db = new SUPEntities())
{
IEnumerable<AssignementMasterData> masterDatas = null;
masterDatas = db.AssignementMasterDatas
.Where(m => DbFunctions.TruncateTime(m.CreatedDateTime) >= DbFunctions.TruncateTime(criteria.FilterStartDate)
&& DbFunctions.TruncateTime(m.CreatedDateTime) <= DbFunctions.TruncateTime(criteria.FilterEndDate)
&& (m.AssignmentNoteNumber == criteria.AssigmentNumber || criteria.AssignmentNumber == null)
&& (m.BaseCourseId == criteria.courseId || criteria.CourseId == 0)
&& (m.AccountNumber == criteria.AccountNumber || criteria.AccountNumber == null)
&& (m.ReferenceNumber == criteria.ReferenceNumber || criteria.ReferenceNumber == null)
&& (m.FacultyCode == criteria.FAcultyCode || criteria.FacultyCode == null)
&& (m.Processed == criteria.Processed)
&& (m.ClassNumber == criteria.ClassNumber || criteria.ClassNumber == null))
.ForEachAsync(t => t.AssignmentNoteIdentifiedClasses.Select(e => String.IsNullOrEmpty(e.Category)? "(!)": e.Category));
}
首先,关于错误信息:
您正试图将错误的类型分配给您的 masterDatas
变量。
您将其声明为 IEnumerable<Respositories.AssignmentMasterData>
,但最后一行的 ForEachAsync
将 return 为 Task
,因此出现错误消息。
查看ForEachAsync
签名:
public static System.Threading.Tasks.Task ForEachAsync (this System.Linq.IQueryable source, Action action);
其次。你想要 return 一个 IEnumerable<Respositories.AssignmentMasterData>
如果您可以满足于同步方法,您可以这样做:
您需要在某个时候将您的 IQueryable
转换为 IEnumerable
。调用 AsEnumerable()
即可。然后你需要替换一些值。所以你需要使用 Select
.
using (SUPEntities db = new SUPEntities())
{
var masterDatas = db.AssignementMasterDatas
.Where(m => DbFunctions.TruncateTime(m.CreatedDateTime) >= DbFunctions.TruncateTime(criteria.FilterStartDate)
&& DbFunctions.TruncateTime(m.CreatedDateTime) <= DbFunctions.TruncateTime(criteria.FilterEndDate)
&& (m.AssignmentNoteNumber == criteria.AssigmentNumber || criteria.AssignmentNumber == null)
&& (m.BaseCourseId == criteria.courseId || criteria.CourseId == 0)
&& (m.AccountNumber == criteria.AccountNumber || criteria.AccountNumber == null)
&& (m.ReferenceNumber == criteria.ReferenceNumber || criteria.ReferenceNumber == null)
&& (m.FacultyCode == criteria.FAcultyCode || criteria.FacultyCode == null)
&& (m.Processed == criteria.Processed)
&& (m.ClassNumber == criteria.ClassNumber || criteria.ClassNumber == null))
.AsEnumerable()
.Select(a =>
{
a.AssignmentNoteIdentifiedClasses = a.AssignmentNoteIdentifiedClasses
.Select(e =>
{
e.Category = string.IsNullOrWhiteSpace(e.Category) ? "(!)" : e.Category;
return e;
})
.ToList(); // Depending on the type of AssignmentNoteIdentifiedClasses, ToList() might be replaced.
return a;
});
return masterDatas;
}
- 使用
.Include( m => m.AssignmentNoteIdentifiedClasses )
在单个查询中引入相关数据,这比在 for 中加载每组相关AssignmentNoteIdentifiedClasses
快得多 - 每行循环。 - 您不需要使用
TruncateTime
。- 事实上,您不应该,因为那将意味着您的查询不可搜索。
- 避免 SQL 谓词中的函数。
- 相反,只需将
criteria.FilterStartDate
舍入到应用程序代码中的一天的开始,然后将其与m => m.CreatedDateTime >= filterStart
进行正常比较。 - 同样,
FilterEndDate
应该四舍五入,然后像这样进行比较:m => m.CreatedDateTime < filterEnd
- 始终使用独占上限。它使一切,尤其是日期范围谓词,更容易处理。
- 您的
Where
中不需要内联&&
。请改用额外的单独.Where()
子句。它们将作为单独的AND
个条款添加到相同的(单个)WHERE
子句中。 - 我假设 EF Core 不够复杂,无法识别可选搜索谓词的“
NULL
-means-ignore”反模式,在这种情况下不要使用“NULL
-means-ignore" anti-pattern IN ANIQUERYABLE<T>
PREDICATE!- 这很糟糕,原因有很多:即因为查询执行计划是基于 结构 (“形状”)的 SQL 查询和 not 参数值,因此当某些甚至所有参数为
NULL
时,将使用非NULL
参数的相同缓存执行计划 - 这是一个问题.另外,请务必阅读 parameter sniffing. - 相反,通过使用
IQueryable<T>
的 Linq 扩展并重新分配给自身来构建您的查询。- 例如
IQueryable<T> query = db.Etc; query = query.Where( e => etc );
- 每个
.Where()
添加为一个AND
条件。如果你想建立一个OR
条件 then usePredicateBuilder
.
- 例如
- 这很糟糕,原因有很多:即因为查询执行计划是基于 结构 (“形状”)的 SQL 查询和 not 参数值,因此当某些甚至所有参数为
DateTime filterStart = criteria.FilterStartDate.Date;
DateTime filterEndExcl = criteria.FilterEndDate .Date.AddDays(1);
using (SUPEntities db = new SUPEntities())
{
IQueryable<AssignementMasterData> query = db.AssignementMasterDatas
.Include( m => m.AssignmentNoteIdentifiedClasses )
.Where( m => m.CreatedDateTime >= filterStart )
.Where( m => m.CreatedDateTime < filterEndExcl ) // Exclusive upper-bound.
.Where( m => m.Processed == criteria.Processed )
.Where( m => m.ClassNumber == criteria.ClassNumber )
;
if( criteria.AssigmentNumber != null )
{
query = query.Where( m => m.AssignmentNoteNumber == criteria.AssigmentNumber );
}
if( criteria.AccountNumber != null )
{
query = query.Where( m => m.AccountNumber == criteria.AccountNumber );
}
if( criteria.CourseId != null && criteria.CourseId.Value > 0 )
{
query = query.Where( m => m.BaseCourseId == criteria.CourseId );
}
if( criteria.ReferenceNumber != null )
{
query = query.Where( m => m.ReferenceNumber == criteria.ReferenceNumber );
}
if( criteria.FacultyCode != null )
{
query = query.Where( m => m.FacultyCode == criteria.FacultyCode );
}
if( criteria.ClassNumber != null )
{
query = query.Where( m => m.ClassNumber == criteria.ClassNumber );
}
List<AssignementMasterData> rows = await query.ToListAsync().ConfigureAwait(false);
List<String> categories = rows
.SelectMany( r => r.AssignmentNoteIdentifiedClasses )
.Select( String.IsNullOrEmpty(e.Category)? "(!)": e.Category) )
.ToList();
return categories;
}
可以通过添加新的扩展方法来简化上述内容(确保使用 Expression<Func<...>>
而不仅仅是 Func<>
,这样 EF 仍然可以解释查询:
public static class MyQueryableExtensions
{
public static IQueryable<T> WhereIfNotNull<T,TValue>( this IQueryable<T> query, TValue? value, Expression<Func<T,Boolean>> predicate )
where TValue : struct
{
if( value.HasValue && value.Value != default(TValue) )
{
return query.Where( predicate );
}
else
{
return query;
}
}
}
这样使用:
// `criteria` is now named `c` for brevity.
DateTime filterStart = c.FilterStartDate.Date;
DateTime filterEndExcl = c.FilterEndDate .Date.AddDays(1);
using (SUPEntities db = new SUPEntities())
{
IQueryable<AssignementMasterData> query = db.AssignementMasterDatas
.Include( m => m.AssignmentNoteIdentifiedClasses )
.Where( m => m.CreatedDateTime >= filterStart )
.Where( m => m.CreatedDateTime < filterEndExcl ) // Exclusive upper-bound.
.Where( m => m.Processed == c.Processed )
.Where( m => m.ClassNumber == c.ClassNumber )
.WhereIfNotNull( c.AssigmentNumber, m => m.AssignmentNoteNumber == c.AssigmentNumber )
.WhereIfNotNull( c.AccountNumber , m => m.AccountNumber == c.AccountNumber )
.WhereIfNotNull( c.CourseId , m => m.BaseCourseId == c.CourseId )
.WhereIfNotNull( c.ReferenceNumber, m => m.ReferenceNumberr == c.ReferenceNumber )
.WhereIfNotNull( c.FacultyCode , m => m.FacultyCoder == c.FacultyCode )
.WhereIfNotNull( c.ClassNumber , m => m.ClassNumber == c.ClassNumber )
;
List<AssignementMasterData> rows = await query.ToListAsync().ConfigureAwait(false);
List<String> categories = rows
.SelectMany( r => r.AssignmentNoteIdentifiedClasses )
.Select( String.IsNullOrEmpty(e.Category)? "(!)": e.Category) )
.ToList();
return categories;
}