是的,另一个。
“对象引用未设置为对象的实例。”
我最近一直在使用EF6,经过一段时间的开发,我发现需要更多的优化。 Alot已经重新设计没有问题,但似乎我无法弄清楚这个。
在我的应用程序中,我一直在使用这段伪代码从数据库中获取项目。
DbContext context = new DbContext();
public IEnumerable<string> GetExistingNames(IEnumerable<string> names)
{
foreach(string name in names)
{
string existingName = context.Names.Where(n => n.Name == name).FirstOrDefault();
if(existingName == null) continue;
yield return existingName;
}
}
请注意,DbContext
仅供澄清。它会在需要时被处理掉。
这种方法“有效”,但这意味着,如果我有20个名字要查找,我会打到数据库大约20次。哎哟!
因此,我开始寻找实现单个查询的方法。我找到了一种方法,但它并没有真正发挥作用。这是我目前的做法;
public IEnumerable<string> GetExistingNames(ICollection<string> names)
{
IQueryable<Names> query = context.Names.Where(n => names.Contains(n.Name));
if(query == null) yield break;
foreach(var name in query)
{
yield return name.Name;
}
}
据我所知,这应该在SELECT ... FROM Names WHERE ... IN (...)
翻译。但是,我的应用程序在foreach(var name in query)
遇到name
后立即崩溃,引发了恐惧的NullReferenceException
。
但是,它会传递if(query == null)
,这意味着查询不为空。此时,我很困惑。它怎么能不是null,但仍然抛出这个错误?
如果我尝试使用这种方法访问它,我不确定查询是否会被执行。因此,我尝试使用ToList()
从查询创建列表,但这会在创建列表时引发相同的异常。
似乎每当我拨打query
时,它都会给我一个NullReferenceException
。但是,它仍然通过if(query == null)
。所以,我的问题是;
为什么通过测试,但是无法访问?我误解了IQueryable<>
吗?如果我误解了它,应该如何正确地完成它?
修改
我在发布之前已经调试过了。我肯定知道;
names
不为空。context
不为空。代码调用函数:
//A wrapper for the DbContext. This is only used for some methods
//which require the DbContext
DbContextWrapper wrapper = new DbContextWrapper();
public void ProcessNames(List<string> inputNames)
{
//...
foreach(string existingName in wrapper.GetExistingNames(inputNames))
{
//Do something with the names
}
//...
}
编辑2
经过一些调试后,我发现正在创建的查询有些不同。应该是;
SELECT `Extent1`.`Name`
FROM `Names` AS `Extent1`
WHERE (`Extent1`.`Name` IN ( @gp1,@gp2))
然而,我得到了这个;
System.Data.Entity.Infrastructure.DbQuery<MyDbContext.Names>
作为实际查询。
堆栈跟踪;
at MySql.Data.Entity.SqlGenerator.Visit(DbPropertyExpression expression)
at MySql.Data.Entity.SqlGenerator.Visit(DbInExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbInExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SqlGenerator.VisitBinaryExpression(DbExpression left, DbExpression right, String op)
at MySql.Data.Entity.SqlGenerator.Visit(DbAndExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbAndExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SelectGenerator.Visit(DbFilterExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbFilterExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type)
at MySql.Data.Entity.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type)
at MySql.Data.Entity.SelectGenerator.Visit(DbProjectExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbProjectExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SelectGenerator.GenerateSQL(DbCommandTree tree)
at MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
at System.Data.Entity.Core.Common.DbProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)
at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateCommandDefinition(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver)
at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree)
at 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)
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.Infrastructure.DefaultExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at MyNameSpace.DbContextWrapper.<GetExistingNames>d__1b.MoveNext() in c:~omitted~\DbContextWrapper.cs:line 70
at MyNameSpace.NameProcessor.ProcessNames(List<string> inputNames) in c:~omitted~\NameProcessor.cs:line 60
答案 0 :(得分:2)
发布堆栈跟踪后我发现你使用的是MySQL,所以我的猜测就是你遇到了这个错误:Exception when using IEnumera.Contains(model.property) in Where predicate
因此,解决方案是确保您拥有MySQL Connector / NET 6.7.6 / 6.8.4 / 6.9.5及更新版本。
或者尝试使用Any
方法而不是Contains
。
P.S。此错误报告来自Alnedru的这篇文章:Int[].Contains doesn't work in EF6
答案 1 :(得分:0)
您对查询的空检查永远不会失败,因为它返回IQueryable对象(即正在构建的查询)。您已实例化它并开始构建查询,因此它将始终通过。
要清楚 - IQueryable大致相当于包含ADO.Net Select语句的字符串。它本质上不是实际数据。
这并不能解释为什么它会抛出一个空异常,但确实解释了为什么null检查通过,而foreach仍然可能失败。
编辑:尝试复制时,我发现在使用以下代码时出现异常:
public IEnumerable<string> GetExistingNames(ICollection<string> names)
{
IQueryable<Names> query = Names.Where(n => names.Contains(n.Name));
if (query == null) yield break;
foreach (var name in query)
{
yield return name.Name;
}
}
它不是NullReferenceException
,而是NotSupportedException
,因为ICollections Contains
没有支持的SQL转换。将参数切换为List
会导致问题消失:
public IEnumerable<string> GetExistingNames(List<string> names)
或者您可以将其转换为动态列表:
IQueryable<Names> query = Names.Where(n => names.ToList().Contains(n.Name));
答案 2 :(得分:-2)
为什么不直接添加扩展方法来缓解这给您带来的压力。 试试这段代码
namespace HelperExtensionMethods
{
public static class ExtensionMethods
{
public static string UpdateNullString(this string testNullstring)
{
if (TestNullstring == null)
return "";
return Testullstring;
}
}
}
然后像这样调用它
using HelperExtesionMethods
DbContext context = new DbContext();
public IEnumerable<string> GetExistingNames(ICollection<string> names)
{
IQueryable<Names> query = context.Names.Where(n => names.UpdateNullString().Contains(n.Name.UpdateNullString()));
if(query == null) yield break;
foreach(var name in query)
{
yield return name.Name;
}
}