簡體   English   中英

在C#中擴展DataTable

[英]Extend DataTable in C#

SourceManager靜態構造函數遍歷所有模塊/類,並發現實現ISource所有類。 它將實例化其中的每一個並將它們的IEnumerable暴露為名為IEnumerable<ISource> Sources的靜態屬性。 為簡單起見, ISource有兩個屬性, DataTable Table { get; } DataTable Table { get; }string UniqueName { get; } string UniqueName { get; } 實例化時,每個不同的ISource負責從SQL,MDX等填充其Table 。對於我到目前為止所寫的所有ISource ,在實例化時加載Table包含所有DataRow就足夠了。 但是我現在有一種情況,我想用DataRow懶惰地加載Table ,而不是DataRow加載。 我該怎么做呢? 我將通過一個例子來說明。

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()

我已經覆蓋PermissionDataTableRows屬性,以便上面以某種方式獲取與數據庫中的"PermissionName"關聯的權限的值。 未加載其他權限。

我在權限系統中沒有選擇權,我沒有選擇不使用DataTable

編輯:

在我的示例中,我需要覆蓋DataTableRows屬性。 但是, Rows是一個sealedDataRowCollection 因此,在創建像我想要的那樣的最小自定義DataTable實現方面沒有太多可以做的事情。

我不確定我是否理解你使用DataTable的限制,但我過去曾經做過的一件事是我需要“刷新”DataTable中的數據或者使用不同的標准重新填充它是創建一個派生自DataTable的新類其中包括對DataAdapter的引用,其中包含最初用於填充DataTable的連接和選擇信息。

例如,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;
    }
}

然后你的PermissionSource將創建一個LazyDataTable並適當地設置DataAdapter(包括連接和SELECT命令)。 它不會填充DataTable,而是將其返回為空,以便稍后由應用程序代碼填充。 所以你的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"; } }
}

您的主程序代碼將使用PermissionSourceLazyDataTable ,如下所示:

    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的各個部分,以在您的應用程序約束中實現您想要的內容。 如果你可以切換到不同的共享數據模型當然會好得多,但是如果你必須從每個Source返回一個DataTable,那么至少你可以在必要的時候通過子類化返回一個更具功能性的DataTable,就像我演示的那樣這里。 這允許您傳回更多信息,您可以根據需要使用這些信息填充表格。 我仍然鼓勵你研究LinqToSQL以及可能嘗試切換到簡單地傳回一個DbDataReader或類似於我在這里展示的LazyDataTable的其他對象,這將允許你自定義原始查詢(例如通過使用SetSelectParam方法)並且還一次讀取一行中的數據。

希望有所幫助!

它可能不再使用,但這可能會這樣做。

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

我認為你所尋找的東西就像LinqToSql,你的每個ISource都可以返回一個Table而不是一個DataTable。 這將允許您使用您在示例中給出的動態查詢,並僅加載請求的數據,並且僅在需要時加載。 我不知道您是否能夠為所有數據源找到LinqToSql提供程序。 如果這成為問題,您可以嘗試使用實體框架,如前所述。

以下示例通過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"]);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM