"Ссылка на объект не указывает на экземпляр объекта" - но ничто не является нулем?

Да, вы, наверное, думаете; " Боже, еще один?".

да, еще одно.

"ссылка на объект не указывает на экземпляр объекта."

в последнее время я работаю с EF6, и после разработки в течение некоторого времени я обнаружил, что требуется немного больше оптимизации. Много было переработано без проблем,но, похоже, я не могу понять это.

в моем приложении я использовал этот кусок псевдо код для получения элементов из базы данных.

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. Однако это проходит!--11-->, что означает, что запрос не равен null. В этот момент я растерялся. Как это не может быть null, но все же выбросить эту ошибку?

Я не был уверен, что запрос будет выполнен, если я попытаюсь получить к нему доступ с помощью этого подхода. Поэтому я попытался создать список из запроса используя ToList(), но это вызывает то же исключение при создании списка.

кажется, что каждый раз, когда я звоню query, это дает мне NullReferenceException. Однако он все равно проходит if(query == null). Итак, мой вопрос:

почему он проходит тест, но недоступен? Я перевирать IQueryable<>? А если я неверно истолковал, то как это сделать правильно?

редактировать

Я отлаживал раньше публиковать. Я это точно знаю;

  • names не null.
  • context не null.

код вызова функции:

//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

3 ответов


после того, как вы опубликовали stacktrace я заметил, что вы используете MySQL, и поэтому я предполагаю, что вы попали в эту ошибку:исключение при использовании IEnumera.Содержит (модель.свойство) В где сказуемое

таким решением было бы гарантировать, что у вас есть версии MySQL Connector / NET 6.7.6 / 6.8.4 / 6.9.5 и новее. Или попробуйте использовать Any вместо Contains.

P. s. Этот отчет об ошибке пришел с этого поста от Alnedru:Int[].Contains не работает в Ef6 в


ваша проверка null на запрос никогда не завершится неудачей, потому что он возвращает объект IQueryable (то есть строящийся запрос). Вы создали его экземпляр и начали создавать запрос, поэтому он всегда будет проходить.

чтобы быть ясным-IQueryable примерно эквивалентно строке, содержащей ADO.Net выберите оператор. По сути, это не фактические данные.

это не объясняет, почему он бросает исключение нулевого, но это тут объясните, почему проверка null проходит, и foreach все равно может потерпеть неудачу.

EDIT: при попытке дублировать это я обнаружил, что получаю исключение при использовании приведенного ниже кода:

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, но a NotSupportedException, как ICollections Contains не поддерживает перевод на SQL. Переключение параметра на List вызвало исчезновение проблемы:

public IEnumerable<string> GetExistingNames(List<string> names)

или вы можете преобразовать его в список на лету:

IQueryable<Names> query = Names.Where(n => names.ToList().Contains(n.Name));

почему бы вам просто не добавить метод расширения, чтобы облегчить стресс, который это приносит вам. попробуйте этот фрагмент кода

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;
    }
}