Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用实体框架执行简单查询时出现严重的性能问题_C#_Performance_Entity Framework_Entity Framework 4 - Fatal编程技术网

C# 使用实体框架执行简单查询时出现严重的性能问题

C# 使用实体框架执行简单查询时出现严重的性能问题,c#,performance,entity-framework,entity-framework-4,C#,Performance,Entity Framework,Entity Framework 4,我有一个相当通用的CRUDWebApp,它根据几个数据库表的内容动态生成页面。我使用EntityFramework4.0将这些数据从数据库中提取出来,但是我遇到了严重的性能问题。我已经成功地迭代到一个问题中,这个问题包含的内容足够详细,我可以在下面详细介绍 我有一个包含页面表单列表的表(~200)。每个表单都有一个或多个字段(总计约4000个),每个字段可能有一些参数(总计约16000个) 我在下面附上了我的模型的截图: 关联的实体对象如下所示: public class Form {

我有一个相当通用的CRUDWebApp,它根据几个数据库表的内容动态生成页面。我使用EntityFramework4.0将这些数据从数据库中提取出来,但是我遇到了严重的性能问题。我已经成功地迭代到一个问题中,这个问题包含的内容足够详细,我可以在下面详细介绍

我有一个包含页面表单列表的表(~200)。每个表单都有一个或多个字段(总计约4000个),每个字段可能有一些参数(总计约16000个)

我在下面附上了我的模型的截图:

关联的实体对象如下所示:

public class Form
{
    public int FormID { get; set; }
    public string FormName { get; set; }

    public IList<FormField> FormFields { get; set; }

}

public class FormField
{
    public int FieldID { get; set; }
    public string FieldName { get; set; }
    public int FormID{ get; set; } 

    public IList<FormFieldParameter> FormFieldParameters { get; set; }
    public Form ParentForm { get; set; }

}

public class FormFieldParameter
{
    public int FieldParamID{ get; set; }
    public string Value{ get; set; }
    public int? FieldID { get; set; }

    public FormField ParentField { get; set; }
}
sql事件探查器上显示的此查询的持续时间表明,此查询需要花费很长时间才能运行。查询的有趣之处在于,它根本没有过滤功能——它返回的是整个树!我不明白为什么它会返回所有内容,因为过滤器
myObjectSet。其中(c=>c.FormID==1)
非常明确。实际返回的对象只包含一个条目,这是我所期望的

我的整个数据访问层都存在这个问题,其性能令人震惊。我不知道为什么生成的查询不包含过滤器,也不知道如何告诉它这样做。有人知道答案吗?

TL;DR删除
AsEnumerable
调用,并将其替换为
AsQueryable
调用,它应该可以解决大多数性能问题(实际数据库执行之外的成本很低,这是通过在筛选/加入的列上添加索引来解决的)。 解释实际发生的事情

只要调用
aseneumerable
,您现在就置身于实体框架之外,置身于LINQ to对象的世界中。这意味着在对数据库进行枚举时,它将对数据库执行查询。再次调用
AsQueryable
并不重要,这仅仅意味着您正在针对内存中的结构创建查询

有效的执行是这样的

  • 创建对象查询,包括链接到表单的所有FormFieldProperties
  • 将当前IQueryable实例转换为可枚举实例
  • 针对可枚举实例添加谓词,该实例将仅返回FormID值为1的项
  • 调用ToList,它将源可枚举的所有值复制到列表中
  • 现在,直到第4步,查询实际上还没有查询数据库。当您调用
    ToList
    时,它将执行第一步中的查询(如您所见)。由于返回的数据量和/或丢失的索引可能会提高查询的性能,因此此查询可能很昂贵并且需要一段时间

    一旦查询完成并具体化,它的结果将包装在一个枚举器中

    现在,对每个对象进行迭代和检查,以查看它是否匹配在步骤3中添加的谓词。如果它确实匹配,那么它将返回给迭代它的人(在本例中是ToList函数)

    现在值已经返回,它被添加到使用值创建的列表中


    最后,您从
    ToList
    方法返回一个列表,它完全符合您的要求,但它在内存中而不是数据库中完成了所有这些操作。

    您需要。可以在CreateObjectSet上查询您阅读了吗?哇,谢谢。将.AsQueryable添加到CreateObjectSet的末尾确实有效。我想既然.Where语句返回了一个IQueryable,就没问题了。我想不是!您知道这种行为的原因吗?LINQ查询代码块正是您正在使用的代码吗?或者您是从方法中检索部分内容还是将其传递到方法中?我刚刚测试了它,无法确认是否需要
    AsQueryable()
    (这会让我很困惑)。使用上面的代码创建一个SQL查询,该查询包含预期的WHERE子句:在第二个左外部联接的正下方,我得到一个
    WHERE 1=[Extent1].[FormID]
    。很抱歉,这是一个用于执行筛选的方法调用,我没有确切说明它在做什么。基本上,它将ObjectQuery转换为IEnumerable,执行过滤器,然后再转换回IQueryable。。。我已经阅读了相关的问题,完全有理由解释为什么它失败了。我将用缺失的信息更新我的问题。非常清楚地解释引擎盖下发生的事情,非常感谢。
    EntityConnection myConnection = new EntityConnection("name=myModel");
    
    if(conn.State != ConnectionState.Open) {
        conn.Open();
    }
    ObjectContext context = new ObjectContext("name=myModel");
    context.ContextOptions.LazyLoadingEnabled = false;
    
    ObjectQuery<PageForm> myObjectSet = context.CreateObjectSet<PageForm>()
                                               .Include("FormField.FormFieldParameter");
    
    //Edit: I missed this part out, sorry. In hindsight, this was exactly what was
    //causing the issue.
    IEnumerable<PageForm> myObjectSetEnumerable = myObjectSet.AsEnumerable();
    IQueryable<PageForm> myFilteredObjectSet = myObjectSetEnumerable.Where(c => c.FormID == 1)
                                                                    .AsQueryable();
    
    
    List<PageForm> myReturnValue = myFilteredObjectSet.toList();
    
    SELECT 
    [Project1].[FormID] AS [FormID], 
    [Project1].[FormName] AS [FormName], 
    [Project1].[C2] AS [C1], 
    [Project1].[FormID1] AS [FormID1], 
    [Project1].[FieldID] AS [FieldID], 
    [Project1].[FieldName] AS [FieldName], 
    [Project1].[C1] AS [C2], 
    [Project1].[FieldParamID] AS [FieldParamID], 
    [Project1].[Value] AS [Value], 
    [Project1].[FieldID1] AS [FieldID1]
    FROM ( SELECT 
        [Extent1].[FormID] AS [FormID], 
        [Extent1].[FormName] AS [FormName], 
        [Join1].[FieldID] AS [FieldID], 
        [Join1].[FieldName] AS [FieldName], 
        [Join1].[FormID] AS [FormID1], 
        [Join1].[FieldParamID] AS [FieldParamID], 
        [Join1].[Value] AS [Value], 
        [Join1].[FieldID1] AS [FieldID1], 
        CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) WHEN ([Join1].[FieldParamID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
        CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
        FROM  [dbo].[PageForm] AS [Extent1]
        LEFT OUTER JOIN  (SELECT [Extent2].[FieldID] AS [FieldID], [Extent2].[FieldName] AS [FieldName], [Extent2].[FormID] AS [FormID], [Extent3].[FieldParamID] AS [FieldParamID], [Extent3].[Value] AS [Value], [Extent3].[FieldID] AS [FieldID1]
            FROM  [dbo].[FormField] AS [Extent2]
            LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID] ) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]
    )  AS [Project1]
    ORDER BY [Project1].[FormID] ASC, [Project1].[C2] ASC, [Project1].[FieldID] ASC, [Project1].[C1] ASC