C# 创建数据库查询方法
我不确定我是否被删除,但我想做的是创建一个返回查询结果的方法,这样我就可以重用连接代码。据我所知,查询返回一个对象,但如何将该对象传回?我想将查询作为字符串参数发送到方法中,并让它返回结果,以便使用它们。这是我在黑暗中的一次尝试,它显然不起作用。这个例子是我试图用查询结果填充一个列表框;工作表名称为Employees,字段/列为name。我得到的错误是“复杂数据绑定接受IList或IListSource作为数据源。”。有什么想法吗C# 创建数据库查询方法,c#,object,methods,return,C#,Object,Methods,Return,我不确定我是否被删除,但我想做的是创建一个返回查询结果的方法,这样我就可以重用连接代码。据我所知,查询返回一个对象,但如何将该对象传回?我想将查询作为字符串参数发送到方法中,并让它返回结果,以便使用它们。这是我在黑暗中的一次尝试,它显然不起作用。这个例子是我试图用查询结果填充一个列表框;工作表名称为Employees,字段/列为name。我得到的错误是“复杂数据绑定接受IList或IListSource作为数据源。”。有什么想法吗 public Form1() {
public Form1()
{
InitializeComponent();
openFileDialog1.ShowDialog();
openedFile = openFileDialog1.FileName;
lbxEmployeeNames.DataSource = Query("Select [name] FROM [Employees$]");
}
public object Query(string sql)
{
System.Data.OleDb.OleDbConnection MyConnection;
System.Data.OleDb.OleDbCommand myCommand = new System.Data.OleDb.OleDbCommand();
string connectionPath;
//build connection string
connectionPath = "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + openedFile + "';Extended Properties=Excel 8.0;";
MyConnection = new System.Data.OleDb.OleDbConnection(connectionPath);
MyConnection.Open();
myCommand.Connection = MyConnection;
myCommand.CommandText = sql;
return myCommand.ExecuteNonQuery();
}
试试看。它返回一个对象,然后可以像文件一样读取该对象以获得结果:
OleDbDataReader myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection);
while(myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
}
您发布的代码的大问题是无法正确地参数化查询。在调用函数之前,必须进行字符串连接,这会导致sql注入攻击。您需要在代码中找到一种方法,允许查询参数与sql字符串分开 示例中的其他一些问题包括未正确关闭连接(如果查询引发异常,连接将挂起)和调用错误的ADO方法 我已经做了大量的工作来实现这一点,我想我在回答另一个问题时已经很好地确定了一些接近理想模式的东西:
基本上,当您调用ADO函数来实际运行查询时,您会得到一个。我使用一个迭代器块将该数据读取器转换为一个IEnumerable,该IEnumerable可以很好地与linq和其他代码一起工作,并使用一个操作来鼓励正确的查询参数化。因此,将连接代码抽象为如下方法:
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
public IEnumerable<IDataRecord> GetSomeDataById(int MyId)
{
return Retrieve(
"SELECT * FROM [MyTable] WHERE ID= @MyID",
p =>
{
p.Add("@MyID", SqlDbType.Int).Value = MyId;
}
);
}
SqlConnection connection;
try
{
connection = new SqlConnection("connection string here");
SqlCommand command = new SqlCommand("sql query here", connetion);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
//do something with the data reader here
}
finally
{
connection.Close();
}
public DataTable GetEmployeeData(string FirstName)
{
return Query("SELECT * FROM Employees WHERE FirstName='" + FirstName + "'");
}
私有静态IEnumerable检索(字符串sql、操作addParameters)
{
使用(var cn=new-SqlConnection(ConnectionString))
使用(var cmd=newsqlcommand(sql,cn))
{
addParameters(命令参数);
cn.Open();
使用(var rdr=cmd.ExecuteReader())
{
while(rdr.Read())
收益率;
rdr.Close();
}
}
}
并在代码中用于实际查询,如下所示:
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
public IEnumerable<IDataRecord> GetSomeDataById(int MyId)
{
return Retrieve(
"SELECT * FROM [MyTable] WHERE ID= @MyID",
p =>
{
p.Add("@MyID", SqlDbType.Int).Value = MyId;
}
);
}
SqlConnection connection;
try
{
connection = new SqlConnection("connection string here");
SqlCommand command = new SqlCommand("sql query here", connetion);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
//do something with the data reader here
}
finally
{
connection.Close();
}
public DataTable GetEmployeeData(string FirstName)
{
return Query("SELECT * FROM Employees WHERE FirstName='" + FirstName + "'");
}
public IEnumerable GetSomeDataById(int-MyId)
{
返回检索(
“从[MyTable]中选择*,其中ID=@MyID”,
p=>
{
p、 Add(“@MyID”,SqlDbType.Int).Value=MyID;
}
);
}
请注意,这使您能够正确地参数化查询,始终正确地关闭和处理连接对象,设置您在3层或服务体系结构中的每一层之间进行管道连接(使其快速),并以最小的代码开销完成此操作。嘿!试试这个,
如果您只想在列表框中显示所有员工的姓名,这应该是可行的。
我刚从你的代码中编辑了几行
Form1()
{
InitializeComponent();
openFileDialog1.ShowDialog();
openedFile = openFileDialog1.FileName;
lbxEmployeeNames.DataSource = Query("Select [name] FROM [Employees$]");
lbxEmployeeNames.DisplayMember = "name"; // The column you want to be displayed in your listBox.
}
// Return a DataTable instead of String.
public DataTable Query(string sql)
{
System.Data.OleDb.OleDbConnection MyConnection;
string connectionPath;
//build connection string
connectionPath = "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + openedFile + "';Extended Properties=Excel 8.0;";
MyConnection = new System.Data.OleDb.OleDbConnection(connectionPath);
MyConnection.Open();
System.Data.OleDb.OleDbDataAdapter myDataAdapter = new System.Data.OleDb.OleDbDataAdapter(sql, MyConnection);
DataTable dt = new DataTable();
myDataAdapter.Fill(dt);
return dt;
}
学习与数据库对话时,每个程序员都必须做两件基本的事情:关闭连接和参数化查询。这些项目与运行sql语句和接收结果的实际过程是分开的,但它们仍然是绝对必要的。出于某种原因,互联网上提供的大多数教程只是对它们进行了掩饰,甚至完全弄错了,这可能是因为对于任何有能力编写教程的人来说,这是第二天性。我在这里的目标是向您展示如何构建整个过程,包括这些额外的基础知识,以一种更容易做到这一点的方式,并且每次都做到这一点 首先要做的是认识到,在一个方法中隐藏数据访问代码是不够的:我们实际上想要为此构建一个单独的类(甚至类库)。通过创建一个单独的类,我们可以使实际的连接方法在该类中成为私有的,这样只有该类中的其他方法才能连接到数据库。通过这种方式,我们设置了一个网守,强制程序中的所有数据库代码通过一个批准的通道运行。对于我上面提到的两个问题,正确地使用守门人代码,您的整个程序也将始终正确。这是我们的开始:
public class DataLayer
{
private DbConnection GetConnection()
{
//This could also be a connection for OleDb, ODBC, Oracle, MySQL,
// or whatever kind of database you have.
//We could also use this place (or the constructor) to load the
// connection string from an external source, like a
// (possibly-encrypted) config file
return new SqlConnection("connection string here");
}
}
到目前为止,我们还没有从导言中真正解决两个基本问题。到目前为止,我们所做的只是让自己编写代码,以便在以后实施良好实践。让我们开始吧。首先,我们将考虑如何强制关闭您的连接。为此,我们添加了一个方法,该方法运行查询,返回结果,并确保在完成后关闭连接:
private DataTable Query(string sql)
{
var result = new DataTable();
using (var connection = GetConnection())
using (var command = new SqlCommand(sql, connection)
{
connection.Open();
result.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
}
return result;
}
您可以添加其他类似的方法来返回标量数据或根本不返回数据(用于更新/插入/删除)。现在还不要太执着于这段代码,因为它还没有完成。我马上解释原因。现在,让我指出这个方法仍然是私有的。我们还没有完成,因此我们不希望此代码可用于程序的其他部分
我想强调的另一件事是使用关键字的。此关键字是在.Net和C#中声明变量的一种强大方法。使用
关键字在变量声明下面创建一个范围块。在作用域块的末尾,您的变量被释放。请注意,这有三个重要部分。首先,这实际上只适用于非托管资源,如数据库连接;记忆仍然以通常的方式收集。第二,即使抛出异常,也会释放该变量。这使得关键字适合与时间敏感或受严格限制的资源(如数据库连接)一起使用,而不需要在附近使用单独的try/catch块。最后一点是关键字使用.Net中的IDisposable模式。
public DataTable GetAllEmployees()
{
return Query("SELECT * FROM Employees", p => {});
}