在不使用DataTable(或使用可能具有重复列名称的DataTable)的情况下快速序列化DataReader的最佳方法是什么?

时间:2014-12-09 15:35:09

标签: datatable dapper datareader

我们使用包装IDbConnection / IDbCommand的装饰器广泛地缓存我们的查询。当某些东西调用'ExecuteReader()'时,它使用command.text作为缓存键(例如SELECT * FROM Foo),我们也可以将标记应用于我们的装饰器(例如new FooDbCommand("UserCacheTag"))来管理它。 Reader被转换为DataTable,可以进行明显的序列化,并使用DataTables api(CreateDataReader)进行转换。

这一切都很出色,但有一个缺陷。

如果传入的记录集具有重复的列名,当IDataReader被转换为DataTable时,重复的列名称将以数字递增(例如“PersonID1”)。

我们如何制作IDataReader,它允许重复的列名,可缓存(优选可序列化),然后将其转换回具有原始列名的IDataReader,而不必担心在各州之间更改列名?

其他信息:

我们主要使用Dapper和动态返回类型来避免这些问题。我们想开始更多地使用多重映射。但是,如果在一个查询中多次返回一个类型,例如“Person”,则会出现重复的列名。

例如:

SELECT pEmployee.*, pManager.* 
FROM Persons pEmployee 
LEFT OUTER JOIN Persons pManager ON pManager.PersonID = pEmployee.ManagerPersonID

1 个答案:

答案 0 :(得分:0)

我通过创建自己的DataTable类来完成此操作,该类将原始列名称存储到' Caption' property和我自己的IDataReader,它覆盖GetName()方法以查看模式表。它非常简单,但我确实希望DataTable只有一个“AllowDuplicateColumnNames”和#39; AllowDuplicateColumnNames'属性。

<强> MyDataTable

加载DataReader时,会设置&#39; Caption&#39;属性到原始列名称。创建DataReader时,它将重命名&#39; ColumnName&#39; schematable的属性返回原始名称(当前存储在Caption列中)。

internal class MyDataTable : DataTable
    {
        public MyDataTable()
        {

        }

        /// <summary>
        /// This is a helper method which accounts for duplicate column names.
        /// In the loading process, it will rewrite the caption to be the original column name
        /// which can be a duplicate.
        /// </summary>
        /// <param name="dataReader"></param>
        public void LoadIDataReader(IDataReader dataReader)
        {
            var schemaTable = dataReader.GetSchemaTable();

            base.Load(dataReader);

            for (var i = 0; i < schemaTable.Rows.Count; i++)
            {
                var originalColumnName = schemaTable.Rows[i]["ColumnName"] as string;
                var currentColumnName = this.Columns[i].ColumnName;

                if(originalColumnName != currentColumnName)
                {
                    this.Columns[i].Caption = originalColumnName;
                }
            }
        }

        public IDataReader CreateIDataReader()
        {
            var dataReader = base.CreateDataReader();

            var schemaTable = dataReader.GetSchemaTable();

            for(var i = 0; i < schemaTable.Rows.Count; i++)
            {
                //this is reverse from above
                var originalColumnName = this.Columns[i].Caption;
                var currentColumnName = schemaTable.Rows[i]["ColumnName"] as string;

                if (originalColumnName != currentColumnName)
                {
                    schemaTable.Rows[i]["ColumnName"] = originalColumnName;
                }
            }

            return new MyDataReader(dataReader);
        }
    }

MyDataReader:IDataReader

这接受一个datareader,只是充当GetName()方法的装饰器。

internal class MyDataReader : IDataReader
    {
        private IDataReader _dataReader;

        public MyDataReader(IDataReader dataReader)
        {
            _dataReader = dataReader;
        }

        //other IDataReader methods not included for clarity

        public string GetName(int i)
        {
            return this.GetSchemaTable().Rows[i]["ColumnName"] as string;
        }
    }