一眼就可以告诉我为什么这个动作如此缓慢?

时间:2010-09-17 15:22:45

标签: c# asp.net-mvc linq-to-sql json recursion

由于数据库中没有很多“树节点”,因此最初没有注意到这一点很慢。现在它真的很慢,一眼就看出有什么重大的问题吗?我真的需要优化它,在我重新整理之前,我想知道是否有任何突出的真正的痛点。我已经将缓慢的部分缩小到递归存储库函数,这是本文的最后一件事,我必须定义一些导致它的东西...祝你好运。 (注意:我没有写这个,我只是受到了伤害控制,我试图绕过它。)

首先要理解的一些事情:

JsTreeNode定义:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace App.Models
{
    public class JsTreeNode
    {
        public JsTreeNode()
        {
        }

        public Attributes attributes { get; set; }
        public Data data { get; set; }
        public string state { get; set; }
        public List<JsTreeNode> children { get; set; }
    }

    public class Attributes
    {
        public string id { get; set; }
        public string rel { get; set; }
        public string mdata { get; set; }
        public string href { get; set; }
        public string complete { get; set; }
        public string edit { get; set; }
        public string title { get; set; }
        public string resourceAccountID { get; set; }
    }

    public class Data
    {
        public string title {get;set;}
        public string icon {get;set;}
    } 
}

现在TreeNodes表定义:

CREATE TABLE [dbo].[TreeNodes](
    [TreeNodeID] [int] IDENTITY(1,1) NOT NULL,
    [TreeID] [int] NOT NULL,
    [ParentNodeID] [int] NULL,
    [ResourceID] [int] NULL,
    [NodeOrder] [int] NOT NULL,
    [NodeName] [nvarchar](250) NOT NULL,
    [CreateBy] [int] NULL,
    [CreateDate] [datetime] NULL,
    [ModifyBy] [int] NULL,
    [ModifyDate] [datetime] NULL,
    [TreeRevisionID] [int] NULL,

树修订表定义...

CREATE TABLE [dbo].[TreeRevisions](
    [TreeRevisionID] [int] IDENTITY(1,1) NOT NULL,
    [TreeID] [int] NOT NULL,
    [Notes] [text] NULL,
    [CreatedBy] [int] NOT NULL,
    [CreatedDate] [datetime] NOT NULL,
    [ModifyBy] [int] NOT NULL,
    [ModifyDate] [datetime] NOT NULL,

现在这里是Get Action本身需要20-25秒,你可以在那里看到它调用了我已经在这个线程中进一步定义的存储库函数。

public ContentResult Get(string target,int id)
        {
            int revisionID = Convert.ToInt32(Request.QueryString["revisionID"]);
            int tempUserID = (Request.QueryString["userID"] != null)
                                 ? Convert.ToInt32(Request.QueryString["userID"])
                                 : UserID;

            var nodesList = new List<JsTreeNode>();
            if(target.Contains("tree"))
            {
                tree tree = _treeRepository.GettreeByID(id);

                var cnode = new JsTreeNode
                                       {
                                           attributes = new Attributes {id = "0",title = tree.treeName},
                                           data = new Data {title = tree.treeName},
                                           children = _treeNodesRepository.GetNodesBytreeID(id, null,revisionID),
                                           state = "open"
                                       };
                cnode.attributes.rel = "root";
                cnode.attributes.mdata = "{draggable : true}";
                nodesList.Add(cnode);
            }
            else
            {

                var trees = _CategoryRepository.getAlltreesByCategoryID(id);

                IQueryable<tree> custom;

                if(revisionID != 0)
                {
                    custom = from c in trees
                              where
                                  c.AccountID == AccountID
                              select c;
                } else
                {
                    custom = from c in trees
                             where
                                 c.AccountID == AccountID && c.PublishedRevisionID != null && c.PublishedRevisionID != 0
                             select c;
                }

                var acme = from c in trees where c.AccountID == acmeContent.acmeID select c;

                foreach (var tree in (custom.Count() > 0) ? custom : acme)
                {
                    if(revisionID == 0 && tree.PublishedRevisionID == null) continue;
                    int tempRev = (revisionID != 0) ? revisionID : (int)tree.PublishedRevisionID;

                    if(custom.Count() == 1)
                    {
                        var tempNodes = _treeNodesRepository.GetNodesBytreeID(tree.treeID, null, tempUserID, tempRev);
                        nodesList.AddRange(tempNodes);
                    }
                    else
                    {
                        var cnode = new JsTreeNode();
                        cnode.attributes = new Attributes { id = tree.treeID.ToString(), title = tree.treeName };
                        cnode.data = new Data { title = tree.treeName };
                        cnode.children = _treeNodesRepository.GetNodesBytreeID(tree.treeID, null, tempUserID, tempRev);
                        cnode.attributes.rel = "Folder";

                        nodesList.Add(cnode);
                    }
                }
            }

            var ser = new JavaScriptSerializer();
            string res = ser.Serialize(nodesList);

            return Content(res,"application/json");

        }

...最后,'罪魁祸首',看看它自称:

public List<JsTreeNode> GetNodesByTreeID(int TreeID, int? parentID,int userID, int revisionID)
        {

            IQueryable<UserTreeNode> TreeNodes = GetAllTreeNodesByTreeIDAndParentNodeID(TreeID, parentID,userID,revisionID);
            List<JsTreeNode> nodesList = new List<JsTreeNode>();



            foreach (UserTreeNode node in TreeNodes)
            {
                string nodeName = node.Node.NodeName.Replace("'", "&#39;");

                JsTreeNode cnode = new JsTreeNode
                                       {
                                           attributes = new Attributes
                                                            {id = node.Node.TreeNodeID.ToString(),
                                                            title = nodeName},
                                           data = new Data {title = nodeName}
                                       };

                if(node.Node.ResourceID != null)
                {
                    cnode.attributes.complete = (node.IsComplete) ? "true" : "false";
                    cnode.attributes.rel = ResourceTypes.ReturnResourceTypeName(_resourceRepository.getResourceByID(Convert.ToInt32(node.Node.ResourceID)).ResourceTypeID);
                    cnode.attributes.href = "/resource/" + node.Node.ResourceID + "?minimal=true&nodeID=" + node.Node.TreeNodeID.ToString();
                }
                else
                {
                    var nodeChildren = GetNodesByTreeID(TreeID, node.Node.TreeNodeID,userID,revisionID);
                    if (nodeChildren.Count > 0)
                        cnode.children = nodeChildren;

                    cnode.attributes.complete = "false";
                    cnode.attributes.rel = "Folder";
                }

                nodesList.Add(cnode);
            }

            return nodesList;
        }

2 个答案:

答案 0 :(得分:1)

此过程中我最好的建议是启动Visual Studio Profiler(与Sql Profiler不同)并了解该过程的哪个部分实际花费了大量时间。

如果您没有配置分析器的VS版本,则可以check out this search用于其他应用程序分析器。首先是针对asp.net。

答案 1 :(得分:1)

您发布的表格包含标识列,但这些列未指定为主键(至少不在您显示的示例中)。

我无法找到您的LinqToSql查询结构,因此很难评论查询的优化。我怀疑它包含在GetAllTreeNodesByTreeIDAndParentNodeID

在Sql Studio中单独运行这些语句:

sp_help TreeNodes
sp_help TreeRevisions

第6个结果集是索引列表...在您的情况下 - 此结果集可能为空。如果你没有索引,那就是你的第一个问题。

乍一看,我建议添加这些索引

TreeNodes
  TreeNodeID primary key
x TreeID nonclustered index
  TreeRevisionID nonclustered index
* TreeID, ParentNodeId nonclustered index

TreeRevisions
  TreeRevisionID primary key
  TreeID nonclustered index

标有*的那个可能是您当前查询方式中最重要的一个。列顺序在多列索引中很重要。

还考虑通过TreeId获取整个树并在内存中进行更多过滤/整形。这将避免数据库中的递归/重复查询。使用这种方法,用x标记的索引是重要的。