返回一个SqlDataReader

12

我有这段代码:

public static SqlDataReader GetGeneralInformation ( int RecID )
{
    using ( var conn = new SqlConnection( GetConnectionString() ) )
    using ( var cmd = conn.CreateCommand() )
    {
        conn.Open();
        cmd.CommandText =
        @"SELECT cs.Status, cs.Completed
          FROM NC_Steps s
          INNER JOIN NC_ClientSteps cs
              ON cs.RecID = s.RecID
          WHERE cs.ClientID = 162
          AND s.RecID = @value";
        cmd.Parameters.AddWithValue( "@value", RecID );
        using ( var reader = cmd.ExecuteReader() )
        {
            if ( reader.Read() )
            {
                return reader;
            }
            return null;
        }
    }
}

如何引用这个?

我尝试过这个,但它没有起作用。

SqlDataReader reader = GeneralFunctions.GetGeneralInformation();

我该如何从阅读器(reader)中读取数据呢?

Reader.GetString( reader.GetOrdinal( "Status" ) )

编辑:

我现在遇到以下错误:

异常详细信息:System.NullReferenceException: 对象引用未设置为对象的实例。

这是更新后的代码:

public static IEnumerable<IDataRecord> GetGeneralInformation ( int ClientID )
{
    using ( var conn = new SqlConnection( GetConnectionString() ) )
    using ( var cmd = conn.CreateCommand() )
    {
        conn.Open();
        cmd.CommandText =
        @"SELECT i.GoLiveDate, i.FirstBonusRun, i.TechFName, i.TechLName, i.TechEmail, i.TechPhone, i.WebISPFName, i.WebISPLName, 
          i.WebISPEmail, i.WebISPPhone, i.FullFillFName, i.FullFillLName, i.FullFillEmail, i.FullFillPhone, d.FName,
          d.LName, d.HomePhone, d.Email
          FROM NC_Information i
          INNER JOIN Distributor d
            ON d.DistID = i.ClientID
          WHERE clientID = @value";
        cmd.Parameters.AddWithValue( "@value", ClientID );
        using ( var reader = cmd.ExecuteReader() )
        {
            while ( reader.Read() )
            {
                yield return reader;
            }
            yield return null;
        }
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    IEnumerable<IDataRecord> reader = GeneralFunctions.GetGeneralInformation( (int)Session[ "DistID" ] );

    //string result = GeneralFunctions.GetGeneralInformation( Globals.GeneralInformation ).First()[ "Status" ].ToString();
}

1
为什么你不能像读取其他SqlDataReader一样读取它?它失败了吗?如果是这样,那就是因为你的“using”语句。在使用阅读器之前,你已经关闭了连接。 - John Saunders
1
你可以返回一个从读取器中填充的对象。而且你还没有说什么没有起作用。 - John Saunders
2
@JamesWilson:我会重构这个代码——把一个读取器“泄露”给另一个方法不是一个好的设计(即使你可以让它工作),最好只返回结果。 - BrokenGlass
1
我不会直接使用ADO.NET。我会使用Entity Framework或其他抽象层。我还会在单独的数据访问层类库中进行操作。你不应该在网页中直接访问数据(尽管我有时会为数据源控件做出例外)。 - John Saunders
1
你得到了空引用异常,因为你返回了null。你不需要"yield return null"这部分。你误解了C#中yield的工作原理。 - nawfal
显示剩余6条评论
5个回答

21

问题在于通过 return 语句离开函数会使你退出 using 块,并且正在使用的 SqlDataReaderSqlConnections 将被处理。为了解决这个问题,尝试像这样更改函数签名:

public static IEnumerable<IDataRecord> GetGeneralInformation ( int RecID )

然后将函数中间更新为:

using ( var reader = cmd.ExecuteReader() )
{
    while ( reader.Read() )
    {
        yield return reader;
    }
}

最后一个“我该如何从中读取?”部分可能看起来像这样:

int RecID = 12345;
string result = GetGeneralInformation(RecID).First()["Status"].ToString();

1
你能帮我理解 IEnumerable<IDataRecord> 和 yield 是什么吗?如果不行的话,我可以自己查一下资料。无论如何,感谢你的回答。 - James Wilson
1
@JamesWilson 关键字yield创建所谓的“迭代器”。这是一个知道如何循环遍历某种集合的对象。IEnumerable<IDataRecord> 是另一种描述SqlDataReader重要部分的方式。你可以在foreach(;;)循环中使用它。 - Joel Coehoorn
我该如何从中提取数据?reader. 给了我很多选项,但似乎没有一种允许我推断出特定的数据?我该如何提取“已完成”列?返回的数据将始终只有一行信息。 - James Wilson
1
那么你需要一种方法来解决这个问题...但现在你面临一个全新的问题。 - Joel Coehoorn
1
@James:这正是我担心的。当从using语句返回时,读取器已关闭。按照此答案中所示初始化自定义对象:http://stackoverflow.com/a/4912837/284240 - Tim Schmelter
显示剩余9条评论

6
将您的连接字符串添加到 app.config 或 web.config 文件的 AppSettings 部分中。
   public string GetSqlConnection()
    {
        return  System.Configuration.ConfigurationManager.AppSettings["SqlConnectionString"];
    }

//返回SqlDataReader结果的函数

    public SqlDataReader executeReader(string sql, SqlParameter[] parameters=null)
    {
        SqlConnection conn = new SqlConnection();
        conn.ConnectionString = GetSqlConnection();
        conn.Open();
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = conn;
        cmd.CommandText = sql;
        if (parameters != null)
        {
            cmd.CommandType = CommandType.StoredProcedure;
            foreach (SqlParameter p in parameters)
            {
                cmd.Parameters.Add(p);
            }
        }
        else
        {
            cmd.CommandType = CommandType.Text;
        }
        SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
        return reader;
    }

使用该函数的方法如下:
        string query = @"SELECT cs.Status, cs.Completed
      FROM NC_Steps s
      INNER JOIN NC_ClientSteps cs
          ON cs.RecID = s.RecID
      WHERE cs.ClientID = 162
      AND s.RecID = @value";
       //you can add more parameters by adding commas
       var parameters = new SqlParameter[] {
            new SqlParameter("@value", RecID )
           };

        SqlDataReader dr = executeReader(query, parameters);
        while (dr.Read())
        {
            //fill your controls with data 
        }
        dr.Close();

1

0
问题在于您正在方法内部创建数据库连接。
如果您要在多个方法之间共享数据库资源,请将 SqlConnection 移动到此范围之外。
这样,您可以从此函数返回 Reader,并使其持久化。
另外,请不要在此函数内部使用 .Read(),只需返回对象即可。

-1
根据定义,using语句被设计用于在调用后处理对象的Dispose操作。
因此,在返回数据读取器之后,它会被处理并释放。

你在说什么?你不需要一个yield。去试试吧。它可能不会起作用,但肯定会编译。 - John Saunders

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