c# - 值不能为空;参数名称 : constructor when using SqlExecuteQuery with an abstract Entity model

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

我们在数据库中存储项目,这些项目源自名为 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,而不是一个子类,从而导致一个无用的对象。

最佳答案

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

关于c# - 值不能为空;参数名称 : constructor when using SqlExecuteQuery with an abstract Entity model,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7001976/

相关文章:

c# - 将 ReadAsStreamAsync 与 StreamReader 一起使用时出现 "stream was not readable"ArgumentException

c# - 如何在 Monogame 中全屏启动?

c# - 重构保护条款

c# - 有没有办法测试 SQL 语句以查看是否可以在 C# 中执行?

asp.net-mvc - jquery ui css 不加载和创建 asp.net mvc3 页面问题(意外 token 错误)

c# - 将 3D 点投影到 2D 屏幕坐标

entity-framework - 在 LINQPad 中使用 CreateSourceQuery

entity-framework - 有没有办法让类名与表名不同?

asp.net-mvc - 将数据存储在缓存中 30 分钟 MVC

asp.net-mvc-3 - MVC Paypal 助手错误 : PayPal. Platform.SDK.FATALException