【问题标题】:C# and MySQL - Method Returning a DataReader (or its contents)C# 和 MySQL - 返回 DataReader(或其内容)的方法
【发布时间】:2014-09-04 17:36:04
【问题描述】:

在整个程序中,我将重复使用(MySQL 的)SELECT 命令。每次,我都必须建立连接和东西。我正在考虑制作一个接收 SELECT 命令字符串并返回参数的等效 DataReader 的方法。我认为这将帮助我减少每次必须生成的大量代码。

我想以这样的方式使用它:

MySqlDataReader myReader = myObj.loadDataToReader("SELECT * FROM tblSample");

然后,我可以像普通的 MySqlDataReader 一样操作myReader。但是,我担心使用数据读取器时必须打开连接这一事实,当然,需要关闭它(读取器和连接)以及所有(传统的安全措施,我相信)。我一直在互联网上查看东西,但似乎无法找到有关如何执行此操作的提示。

我正在尝试,我有以下代码行:

public MySqlDataReader loadDataToReader(string selectCommand)
{
    MySqlDataReader myReader = null;
    string myConnectionString = "Data Source = " + server + "; User = " + user + "; Port = 3306; Password = " + password + ";";
    string useDataBaseCommand = "USE " + dbName + ";";
    using (MySqlConnection myConnection = new MySqlConnection(myConnectionString))
    {
        using (MySqlCommand myCommand = new MySqlCommand(useDataBaseCommand + selectCommand, myConnection))
        {
            try
            {
                myConnection.Open();
                myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch (Exception ex)
            {
                myConnection.Close();
                MessageBox.Show(ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return myReader;
        }
    }
}

在另一个类的方法中,我创建了一个能够使用上面定义的方法的对象,它看起来像这样:

string selectTableCommand = "SELECT * FROM tblusers WHERE Username = \'" + txtID.Text + "\' AND Password = \'" + txtPassword.Text + "\';";
MySQLOperations objSQLOperations = new MySQLOperations("localhost", "root", "mypass", "mydatabase");
MySqlDataReader myDataReader = objSQLOperations.loadDataToReader(selectTableCommand);
if (myDataReader.Read() && txtPassword.Text.Equals(myDataReader["Password"].ToString()))
     { /* do something */ }
else
     { /* do something */ }

但是,我收到一条消息,提示“阅读器关闭时尝试阅读无效!” 我怎样才能纠正这个问题并让它发挥作用?或者更确切地说,有没有更合适的方法来做到这一点?以及如何确保我的连接/阅读器在使用后关闭?

【问题讨论】:

  • 改用数据表。将阅读器加载到数据表中,即var dt = new DataTable(); dt.Load(myReader);,然后将数据表返回给调用者以对其进行处理。
  • DataSetDataTable 通常是比 DataReader 更好的选择。此外,您应该参数化您的 SQL 语句,而不是连接它们:en.wikipedia.org/wiki/SQL_injection
  • 感谢您的想法。我将改用 DataTable。

标签: c# mysql methods datareader


【解决方案1】:

您可以使您的方法通用并注入一个函数以在阅读器上工作,然后返回函数的输出而不是阅读器:

public T LoadDataToReader<T>(string selectCommand, Func<IDataReader,T> ProcessResults)
{
    string myConnectionString = "Data Source = " + server + "; User = " + user + "; Port = 3306; Password = " + password + ";";
    string useDataBaseCommand = "USE " + dbName + ";";
    using (var myConnection = new MySqlConnection(myConnectionString))
    {
        myConnection.Open();

        using (var myCommand = myConnection.CreateCommand())
        {
            myCommand.CommandText = useDataBaseCommand + selectCommand;

            using(var myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection))
            {
               return ProcessResults(myReader);
            }
        }
    }
}

我还做了一些其他的改变:

  • IDataReader 实现了IDisposable,所以我为读者添加了 using 语句
  • 我使用 ADO.NET 接口方法来创建命令而不是构造函数
  • 我删除了 catch 块,因为当您离开 using 块时连接关闭会自动发生,并且 UI 代码(例如,MessageBox)不属于 DAL 代码。这应该作为调用此方法的 try/catch 来完成。
  • 我将函数的第一个字母大写以使其符合 .NET 编码标准

然后,您只需按如下方式使用它:

public static string GetStringData(IDataReader reader)
{
  var ord_name = reader.GetOrdinal("Name");

   if(reader.Read())
     return reader.GetString(ord_name);

   return null;
}

public static IEnumerable<Foo> GetFoos(IDataReader reader)
{
   var ord_name = reader.GetOrdinal("Name");
   var foos = new List<Foo>();

   while(reader.Read())
     foos.Add(new Foo {Name = reader.GetString(ord_name)});

   return foos;
}

static void Main(string[] args)
{
   var program = new Program();
   try
   {
      var name = program.LoadDataToReader("SELECT name FROM thename", GetStringData);
   }
   catch(Exception ex)
   {
      MessageBox.Show(ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
   try
   {
      var foos = program.LoadDataToReader("SELECT foos FROM footable", GetFoos);
   }
   catch(Exception ex)
   {
      MessageBox.Show(ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
}

此外,您可能应该传入连接字符串,而不是从字段中构建它。或者,您可以为整个连接字符串设置一个字段(只构建一次,而不是每次执行)并使用它。

此外,可以使用Initial CatalogDatabase 在连接字符串中设置数据库,这比使用USE database 子句作为select 语句的前缀更好。

【讨论】:

  • 我已经决定我要坚持我上面所做的,只是将其更改为 DataTable。不过,还是谢谢你,先生。总有一天,我可能会以某种方式使用它。
  • @Jill 请记住,DataSetDataTable 都是 IDisposable。它们在内存方面和查询方面都是相当重的对象,因为数据适配器必须执行额外的步骤来确定结果集模式。如果您提前知道架构,最好使用数据阅读器,尽管使用像实体框架或 nHibernate 这样的 ORM 会比任何建议的选项更好。
猜你喜欢
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
相关资源
最近更新 更多