13

クラスの静的コンストラクターSourceManagerは、すべてのモジュール/クラスを調べて、実装するすべてのクラスを検出しますISourceIEnumerableこれらのそれぞれをインスタンス化し、それらの 1 つをという静的プロパティとして公開しますIEnumerable<ISource> Sources。簡単ISourceにするために、 と の 2 つのプロパティがDataTable Table { get; }ありstring UniqueName { get; }ます。インスタンス化されると、それぞれがSQL、MDX などからデータを取り込むISource責任があります。これまでに記述したすべての については、インスタンス化されたときにすべての をロードするだけで十分です。ただし、 with をすべて前もってロードするのではなく、遅延してロードしたい状況があります。どうすればいいですか? 例を見ていきます。TableISourceTableDataRowTableDataRow

PermissionSource実装しISourceます。を持つそのTableプロパティにはprivate set、 の値が与えられますnew PermissionDataTable()。それUniqueName"Permissions"です。現時点では、データベースからこのTableプロパティに読み込まれる権限はありません。

ISource permissionSource = SourceManager.Sources.
    Where(s => "Permission".Equals(s.UniqueName)).First();

これで を取得できましたPermissionSourceが、インターフェイスを介して. 許可を取りましょう。

DataRow row = permissionSource.Table.Rows.Cast<DataRow>().
    Where(r => r["PermissionName"].Equals("PermissionName")).First()

上記が何らかの形で、データベースに関連付けられているアクセス許可の値を取得するように、Rowsプロパティをオーバーライドしました。他の権限は読み込まれません。PermissionDataTable"PermissionName"

権限システムには選択肢がなく、. を使用しないという選択肢もありませんDataTable

編集:

私の例では、のRowsプロパティをオーバーライドする必要がありますDataTableRowsただし、DataRowCollectionは ですsealed。したがって、私がやりたいような最小限のカスタム DataTable 実装を作成するという点では、できることはあまりありません。

4

4 に答える 4

2

DataTable を使用する際の制限を理解しているかどうかはわかりませんが、過去に DataTable のデータを「更新」するか、別の基準を使用してデータを再入力する必要があったときに行ったことの 1 つは、DataTable から派生した新しいクラスを作成することです。これには、DataTable を埋めるために最初に使用された接続および選択情報を含む DataAdapter への参照が含まれます。

たとえば、DataTable サブクラスは次のLazyDataTableコードのようになります。行にアクセスするいくつかの異なる方法を追加したことに注意してください。この投稿の最後近くにあるPermissionSourceと メインのコードを見れば、より理解が深まるかもしれません。また、すべてのケースでデータベース接続を適切に開いたり閉じたりすることに関するすべての詳細が含まれているわけではないことに注意してください。それをどのように処理するかは、データベース アクセスのモデル (接続プーリング、共有接続など) によって異なります。Program

//using System.Data.Common;
public class LazyDataTable : DataTable {
    protected DbDataAdapter Adapter { get; set; }

    public LazyDataTable(DbDataAdapter a) {
        Adapter = a;
    }
    /// <summary>
    /// Save changes back to the database, using the DataAdapter
    /// </summary>
    public void Update() {
        Adapter.Update(this);
    }
    /// <summary>
    /// Fill this datatable using the SelectCommand in the DataAdapter
    /// The DB connection and query have already been set.
    /// </summary>
    public void Fill() {
        Adapter.Fill(this);
    }

    /// <summary>
    /// read and return one row at a time, using IEnumerable syntax
    /// (this example does not actually add the row to this table, 
    /// but that can be done as well, if desired.
    /// </summary>
    public IEnumerable<DataRow> LazyReadRows() {
        using (var reader = OpenReader()) {
            //Get the schema from the reader and copy it to this table.
            var schema = reader.GetSchemaTable();
            var values = new object[schema.Columns.Count];
            while (reader.Read()) {
                reader.GetValues(values);
                var row = schema.NewRow();
                row.ItemArray = values;
                yield return row;
            }
        }
    }

    /// <summary>
    /// Fill one row at a time, and return the new row.
    /// </summary>
    public DataRow ReadRow() {
        if (_reader == null || _reader.IsClosed) 
            _reader = OpenReader();
        //Get the schema from the reader and copy it to this table.
        if (this.Columns.Count == 0) 
            this.Columns.AddRange(_reader.GetSchemaTable().Columns.Cast<DataColumn>().ToArray());
        if (!_reader.Read()) {
            _reader.Dispose();
            return null;
        }
        var values = new object[_reader.FieldCount];
        _reader.GetValues(values);
        return this.Rows.Add(values);
    }
    private DbDataReader _reader = null;

    private DbDataReader OpenReader() {
        OpenConnect();
        return Adapter.SelectCommand.ExecuteReader();
    }

    private void OpenConnect() {
        var cn = Adapter.SelectCommand.Connection;
        if (cn.State == ConnectionState.Closed)
            cn.Open();
    }

    /// <summary>
    /// Change a Parameter in the SelectCommand, to filter which rows to retrieve.
    /// </summary>
    public void SetSelectParam(string name, object value) {
        var selparams = Adapter.SelectCommand.Parameters;
        selparams[name].Value = value;
    }
}

次に、DataAdapter (接続と SELECT コマンドを含む) をPermissionSource作成して適切に設定します。LazyDataTableDataTable は埋められませんが、代わりに空が返され、後でアプリケーション コードによって埋められます。したがって、PermissionSource以下のコードのようなものかもしれません。例としてデータ オブジェクトを使用しましSystem.Data.OleDbたが、任意の ADO プロバイダーを使用できます。

interface ISource {
    public DataTable Table { get; }
    string UniqueName { get; }
}

public class PermissionSource : ISource {
    /// <summary>
    /// Loads a DataTable with all of the information to load it lazily.
    /// </summary>
    public DataTable Table { 
        get { 
            const string SELECT_CMD = "SELECT * FROM [Permissions] WHERE ([PermissionName] IS NULL OR [PermissionName]=@p1) AND [OtherProperty]=@p2";
            var conn = new OleDbConnection("...ConnectionString...");
            var selectCmd = new OleDbCommand(SELECT_CMD, conn);
            selectCmd.Parameters.AddWithValue("p1", "PermissionName");
            selectCmd.Parameters.AddWithValue("p2", 0);
            var adapter = new OleDbDataAdapter(selectCmd);
            var builder = new OleDbCommandBuilder(adapter); //used to generate the UPDATE and DELETE commands...
            adapter.UpdateCommand = builder.GetUpdateCommand(); //etc.
            //Do NOT fill the table here. Instead, let the caller fill it.
            return new LazyDataTable(adapter);
        }
    }
    public string UniqueName { get { return "Permission"; } }
}

メイン プログラム コードでは、次のようPermissionSourceに andを使用LazyDataTableします。

    static class Program {
    void GetPermissions() {
        ISource permissionSource = SourceManager.Sources.
            Where(s => "Permission".Equals(s.UniqueName)).First();

        var table = permissionSource.Table as LazyDataTable;
        table.SetSelectParam("PermissionName", "Admin");

        //If you want to fill ALL rows in one step:
        table.Fill(); 

        // OR If you want to fill one row at a time, and add it to the table:
        DataRow row;
        while(null != (row = table.ReadRow())) {
            //do something with each individual row. Exit whenever desired.
            Console.WriteLine(row["PermissionName"]);
        }

        // OR If you prefer IEnumerable semantics:
        DataRow row = table.LazyReadRows().FirstOrDefault(someValue.Equals(row["columnname"]));

        //OR use foreach, etc. Rows are still ONLY read one at a time, each time IEnumerator.MoveNext() is called.
        foreach (var row in table.LazyReadRows())
            if (row["someColumn"] == "someValue")
                DoSomething(row["anothercolumn"]);
    }
}

ここに示す LazyDataTable の一部を組み合わせて、アプリケーションの制約内で目的を正確に達成することができます。もちろん、データ共有の別のモデルに切り替えることができれば、はるかに優れていますが、各ソースから DataTable を返す必要がある場合は、少なくとも、必要に応じて、私が示したようにサブクラス化することで、より機能的な DataTable を返すことができます。ここ。これにより、より多くの情報を返すことができます。これを使用して、必要に応じてテーブルを埋めることができます。LinqToSQL を調べて、DbDataReader またはここで示した LazyDataTable に似た他のオブジェクトを単純に返すように切り替えて、元のクエリをカスタマイズすることをお勧めします (たとえば、SetSelectParamメソッド) また、一度に 1 行のデータを読み取ることもできます。

それが役立つことを願っています!

于 2012-12-04T09:30:45.877 に答える
0

あなたが探しているのはLinqToSqlのようなものだと思います。ここでは、各ISourceがDataTableではなくTableを返す可能性があります。これにより、例で示したような動的クエリを使用し、必要な場合にのみ、要求されたデータのみを読み込むことができます。すべてのデータソースのLinqToSqlプロバイダーを見つけることができるかどうかはわかりません。それが問題になる場合は、前に提案したように、EntityFrameworkの使用を試みることができます。

于 2012-12-04T09:08:07.927 に答える
0

次の例では、ExtendedProperties プロパティを使用してタイムスタンプ値を DataTable に追加します。

private void GetAndSetExtendedProperties(DataTable myTable){
 // Add an item to the collection.
 myTable.ExtendedProperties.Add("TimeStamp", DateTime.Now);
 // Print the item.
Console.WriteLine(myTable.ExtendedProperties["TimeStamp"]);
}
于 2013-05-21T11:29:20.117 に答える
0

もう使い物にならないかもしれませんが、これで十分でしょう。

public interface ISource
{
    DataTable Table { get; }
    string Name { get; set; }
}

public class MySource : ISource
{
    private DataTable table;
    public DataTable Table
    {
        get
        {
            if (table == null)
                // Initialize your data.
                table = new System.Data.DataTable();
            return table;
        }
        private set
        {
            this.table = value;
        }
    }
    public string Name { get; set; }
}
于 2012-10-11T14:16:44.880 に答える