C# 从数据库而不是递归函数检索数据的优化解决方案

C# 从数据库而不是递归函数检索数据的优化解决方案,c#,.net,sql,database,vb.net,C#,.net,Sql,Database,Vb.net,我编写了递归函数从数据库检索父/子样式菜单: <ul> <li> <a href='#'>level1-a</a> <ul> <li> <a href='#'>level2-a1</a> <ul> <li><a href='#'>level3-a11</a></li>

我编写了递归函数从数据库检索父/子样式菜单:

<ul>
  <li>
  <a href='#'>level1-a</a>
    <ul> 
      <li>
      <a href='#'>level2-a1</a>
    <ul>
          <li><a href='#'>level3-a11</a></li>
    </ul>
      </li>
      <li><a href='#'>level2-a2</a></li>
    </ul>
  </li>
  <li><a href='#'>level1-b</a></li>
  <li><a href='#'>level1-c</a></li>
</ul>
C代码: VB.net代码:


是否有任何类型的变量能够存储这样的数据结构以在其中搜索,而不是连续连接到数据库?

理想情况下,您希望一次将所有相关记录检索到内存集合中,然后在递归期间从该内存集合中读取。记录的记忆/缓存范例将使这变得简单,并将数据访问逻辑与业务逻辑分离

首先,创建一个用于检索数据的静态方法,该方法第一次从数据库中获取数据,但在后续调用中使用其内存中的集合。我假设,由于您正在传递fTableName,因此该方法可能用于多个表,也可能不用于多个表,因此缓存能够使用表名上键入的字典一次存储多个表,并分别处理对不同表的请求。警告:未经测试的代码,但应该让您了解:

private static Dictionary<string, DataTable> _menuCache = null;
public static DataRow[] GetMenuLayer(string fTableName, string fID, string fClause)
{
    if (_menuCache == null) _menuCache = new Dictionary<string, DataTable>();
    if (!_menuCache.ContainsKey(fTableName)) { 
        // retrieve all records from the database the first time
        SQLCommand = "SELECT * FROM " + fTableName;
        ...
        _menuCache[fTableName] = result;
    }

    // query appropriate records from the cache
    var dt = _menuCache[fTableName];
    return dt.Select("MenuParent = " + fID + " AND Visible=1 AND " + fClause);
}

这是解决方案方法的粗略概念,可能需要一些调整和实验才能使所有内容正常工作。

您可以将数据放入数据集,然后在递归函数中对数据表使用select方法,如下所示:

private sub getUL_String()
    Dim datasetRecs As New DataSet
    Dim datarowParents As DataRow
    Dim finalStringUL As String = ""
    //FILL your dataset here.
    //Table 0 will be your top level parents.
    //Table 1 will be all records.
    For Each datarowParents In datasetRecs.Tables(0).Rows
        //do processing to datarowFiltered row.
        finalStringUL = "fill in your UL stuff for this record"
        finalStringUL &= getChildren(datasetRecs.Tables(1), datarowParents("id"),    fClause)
    Next

    finalStringUL
End Sub


Private Function getChildren(ByRef datatableROWS As DataTable, ByVal currentID As String, ByVal fClause As String) As String
    Dim currentRow As DataRow
    getChildren = ""
    For Each currentRow In datatableROWS.Select("MenuParent=" & currentID & " and " & fClause)
        //do processing to datarowFiltered row.
        getChildren = "fill in your UL stuff for this record"
        getChildren &= getChildren(datatableROWS, currentRow("id"), fClause)
    Next
End Function

这采用纯字典的方法。 您正在处理的只是Int-id。 我发现数据表速度较慢,体积更大。 这需要一种让所有人都满意的方法

private Dictionary<string, Dictionary<string, List<int>>> dDB = new Dictionary<string, Dictionary<string, List<int>>>();
public List<int> ReadData(string fTableName, string fID, string fCluase)
{
    string key = fTableName + "_" + fCluase;  
    if (dDB.ContainsKey(key))
    {
        Dictionary<string, List<int>> sDB = dDB[key];
        if (sDB.ContainsKey(fID)) return sDB[fID];
        return new List<int>();
    }

    string SQLCommand = "SELECT id, MenuParent FROM " + fTableName + " where Visible=1 AND " + fCluase + " order by MenuParent";

    SqlDataReader DR = new SqlDataReader();
    Dictionary<string, List<int>> nsDB = new Dictionary<string, List<int>>();
    int _id;
    string _fid;
    string _fidLast = string.Empty;
    List<int> _ids = new List<int>();
    while (DR.Read())
    {
        _id = DR.GetInt32(0);
        _fid = DR.GetString(1);
        if (_fid != _fidLast && !string.IsNullOrEmpty(_fidLast))
        {
            nsDB.Add(_fidLast, _ids);
            _ids.Clear();
        }
        _fidLast = _fid;
        _ids.Add(_id);
    }
    nsDB.Add(_fid, _ids);
    dDB.Add(key, nsDB);
    if (nsDB.ContainsKey(fID)) return nsDB[fID];
    return new List<int>();
}

这张桌子有多大?您可以用MenuParent键将表读入字典一次。@因为它很小,不到50行。为什么不使用递归SQL在一条语句中检索此信息。请在dt.rows中解释var dr?什么是var?var是。如果类型在编译时已知,则可以使用var而不是显式声明该类型。该类型实际上是DataRow,因此它与在dt.Rows中表示foreach DataRow dr相同{.如何访问表列?drID返回的“Rows”不是“System.Array”的成员。您发布了C和VB代码,因此我选择了C,因为我更熟悉这两种代码。看起来您最熟悉VB.Net?如果是这样,为什么不坚持使用您熟悉的语言?我可以用VB发布我的答案。您可以lso尝试使用这样的工具自动转换它:你能用你当前正在尝试的代码示例添加注释吗?drID中没有行,因此返回这样的错误似乎很奇怪。也许错误与你注释的部分在不同的代码行上?
private sub getUL_String()
    Dim datasetRecs As New DataSet
    Dim datarowParents As DataRow
    Dim finalStringUL As String = ""
    //FILL your dataset here.
    //Table 0 will be your top level parents.
    //Table 1 will be all records.
    For Each datarowParents In datasetRecs.Tables(0).Rows
        //do processing to datarowFiltered row.
        finalStringUL = "fill in your UL stuff for this record"
        finalStringUL &= getChildren(datasetRecs.Tables(1), datarowParents("id"),    fClause)
    Next

    finalStringUL
End Sub


Private Function getChildren(ByRef datatableROWS As DataTable, ByVal currentID As String, ByVal fClause As String) As String
    Dim currentRow As DataRow
    getChildren = ""
    For Each currentRow In datatableROWS.Select("MenuParent=" & currentID & " and " & fClause)
        //do processing to datarowFiltered row.
        getChildren = "fill in your UL stuff for this record"
        getChildren &= getChildren(datatableROWS, currentRow("id"), fClause)
    Next
End Function
private Dictionary<string, Dictionary<string, List<int>>> dDB = new Dictionary<string, Dictionary<string, List<int>>>();
public List<int> ReadData(string fTableName, string fID, string fCluase)
{
    string key = fTableName + "_" + fCluase;  
    if (dDB.ContainsKey(key))
    {
        Dictionary<string, List<int>> sDB = dDB[key];
        if (sDB.ContainsKey(fID)) return sDB[fID];
        return new List<int>();
    }

    string SQLCommand = "SELECT id, MenuParent FROM " + fTableName + " where Visible=1 AND " + fCluase + " order by MenuParent";

    SqlDataReader DR = new SqlDataReader();
    Dictionary<string, List<int>> nsDB = new Dictionary<string, List<int>>();
    int _id;
    string _fid;
    string _fidLast = string.Empty;
    List<int> _ids = new List<int>();
    while (DR.Read())
    {
        _id = DR.GetInt32(0);
        _fid = DR.GetString(1);
        if (_fid != _fidLast && !string.IsNullOrEmpty(_fidLast))
        {
            nsDB.Add(_fidLast, _ids);
            _ids.Clear();
        }
        _fidLast = _fid;
        _ids.Add(_id);
    }
    nsDB.Add(_fid, _ids);
    dDB.Add(key, nsDB);
    if (nsDB.ContainsKey(fID)) return nsDB[fID];
    return new List<int>();
}