【问题标题】:Value cannot be null; Parameter Name: constructor when using SqlExecuteQuery with an abstract Entity model值不能为空;参数名称:使用带有抽象实体模型的 SqlExecuteQuery 时的构造函数
【发布时间】:2011-08-09 19:42:57
【问题描述】:

我们将项目存储在我们的数据库中,这些项目派生自一个名为 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 并创建一个继承AbstractSurveyItem 的新类SurveyItem 时,此问题已得到解决。然后它可以完美地创建 SurveyItem 对象。

但是,我不认为这是一个真正的解决方案,因为您现在失去了抽象机制的安全性和特性。有没有办法可以保持类抽象但 EF 仍然可以以某种方式构造它?它在每一行中都有类的类型,所以理论上至少它应该有足够的信息来创建正确的实例。

更新 2*

事实上,第一次更新的解决方案确实消除了异常,但它也带来了以下问题。即每一个出来的对象都只是一个SurveyItem,而不是一个子类,导致一个无用的对象。

【问题讨论】:

    标签: c# entity-framework asp.net-mvc-3 ef-code-first


    【解决方案1】:

    您可以在尝试查询之前将您的 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));
    

    【讨论】:

    • 这确实可行。尽管在我们的例子中,我们还想在 SurveyHeaderItem 的字段中进行搜索。此外,我们还需要确切地知道它是什么类,而仅仅创建一个 SurveyQuestion 的实例是不行的。我们也可以在手动尝试将 SurveyQuestion 投射到更具体的实例中进行选择。但应该是 EF 应该接受这个。我会给你赏金。
    【解决方案2】:

    您可以使用每个层次结构的表,它使用鉴别器来分隔类型,然后您有一个要查询的表,而且在您的情况下,它应该会影响性能。但是,数据库表未规范化,所有字段都应该可以为空。

    【讨论】:

      猜你喜欢
      • 2015-07-25
      • 2019-11-26
      • 1970-01-01
      • 2017-06-06
      • 1970-01-01
      • 1970-01-01
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多