在C#中扩展DataTable
类SourceManager
静态构造函数遍历所有模块/类,并发现实现ISource
所有类。 它将实例化其中的每一个并将它们的IEnumerable
暴露为名为IEnumerable 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
。 其具有private set
Table
属性的值为new PermissionDataTable()
。 它的UniqueName
是"Permissions"
。 截至目前,没有权限从数据库加载到此Table
属性。
ISource permissionSource = SourceManager.Sources. Where(s => "Permission".Equals(s.UniqueName)).First();
现在我们已经获得了PermissionSource
,但是通过一个接口。 我们得到一个许可。
DataRow row = permissionSource.Table.Rows.Cast(). Where(r => r["PermissionName"].Equals("PermissionName")).First()
我已经覆盖PermissionDataTable
的Rows
属性,以便上面以某种方式获取与数据库中的"PermissionName"
关联的权限的值。 未加载其他权限。
我在权限系统中没有选择权,我没有选择不使用DataTable
。
编辑:
在我的示例中,我需要覆盖DataTable
的Rows
属性。 但是, Rows
是一个sealed
的DataRowCollection 。 因此,在创建像我想要的最小自定义DataTable实现方面没有太多可以做的事情。
我不确定我是否理解你使用DataTable的限制,但我过去曾经做过的一件事是当我需要“刷新”DataTable中的数据或者使用不同的标准重新填充它时,创建一个从DataTable派生的新类其中包括对DataAdapter的引用,其中包含最初用于填充DataTable的连接和选择信息。
例如,DataTable子类可能看起来像下面的LazyDataTable
代码。 请注意,我添加了几种访问行的不同方法。 在查看本文末尾附近的PermissionSource
和主程序代码后,它们可能更有意义。 此外,请注意,我没有包括与在任何情况下正确打开和关闭数据库连接相关的所有细节。 您如何处理这将取决于您的数据库访问模型(例如连接池,共享连接等)。
//using System.Data.Common; public class LazyDataTable : DataTable { protected DbDataAdapter Adapter { get; set; } public LazyDataTable(DbDataAdapter a) { Adapter = a; } /// /// Save changes back to the database, using the DataAdapter /// public void Update() { Adapter.Update(this); } /// /// Fill this datatable using the SelectCommand in the DataAdapter /// The DB connection and query have already been set. /// public void Fill() { Adapter.Fill(this); } /// /// 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. /// public IEnumerable 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; } } } /// /// Fill one row at a time, and return the new row. /// 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().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(); } /// /// Change a Parameter in the SelectCommand, to filter which rows to retrieve. /// 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 { /// /// Loads a DataTable with all of the information to load it lazily. /// 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
和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的各个部分,以在您的应用程序约束中实现您想要的内容。 如果你可以切换到不同的共享数据模型当然会好得多,但是如果你必须从每个Source返回一个DataTable,那么至少你可以在必要时通过inheritance它来返回一个更具function性的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都可以返回一个表而不是一个DataTable。 这将允许您使用您在示例中给出的动态查询,并仅加载所请求的数据,并且仅在需要时加载。 我不知道您是否能够为所有数据源找到LinqToSql提供程序。 如果这成为问题,您可以尝试使用Entity Framework,如前所述。
以下示例通过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"]); }