使用继承的自定义IDataReader从IDbCommand中读取数据

10
我创建了一个自定义类,继承了 IDataReader 接口,并成功地使用该自定义类实现了一个使用 C# 对象而不是 DataTable 的自定义 ServerWriter sqlBulkCopy
如我所料,这证明是一个更有效的方法。
现在我正在尝试进行Read操作,但遇到了一些问题。
这是我的IDataReader:
// get Server Data Table Object IDataReader
public class GetSDTOIDataReaderM : IDataReader
{
   //private IEnumerator<MyTestObject> enumerator = null;

   public MySqlDbTableObject.Raw.SqlDbTableM propSqlTbl { get; set; }

   // implementing relevant GetValue(), filedCount() and Read()
   // this is how I did with WriteToServer
   public bool Read()
   {
       return ++this.ReaderRowsCount < this.propSqlTbl.Table.ElementAt(0).Row.Count;
   }
}

该对象与其对应的SqlTable完全映射。

在我的自定义IDataReader上设置一个执行ExecuteReader()方法的SqlCommand不起作用,所以我尝试使用IDbCommand,然后它编译过了,但是当我尝试读取数据时,仍然会出现以下错误:

无法将类型为“System.Data.SqlClient.SqlDataReader”的对象强制转换为类型“GetSDTOIDataReaderM”。

this.propComunicator.AsGetSDTOCom().SqlCmd.Connection.Open();
//this is the line of the error
using (this.propComunicator.AsGetSDTOCom().SDTOIDataReader = (Raw.Comunicator.Raw.GetSDTOIDataReaderM)this.propComunicator.AsGetSDTOCom().SqlCmd.ExecuteReader())
{

}

在向服务器写入数据时已经起作用,我该如何正确地使用 Read 实现它呢?

更新

关于我的模块还有更多信息。

public class TransManSF
{
    public enum CommunicatorTypeS { Reader, Adapter, GetSDTOReaderA, SqlBcpyIDataReaderM };
    public enum DbIntreactorTypeS { TableObject, TableRowObject, Sproc, SqlCmd };
    public enum SqlCmdActionS { NotSet, SELECT, INSERT, UPDATE, DROP };
    public enum SqlCmdStrSelModeS { Single, RecentEfected, count, All, Top10, Top100, Top1000 };
    public enum SqlCmdStrSelOrderByS { NotSet = 0, Asc = 1, Desc = 2 };
    public enum SqlCmdStrSetterModeS { Single, Multi};
    public enum STDOTypeS { NotSet, ServerWriter, ServerTableGetter, ServerBiDerctional }
    public enum SprocTypeS { Sproc, TvPSrpoc }
    public enum TransExecActionTypeS { WriteToServer, GetTable }
}

public static TransMan.Raw.ComunicatorCLF.BulkCopyComSCL AsBulkCopyCom(this TransMan.Raw.ComunicatorCLF.ComunicatorM SelfCom)
{
    return (TransMan.Raw.ComunicatorCLF.BulkCopyComSCL)SelfCom;
}

virtual public void ExecuteTransaction(AppMods.DataBase.TransManSF.TransExecActionTypeS parSelectedTransType, TransManSF.SqlCmdStrSelOrderByS parExecOrderByS, int OrderByOrdinal = 0)
{
    if (parSelectedTransType == AppMods.DataBase.TransManSF.TransExecActionTypeS.WriteToServer)
        using (this.propComunicator.AsBulkCopyCom().Conn)
        {
            this.propComunicator.AsBulkCopyCom().Conn.Open();
            using (var IDRdrServerWriter = this.propComunicator.AsBulkCopyCom().ServerWriter)
            {
                var Eng = this.propExecuter.AsBulkCopyExec().Engine;
                Eng.BulkCopyTimeout = 240;
                Eng.WriteToServer(IDRdrServerWriter);

            }
            this.propComunicator.AsBulkCopyCom().Conn.Close();
        }
    else if (parSelectedTransType == AppMods.DataBase.TransManSF.TransExecActionTypeS.GetTable)
    {
        var DreaderCom = this.propComunicator.AsDReaderCom();
        using (DreaderCom.SqlCmd.Connection)
        {
            DreaderCom.SqlCmd.Connection.Open();
            using (DreaderCom.Driver = DreaderCom.SqlCmd.ExecuteReader())
            {
                if (DreaderCom.Driver.HasRows) while (DreaderCom.Driver.Read())
                    {
                        for (int i = 0; i < DreaderCom.Driver.FieldCount; i++)
                        {
                            var CurRdrColumn = DreaderCom.Driver.GetValue(i);
                            this.Table[i, 0] = CurRdrColumn;
                        }
                    }
            }
            DreaderCom.SqlCmd.Connection.Close();
        }                           
    }
}


public struct customComConfgBulkCopyA
{
      public TransManSF.CommunicatorTypeS ComType;
      public customComConfgBulkCopyA(TransManSF.CommunicatorTypeS ctrComType = TransManSF.CommunicatorTypeS.SqlBcpyIDataReaderM)
      {
          this.ComType = ctrComType;
      }
}
public sealed class BulkCopyComSCL :CustomComA
{
     public new Configurations.comunicator.customComConfgBulkCopyA Meta;
     public SqlConnection Conn { get; set; }
     public Raw.SqlBcpyIDataReaderM ServerWriter { get; set; }
}

public class SqlDbTableM : SqlDB1stTransA
{
     virtual public DbSchema.Raw.TableDictionaryA TableDict { get; set; }
     public virtual new TransMan.Raw.Configurations.SDB1stTransConfgF.SDTOMetaA Meta { get; set; }
     virtual public Raw.ColumnSetsCollM Table { get; set; }
     public override TransMan.Raw.ComunicatorCLF.ComunicatorM propComunicator
     {
         get
         {
              return base.propComunicator;
         }
         set
         {
              base.propComunicator = value;
         }
     }
     public override TransMan.Raw.Executers.ExecuterM propExecuter
     {
         get
         {
             return base.propExecuter;
         }
         set
         {
             base.propExecuter = value;
         }
     }
     public SqlDbTableM(TransManSF.STDOTypeS ctrTransType)
            : base(TransManSF.DbIntreactorTypeS.TableObject) { }
}

public sealed class GetSDTOComSCL : CustomComA
{
    public new Configurations.comunicator.customComConfgGetSDTOReaderA Meta;
    public new IDbCommand SqlCmd;
    public Raw.GetSDTOIDataReaderM SDTOIDataReader { get; set; }
}

你的更新中仍然缺少AsGetSDTOCom()。看起来它返回的是SqlCommand而不是IDbCommand。你能展示一下代码的这部分吗? - Harsh Shah
1
看起来你的方法有点偏差。除非你正在编写自己的数据库驱动程序,否则你不应该真正实现IDataReader。你能否更具体地说明你在这里尝试实现什么? - Randy
@Randy,与我的意图最接近的是由@jods建议的IDbProviderFactory - Jbob Johan
@Randy,如果成功实现/编码,结果可能是一个“一行代码”数据库事务方法......几乎涵盖了任何场景。 - Jbob Johan
听起来你想要构建一个DAL(数据访问层)-- 你是想要像这样做一些事情吗:DbProvider.GetProvider("ConnectionName").ExecuteQuery("sp_MyStoredProcedure", args)? - Randy
显示剩余4条评论
1个回答

6
简短回答:您无法更改由SqlCommand.ExecuteReader()返回的类型。它将始终是SqlDataReader的实例。
各种解决方案:
1. 在有意义的地方包装事物。如果你走极端,可以编写自己的IDbProviderFactory,用自己的连接、命令和数据读取器类来包装SqlConnection。例如,这就是Miniprofiler所做的,以记录所有SQL请求并收集执行时间、行数等信息。源代码 或者您可以使用某种扩展方法仅包装读取器,例如:sqlCmd.ExecuteReader().AsMyReader()AsMyReader()的作用是返回一个自定义的IDataReader,它会执行您的操作并委托给实际的SqlDataReader
2. 使用现有的帮助库。我不太确定您在读取内容时要实现什么,但是在这个领域存在很好的库。没有必要自己想出解决方案。 FastMember (Nuget)允许在任何IEnumerable<T>DataTable周围创建IDataReader接口。它被设计为易于使用,作为SqlBulkCopy的种子,可轻松将数据插入到SQL Server表中以获得最大性能。

此外,为了简化执行SQL查询和解析其结果,Dapper非常出色。


看第一个链接,自定义的 DbCommand 是解决方案核心的起点吗?如果我使用它作为命令,是否会让自定义的 IDataReader 更容易实现呢?public class ProfiledDbCommand : DbCommand, ICloneable - Jbob Johan
不,一切的开始都是DbProviderFactory。它创建了自己的DbConnection,这些连接又创建了自己的DbCommand,而这些命令最终会创建自己的DbDataReader - jods
好的,我将继续从源代码“DbProviderFactory”进行调查。非常感谢您! - Jbob Johan
仍然使用标准的 SqlDataReader,没有时间去实现/测试最接近我的意图的建议,例如 IDbProviderFactory - Jbob Johan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接