我们在数据库中存储项目,这些项目源自名为 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/