是否可以从方法中 return OleDbDataReader 对象?

Is it possible to return an OleDbDataReader object from a method?

是否可以从函数中 return OleDb.OleDbDataReader 对象?

如果是,你会怎么做?

我当前的代码 return 是数据 reader 对象,但是当我尝试读取该对象时,我收到一条错误消息 System.InvalidOperationException: 'Invalid attempt to call Read when reader is closed.'

我的代码:

 OleDbDataReader queryData = sendQueryReturnData("SELECT * FROM users WHERE username = ?;", Parameters);
 while (queryData.Read()) //error on this line
 {
     //stuff
 }

queryData 保持在范围内的一种方法是使其成为 IDisposable 类型的字段并且不要让任何其他方法关闭它,如下所示:

using System;
using System.Data.OleDb;

namespace TwitterSeachTest
{
    public class MyDataClass : IDisposable
    {
        OleDbDataReader queryData;
        OleDbCommand command;
        OleDbConnection conn;

        public object[] Parameters { get; set; } = new object[0];

        public void DoReadData()
        {
            sendQueryReturnData("SELECT * FROM users WHERE username = ?;", Parameters);
            while (queryData.Read()) //error on this line
            {
                //stuff
            }
        }

        private void sendQueryReturnData(string queryString, object parameters)
        {
            this.conn = new OleDbConnection("connectionstring");
            this.command = new OleDbCommand(queryString, conn);
            conn.Open();

            this.queryData = command.ExecuteReader();

            // your code
        }

        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    this.queryData?.Close();
                    this.command?.Dispose();
                    this.conn?.Close();
                }

                // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
                // TODO: set large fields to null.

                disposedValue = true;
            }
        }

        // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
        // ~MyDataClass()
        // {
        //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        //   Dispose(false);
        // }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            // TODO: uncomment the following line if the finalizer is overridden above.
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}

我把你的代码放在 DoReadData 中,不同之处在于 queryData 现在是一个字段而不是局部变量。

我还为 sendQueryReturnData 添加了一些示例代码。请注意,它将 command.ExecuteReader 的结果分配给 queryData 字段。不要在这里使用 using

最后,使用 IDispose 实现处置模式。这样做的结果是使用此 class 的人现在必须使用 using 语句或调用 Dispose.

也就是说,通常在完成后立即从数据库和 close/dispose 数据库对象中读取数据会更容易。相反,创建一个代表数据的 DTO,填充和 return 一个 List<MyDataDto> 然后 close/dispose 资源。这减少了何时以及由谁负责释放这些资源的模糊性。

听起来你正在尝试 "wrap" 函数调用...所以你有一个中心位置来获取连接句柄,传递查询并发回 reader 这样你可以在那里处理。正如其他人评论的那样,整个关闭、处理、清理等......你可以做的是为你的函数创建一个额外的参数,它接受一个 Action 期望数据 reader。你读你想读的 return,然后关闭你需要的。下面只是一个要实现的示例。

同样,不完美,但原则.. 集中式 class 将获得连接、打开、关闭、释放。准备您已经建立的查询命令,然后传回并让 Action 方法实际处理读取,因为不同的查询将有自己的 return structure/columns,等等,这就是我认为您正在尝试的包起来。

你仍然想要你的 try/catch,比如没有有效的 open/close 连接,添加你的 dispose 调用,但原理可能就是你要找的。

    public class MyQueryWrapper
    {
        public GetMyData(OleDbCommand cmd, Action<OleDbDataReader> letYouReadIt)
        {
            using (var conn = new OleDbConnection(yourConnectionString))
            {
                conn.Open();
                cmd.Connection = conn;
                using (var rdr = cmd.ExecuteReader())
                {
                    // Now, call YOUR routine with the reader object while
                    // it is still active...
                    letYouReadIt(rdr);
                }
                conn.Close();
            }
        }
    }


    public class YourOtherClass
    {
        public void GetUser()
        {
            var mqr = new MyQueryWrapper();
            var sqlcmd = new OleDbCommand();
            // prepare the command and parameters.
            myUsers = new List<someUsers>();
            mqr.GetMyData(sqlcmd, ReadRecordsHere);
        }

        private List<someUsers> myUsers;

        public void ReadRecordsHere(OleDbReader rdr)
        {
            while (rdr.Read())
            {
                // read record, add to the "myUsers" list
                // keep reading until all records, then get out
            }
        }
    }