如何使用where子句检查相关表
当我尝试将相关表与 include 一起使用时,我的空变量查询出现问题,我的数据库是 mysql
// working no error
var query = context.Category.Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();
var count = context.Category.Include(x => x.Products).Count();
// error null
var query = context.Category.Include(x => x.Products).Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();
这里是我的模型产品:
public class category {
public category() {}
public category(int productscount: this() {
products = new List < Product > ();
for (int i = 0; i < productscount: i++) {
products.Add(new Product());
}
}
public override int Id {
get;
set;
}
public List < Product > Products {
get;
set;
}}
这里是我的堆栈跟踪:
à MySql.Data.EntityFramework.SelectStatement.AddColumn(ColumnFragment 列,范围范围) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbApplyExpression 表达式) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, 字符串名称) , TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, 字符串名称, TypeUsage 类型) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression 表达式) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, 字符串名称,类型使用类型)à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, 字符串名称, TypeUsage 类型) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbSortExpression 表达式) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, 字符串名称,类型使用类型)à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression 表达式) à MySql.Data.EntityFramework.SelectGenerator.GenerateSQL(DbCommandTree 树) à MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifestproviderManifest,DbCommandTreecommandTree)àSystem.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory,DbCommandTreecommandTree,DbInterceptionContext拦截上下文,IDbDependencyResolver解析器,BridgeDataReaderFactory bridgeDataReaderFactory,ColumnMapFactory(columnMapFactory)à System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifestproviderManifest,DbCommandTree commandTree,DbInterceptionContext拦截Context)àSystem.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext上下文, DbQueryCommandTree 树)à System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean Streaming, Span span, IEnumerable
1 generatedQueryParameters, AliasGenerator aliasGenerator) à System.Data.Entity .Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable
1 forMergeOption) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__1() à System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func
1 func, IDbExecutionStrategyexecutionStrategy, 布尔startLocalTransaction, 布尔releaseConnectionOnSuccess) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__0() à System.Data.Entity.Core.Objects.ObjectQuery
1.GetResults(Nullable1 forMergeOption)àSystem.Data.Entity.Core.Objects.ObjectQuery
1..GetEnumerator>b__31_0()à System.Data.Entity.Internal.LazyEnumerator1.MoveNext() à System.Collections.Generic.List
1..ctor(IEnumerable1 集合)à System.Linq.Enumerable.ToList[ TSource](IEnumerable
1 源)
I have a problem in my query of null variable when I try to use related table with include, my database is mysql
// working no error
var query = context.Category.Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();
var count = context.Category.Include(x => x.Products).Count();
// error null
var query = context.Category.Include(x => x.Products).Where(c => c != null && ListOfIds.Contains(c.Id)).ToList();
Here My Model Product :
public class category {
public category() {}
public category(int productscount: this() {
products = new List < Product > ();
for (int i = 0; i < productscount: i++) {
products.Add(new Product());
}
}
public override int Id {
get;
set;
}
public List < Product > Products {
get;
set;
}}
Here my stack trace :
à MySql.Data.EntityFramework.SelectStatement.AddColumn(ColumnFragment column, Scope scope) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbApplyExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbSortExpression expression) à MySql.Data.EntityFramework.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type) à MySql.Data.EntityFramework.SelectGenerator.Visit(DbProjectExpression expression) à MySql.Data.EntityFramework.SelectGenerator.GenerateSQL(DbCommandTree tree) à MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) à System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory) à System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext) à System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree) à System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable
1 compiledQueryParameters, AliasGenerator aliasGenerator) à System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable
1 forMergeOption) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__1() à System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func
1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) à System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass41_0.b__0() à System.Data.Entity.Core.Objects.ObjectQuery
1.GetResults(Nullable1 forMergeOption) à System.Data.Entity.Core.Objects.ObjectQuery
1..GetEnumerator>b__31_0() à System.Data.Entity.Internal.LazyEnumerator1.MoveNext() à System.Collections.Generic.List
1..ctor(IEnumerable1 collection) à System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
仅在查询中添加
Include()
语句不应导致执行该查询以具体化结果时出错。这并不是说,如果您稍后迭代这些顶级实体,则包含的关系将不为空。如果您只想返回 ID 列表中的类别并且至少有一个与其关联的产品,请立即加载这些产品:
首先,添加
Where
子句<代码>c => c != null 几乎毫无意义。但是,如果您想要包含产品的类别,请使用 c.Products.Any() 来设置条件来检查这些产品。在处理您可能拥有没有相关数据的实体的可能性时,我必须避免空引用的一项建议是确保初始化相关的集合引用。
而不是:
use
这可确保任何可能访问产品集合的代码都不会引发空引用异常。理想情况下,如果集合没有急切加载,您将支持延迟加载作为故障保护。对于新的顶级实体(类别),这也意味着您的产品集合已准备好添加对产品的引用。
编辑:检查类别和产品之间的关系是否通过属性、OnModelCreating 或 EntityTypeConfiguration 正确映射,或者是否依赖 EDMX 进行 DB-First。 EF 可以按照惯例计算出关系,但这可能会导致 EF 是否正确地计算出结果。
例如,使用属性:
这告诉 EF 该实体中的 FK 属性是什么,并且这与 Category 实体中的 Products 集合相关。或者,可以将
[InverseProperty]
放在类别内的产品集合中。如果 EF 未将产品集合与产品类别的关系正确关联,则可能会出现问题。一般来说,这会导致映射错误,而不是像 NullReferenceException 之类的错误。另一个考虑因素可能是您正在使用的 MySQL EF 提供程序特有的问题。我不知道任何关于
Include
的 MySQL 问题会干扰查询执行。最后一个考虑因素可能是您的代码库是否出于某种原因覆盖 EF 的查询执行以检查并尝试注入 SQL 等。线索可能在异常的调用堆栈中,以查看引发此 NullReferenceException 的位置,无论是在 EF 内部还是在 DbContext 本身内的方法。
编辑 2:好吧,根据您的评论好吧,听起来您有一个带有过滤功能的查询,您最终会在其中获取 ID,但随后希望将这些 ID 提供给此方法以获取数据和相关记录。这将导致一些问题,可能是 MySQL 提供程序的限制。通常,当您在查询中使用
Contains
之类的内容时,提供程序通常会尝试在相关表的 JOIN 中生成 IN() 子句和 ty 之类的内容。如果它在没有Include
的情况下也能处理如此大的集合,我会感到惊讶。作为测试,我要检查的第一件事就是尝试加载急切加载的产品的所有类别...
如果这导致错误,那么可能是您的产品映射或关系的情况产品和类别之间被配置为期望类似于必需的不可为空值/引用之类的内容,并声明为实体中的不可为空成员,但数据支持并且在某些行的该列中具有#null。当加载前几千行时,一切都是有效的,但碰巧后面的一些数据有问题。
下一点是不要使用 Contains 方法来处理如此大的 ID 集。根据您的描述,我猜您有类似的情况:
要解决此问题,其中一个方法具有一个具有所有过滤条件的查询,并且您不想重复该代码,请将查询生成分解为私有方法返回采用条件的
IQueryable
,它可以由原始方法调用以选择 ID 或该方法需要的任何数据,您的新方法可以在其中调用它,Include
产品及列表结果通过相同的标准,而不是 10,000 - 41,000 个 ID 的列表。例如,如果原始方法具有类似以下内容:
并且您正在使用
results.Select(x => x.CategoryId).ToList();
等生成 ListOfIds 以提供给新方法...在原始方法中更改此设置以执行类似以下操作:then
这样,在您的新方法中,不是传递 ID 列表,而是传递将发送到第一个方法以获取这些 ID 的条件
: “标准”可以是包含条件的对象,或者简单地替换为您编写的用于获取 ID 的方法所使用的参数。要点是构建查询的逻辑集中在一个地方,并且使用它的方法仍然可以自定义它们执行和使用查询结果的方式。
加载 10,000 或 41,000 个实体和相关数据当然不是一个好主意。您应该仔细研究为什么要加载此数据并对这些结果进行汇总或分页。用户通常不会一次性需要 40k 个结果,甚至批量更新之类的事情也可能应该使用更直接的机制来更新数据,而不是 EF 实体。 (即存储过程或后台进程,一次处理较小的批次,并在必要时手动处理回滚功能。)
Simply adding an
Include()
statement in a query should not cause an error executing that query to materialize results. That isn't to say that if you are later iterating through those top level entities that the included relationship will not be empty.If you only want to return Categories that are in that list of IDs and have at least one Product associated with them, eager loading those products:
Firstly, adding a
Where
clause forc => c != null
is pretty much pointless. However, if you want categories that have Products then usec.Products.Any()
to put a condition to check for those.One recommendation I have to avoid Null references when dealing with the possibility that you will have entities with no related data is to ensure you initialize related collection references.
Instead of:
use
This ensures that any code that might access the products collection won't raise the null reference exception. Ideally you would support lazy loading as a failsafe in the event that the collection wasn't eager loaded. For new top-level entities (Category) this also means that your products collection is ready to go to add references to products.
Edit: Check that your relationship between category and product is mapped correctly, either via attributes, OnModelCreating, or an EntityTypeConfiguration, or whether you are relying on an EDMX for DB-First. EF can work out relationships by convention, but that can be leaving things to chance as to whether EF worked it out correctly.
For example, using attributes:
This tells EF what the FK property in this entity is, and that this is related to the Products collection in the Category entity. Alternatively the
[InverseProperty]
could be put on the Products collection within Category. If EF isn't associating the Products collection properly with the relationship to a product's Category then there may be issues. Generally though this would result in a mapping error rather than something likeNullReferenceException
.Another consideration might be an issue specific to the MySQL EF provider you are using. I am not aware of any MySQL issues around
Include
interfering with a query execution.the last consideration might be if your code base is overriding EF's query execution for some reason to inspect and attempt to inject SQL or such. A clue might be in the Exception's call stack to see where this NullReferenceException is being raised, whether internal to EF or a method within the DbContext itself.
Edit 2: Ok, from your comments Ok, t sounds like you have one query with filtering where you end up getting the IDs, but then want to feed those IDs into this method to get the data and related records. This will result in a few problems, possibly limitations in the MySQL provider. Normally when you use something like
Contains
in a query, the provider will generally be trying to produce something like an IN() clause and ty in the JOIN for the related table. I'd be surprised if it worked with such a large set without theInclude
.The first thing I'd check as a test is just trying loading all Categories with their products eager loaded...
If this causes an error then it might be a case where your mapping of Products or the relationship between product and category is configured to expect something like a required non-nullable value/reference and declared as a non-nullable member in the entity, but the data supports and has #null in that column on some rows. When loading the first few thousand rows, everything is valid, but it happens that some of the later data has an issue.
The next point is to not use that Contains approach for dealing with such a large set of IDs. From what you describe I'm guessing you have something like:
To fix this where one method has a Query that has all of the filtering criteria and you don't want to duplicate that code, break off the query generation into a private method that returns
IQueryable<Category>
that takes the criteria and can be called by both the original method to Select IDs or whatever data that method needs, where your new method can call it,Include
Products and ToList the results passing the same criteria instead of a list of 10,000 - 41,000 Ids.For instance if the original method had something like:
and you were generating your ListOfIds using the
results.Select(x => x.CategoryId).ToList();
or such to feed to the new method... Change this in the original method to do something like this:then
This way, in your new method, instead of passing the list of IDs, pass the criteria that would have been sent to the first method to get those IDs:
The "Criteria" could be an object containing the criteria, or simply replaced with the parameters used for the method you wrote to get the IDs. The point is that the logic to build the Query is centralized in one spot and the methods using it can still customize how they execute and consume the query results.
Loading 10,000 or 41,000 entities and related data is certainly not a good idea. You should take a close look at why you want to load this data and either summarize or paginate these results. Users typically will not need 40k results all in one go, and even things like batch updates should probably use a more direct mechanism to update the data rather than EF entities. (I.e. a stored procedure, or a background process that will deal with smaller batches at a time and handle rollback capabilities manually if necessary.)