Значение не может быть нулевым; Имя параметра: конструктор при использовании SqlExecuteQuery с абстрактной моделью Entity - PullRequest
3 голосов
/ 09 августа 2011

Мы храним элементы в нашей базе данных, которые получены из одного абстрактного объекта с именем SurveyItem.Мы пытаемся найти SurveyItems, которые содержат некоторую строку запроса.Я прошу прощения за стену кода, но я думаю, что это так мало, как я могу сделать это, оставаясь в нашей ситуации.Я подозреваю, что проблема вызвана наследованием объектов и использованием абстрактного корня.При использовании описанного ниже метода с сущностями без наследования все в порядке.

Наши сущности хранятся с использованием TpH и выглядят так:

public abstract class SurveyItem
{
    [ScaffoldColumn(false)]
    public int Id { get; set; }

    [ScaffoldColumn(false)]
    public virtual ICollection<Survey_SurveyItem> SurveyLinks { get; private set; }

    [ScaffoldColumn(false)]
    public abstract string Identifier { get; }

    public SurveyItem()
    {
        SurveyLinks = new List<Survey_SurveyItem>();
    }
}

public class SurveyHeaderItem : SurveyItem
{
    [Required]
    [Display(Name = "title", ResourceType = typeof(Caracal.Resources.GUI))]
    public Translated Title { get; set; }

    [ScaffoldColumn(false)]
    public override string Identifier
    {
        get { return Title.NL + " / " + Title.EN; }
    }
    // ...
}

public class SurveyQuestion : SurveyItem
{
    [Required]
    [Order(1)]
    [Display(Name = "question", ResourceType = typeof(Caracal.Resources.GUI))]
    public Translated Question { get; set; }

    [Order(2)]
    [Display(Name = "description", ResourceType = typeof(Caracal.Resources.GUI))]
    public TranslatedMultilineOptional Description { get; set; }
    //...
}

Абстрактный корневой класс не содержит полей, которые мы хотимчтобы можно было проверить что-то вроде следующего невозможно:

Context.SurveyItems.Where(x => x.Field.contains(q));

Итак, мы попробовали следующую хранимую процедуру в сочетании с кодом ниже:

CREATE PROCEDURE SearchSurveyItems (@Q nvarchar(255))
AS
BEGIN
    SET NOCOUNT ON;
    SELECT i.*
    FROM [dbo].SurveyItems AS i
    WHERE
        i.Question_NL LIKE ('%' + @Q + '%') OR
        i.Question_EN LIKE ('%' + @Q + '%') OR
        i.Description_NL LIKE ('%' + @Q + '%') OR
        i.Description_EN LIKE ('%' + @Q + '%') OR
        i.Title_NL LIKE ('%' + @Q + '%') OR
        i.Title_EN LIKE ('%' + @Q + '%') OR
        i.Text_NL LIKE ('%' + @Q + '%') OR
        i.Text_EN LIKE ('%' + @Q + '%')
END

И вызовиз C #:

public IEnumerable<SurveyItem> SearchSurveyItems(string q)
{
    return this.Database.SqlQuery<SurveyItem>("dbo.SearchSurveyItems @Q", new SqlParameter("Q", q)).ToList();
}

Это приводит к ошибке из заголовка:

System.ArgumentNullException was unhandled by user code
  Message=Value cannot be null.
Parameter name: constructor
  Source=System.Core
  ParamName=constructor
  StackTrace:
       at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments)
       at System.Data.Common.Internal.Materialization.Translator.Emit_ConstructEntity(EntityType oSpaceType, IEnumerable`1 propertyBindings, Expression entityKeyReader, Expression entitySetReader, TranslatorArg arg, EntityProxyTypeInfo proxyTypeInfo)
       at System.Data.Common.Internal.Materialization.Translator.Visit(EntityColumnMap columnMap, TranslatorArg arg)
       at System.Data.Query.InternalTrees.EntityColumnMap.Accept[TResultType,TArgType](ColumnMapVisitorWithResults`2 visitor, TArgType arg)
       at System.Data.Common.Internal.Materialization.Translator.ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg, ColumnMap discriminatorColumnMap, Object discriminatorValue)
       at System.Data.Common.Internal.Materialization.Translator.Visit(SimpleCollectionColumnMap columnMap, TranslatorArg arg)
       at System.Data.Query.InternalTrees.SimpleCollectionColumnMap.Accept[TResultType,TArgType](ColumnMapVisitorWithResults`2 visitor, TArgType arg)
       at System.Data.Common.Internal.Materialization.Translator.TranslateColumnMap[TRequestedType](QueryCacheManager queryCacheManager, ColumnMap columnMap, MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, Boolean valueLayer)
       at System.Data.Objects.ObjectContext.InternalTranslate[TElement](DbDataReader reader, String entitySetName, MergeOption mergeOption, Boolean readerOwned)
       at System.Data.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](String commandText, String entitySetName, MergeOption mergeOption, Object[] parameters)
       at System.Data.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, Object[] parameters)
       at System.Data.Entity.Internal.InternalContext.ExecuteSqlQuery[TElement](String sql, Object[] parameters)
       at System.Data.Entity.Internal.InternalContext.ExecuteSqlQueryAsIEnumerable[TElement](String sql, Object[] parameters)
       at System.Data.Entity.Internal.InternalContext.ExecuteSqlQuery(Type elementType, String sql, Object[] parameters)
       at System.Data.Entity.Internal.InternalSqlNonSetQuery.GetEnumerator()
       at System.Data.Entity.Internal.InternalSqlQuery`1.GetEnumerator()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
       at Caracal.Entities.CaracalContext.SearchSurveyItems(String q) in J:\Caracal\Entities\CaracalContextDataAccessors.cs:line 48
       at Caracal.Application.Controllers.SearchController.SurveyItem(String q) in J:\Caracal\application\Controllers\SearchController.cs:line 75
       at lambda_method(Closure , ControllerBase , Object[] )
       at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
       at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
  InnerException:

Обновление

Эта проблема исправлена ​​при переименованиикласс SurveyItem для AbstractSurveyItem, и я создаю новый класс SurveyItem, унаследованный от AbstractSurveyItem.Затем он может идеально создавать объекты SurveyItem.

Однако я не считаю это реальным решением, поскольку теперь вы теряете безопасность и возможности абстрактного механизма.Есть ли способ, которым я могу сохранить класс абстрактным, но что EF все равно может его как-то сконструировать?Он имеет тип класса в каждой строке, поэтому, по крайней мере, теоретически у него должно быть достаточно информации для создания правильного экземпляра.

Обновление 2 *

ФактическиРешение из первого обновления удаляет исключение, однако оно также дает следующую проблему.А именно, каждый объект, который выходит, является только SurveyItem, а не подклассом, в результате чего получается бесполезный объект.

Ответы [ 2 ]

2 голосов
/ 18 августа 2011

Вы могли бы привести свою коллекцию SurveyItems к правильному типу, прежде чем пытаться запросить их?Возможно, что-то вроде:

string q = "[some search term]";
Context.SurveyItems.Where(x => x is SurveyQuestion).Where(x => x.Field.Contains(q));

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

string q = "[some search term]";
IEnumerable<SurveyQuestion> surveyQuestions = Context.SurveyItems.Where(x => x is SurveyQuestion);
IEnumerable<SurveyQuestion> matchingSurveyQuestions = surveyQuestions.Where(x => x.Field.Contains(q));

Или для еще лучшей читаемости (по крайней мере, в моих глазах - мне нравится более короткий код):

var searchTerm = "[some search term]";
var questions = Context.SurveyItems.Where(si => si is SurveyQuestion);
var matchingQuestions = questions.Where(q => q.Field.Contains(searchTerm));
0 голосов
/ 10 августа 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...