1

データベースからこの親子スタイル メニューを取得する再帰関数を作成しました。

<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コードが提供されています):

my_function (ID){
 WHILE (read_from_table){
   PRINT data
     my_function(child_id)
 }
}

C#コード: http://pastebin.com/hsqhYF72
VB.netコード: http://pastebin.com/HnyrYnab

データベースに継続的に接続する代わりに、そのようなデータ構造を格納して内部を検索できる変数のタイプはありますか?

4

3 に答える 3

1

理想的には、関連するすべてのレコードを一度にメモリ内コレクションに取得し、再帰中にそのメモリ コレクションから読み取る必要があります。レコードのメモ化/キャッシュのパラダイムにより、これが簡単になり、データ アクセス ロジックがビジネス ロジックから分離されます。

static最初に、最初にデータベースからデータを取得するデータ取得メソッドを作成しますが、その後の呼び出しではメモリ内コレクションを利用します。を渡しているのでfTableName、このメソッドは複数のテーブルで使用される場合と使用されない場合があるため、キャッシュは一度に複数のテーブルを格納でき (Dictionaryテーブル名のキーを使用)、異なるテーブルへのリクエストを個別に処理すると想定しています. (警告: テストされていないコードですが、アイデアが得られるはずです):

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);
}

このメソッドはであるためstatic、そのデータはリクエスト/レスポンス サイクル全体で保存されますが、レスポンス間では保存されません。したがって、これにより、メニューの生成が単一のデータベース呼び出しに削減されますが、ページがロードされるたびに、データは引き続き新しくロードされます。メニュー データが比較的静的である場合は、.NET キャッシュを使用して次のレベルに進むことができます。タイムアウトを指定すると、データベースの結果が保存されます (たとえば、一度に 30 分間更新する前)。その後、1 回のデータベース呼び出しだけでページを何度も読み込むことができます。

GenerateNestedMenusデータベースからデータを取得するコードは、代わりGetMenuLayerに適切なパラメーターを使用して を呼び出します。次に、このメソッドは、可能な方法でデータを取得する責任があります (前者のメソッドは、取得方法を気にする必要はありません)。バックグラウンドで、テーブルが初めてfTableName要求されると、テーブル全体がローカル メモリ キャッシュにダウンロードされます。次に、そのメモリ キャッシュは、パラメーターに従って後続の呼び出しでクエリされ、反復可能な結果行が返されます (これは、基本ロジックの非常に小さなサブセットしか理解できfClauseないため、動的フィルター ロジックがそれほど複雑ではないことを前提としています)。dt.Select(T-SQL

public string GenerateNestedMenus(string fTableName, string fID, string fClause)
{

    DataRow[] dt = GetMenuLayer(fTableName, fID, fClause);

    int i = 0;
    string temp = null;
    for (i = 0; i <= dt.Length - 1; i++) {
        if (Convert.ToInt32(ChildCounter(fTableName, dt[i]["id"])) > 0) {
            temp = "<li>" + Constants.vbCrLf + "<a href='#'>" + Strings.Trim(dt[i]["MenuName"]) + "</a>" + Constants.vbCrLf + Constants.vbTab + "<ul> ";
            _temp += temp;
            GenerateNestedMenus2("menus", dt[i]["id"], fClause);
            _temp += Constants.vbTab + "</ul>" + Constants.vbCrLf + Constants.vbTab + "</li>" + Constants.vbCrLf;
        } else {
            //For rows they have not child
            temp = Constants.vbTab + "<li><a href='#'>" + Strings.Trim(dt[i]["MenuName"]) + "</a>" + "</li>" + Constants.vbCrLf;
            _temp += temp;
            GenerateNestedMenus2("menus", dt[i]["id"], fClause);

        }
    }
    return _temp;
}

これは解決策へのアプローチの大まかなアイデアであり、すべてを機能させるには微調整と実験が必要になる場合があります。

于 2013-01-31T14:05:17.040 に答える
1

データをデータセットに取得し、次のように再帰関数のデータテーブルで 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
于 2013-01-31T14:38:37.827 に答える
0

これは、純粋な Dictionary アプローチを採用しています。
処理しているのは Int (id) だけです。
DataTables は遅くてかさばります。
そして、すべての MenuParent を取得するというアプローチを取ります。

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>();
}
于 2013-01-31T15:49:37.273 に答える