我最近开始学习.NET 开发。我试图制作一个基本的 SQL Server 连接表单,但对于接受 SQL Server 连接查询的用户名和密码的文本框函数,我一直遇到错误的返回类型错误。
我试图接受来自文本框的用户输入,然后使用变量返回它们的值。
private void button1_Click(object sender, EventArgs e)
{
//These are the variables I'm trying to pass to connect functions
string User_name = textBox1_TextChanged(sender, e);
string Pswd = textBox2_TextChanged(sender, e);
string connection_string = null;
MySqlConnection conn;
connection_string = $"server=localhost;database=practice;uid={User_name};pwd={Pswd};";
conn = new MySqlConnection(connection_string);
try
{
conn.Open();
MessageBox.Show("Connection Established !");
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show($"Cannot open Connection ! \nErr code : {ex}");
}
}
//Changed return type from default void to string
public string textBox1_TextChanged(object sender, EventArgs e)
{
string User_name = textBox1.Text;
return User_name;
}
public string textBox2_TextChanged(object sender, EventArgs e)
{
string Pswd = textBox2.Text;
return Pswd;
}
我得到的错误是:
非常感谢任何帮助。
在现代编程中,倾向于将数据以及数据操作(=模型)与数据显示方式(=视图)分开。这样做的优点是,您可以更改模型,而无需显示视图。
例如,如果您有单独的程序来进行用户名密码检查,后来您决定不再使用数据库,而是通过互联网发送数据,或者可能更简单,将其保存在 CSV 文件中,那么您就不需要不必改变视图。
同样,如果您不想使用 TextBox 作为用户名,而是使用 RichTextBox,您将更改视图,但您的模型不必更改。
另一个例子:你可以重用你的模型。例如,您想要在一个大表中显示所有用户名和密码的管理员程序可能会重用相同的模型。
最后但并非最不重要的一点:可以在没有 Windows 窗体的情况下对模型进行单元测试,并且您可以模拟您的模型,以便其他人可以测试您的用户界面。
为了连接模型和视图,需要一个适配器类,通常称为 ViewModel。
这三个类简称为MVVM。如果这对您来说是全新的,请考虑阅读一些有关 MVVM 的背景信息。
回到你的问题
您在一个程序中做了很多事情。这使得程序难以理解、难以维护和测试并且无法重用。
您需要将代码划分为更小的方法。为了提高可测试性,请考虑创建只有一个目标的小类。让你的代码“SOLID”(再次强调,如果你不熟悉 SOLID 原理,请阅读一些背景知识)
首先是一些创建与数据库的开放连接的方法。这些方法易于理解、易于测试、易于维护和更改。
string ConnectionString => $"server=localhost;database=practice;uid={User_name};pwd={Pswd};";
MySqlConnection DefaultDatabaseConnection => this.CreateOpenDatabaseConnection(this.ConnectionString);
// Returns an open Connection to the database with connectionString
// throws InvalidOperationException if cannot open
MySqlConnection CreateOPenDatabaseConnection(string connectionString)
{
MySqlConnection dbConnection = new MySqlConnection(connectionString);
// open the database connection.
// Throws InvalidOperationException if cannot open
dbConnection.Open();
return dbConnection;
}
catch (Exception ex)
{
MessageBox.Show($"Cannot open Connection ! \nErr code : {ex}");
}
检查提供的用户名-密码组合是否有效的方法。
bool IsUserNamePasswordCombinationValid(string userName, string password)
{
// DbConnection implements IDisposable. So we should use "using"
using (var dbConnection = this.DefaultDatabaseConnection)
{
return this.IsUserNamePasswordCombinationValid(dbConnection,
string userName, string password);
}
// Dispose() will close the database connection
}
SqlCommand CreateSqlCommandCheckUserNamePassword(MySqlConnection connectedDb),
{
const string parameterUserName = "@UserName";
const string parameterPassword = "@Password";
string sqlText = ...
// Todo: create sql text that returns 1 if userName / password combination
// is in the database. user parameterUserName and parameterPassword as
// parameters in the sqlText
command.CommandText = sqlText;
command.Parameters.Add(parameterUserName, userName);
command.Parameters.Add(parameterPassword, password);
return command;
}
在完成了这些通常只有一行的小方法之后,我们终于可以将所有内容组合在一起了:
bool IsUserNamePasswordCombinationValid(MySqlConnection connectedDb,
string userName, string password)
{
// SqlCommand implements IDisposable, so we'll use using
using (SqlCommand command = this.CreateSqlCommandCheckUserNamePassword(connectedDb)}
{
// we expect only one boolean, so we can use ExecuteScalar
return (bool)command.ExecuteScalar();
}
}
这看起来有很多方法。幸运的是,大多数方法都是单行的。他们只有一项特定任务,没有其他任务。它们的功能易于理解且易于测试。如果发生更改,例如连接字符串或 SqlText,则只需更改一种方法。
如果您计划重用此代码,请考虑将其全部放入一个类中,例如:
class UserNamePasswordChecker
{
public bool IsUserNamePasswordCombinationValid(string userName, string password)
{
// TODO: add code as described above.
// all other methods can be private
}
仅访问文本框的方法,以及显示确定/不确定的方法
string DisplayedUserName
{
get => this.textBoxUserName.Text;
set => this.textBoxUserName.Text = value;
}
string DisplayedPassword
{
get => this.textboxPassword.Text;
set => this.textboxPassword.Text = value;
}
bool DisplayedOk
{
get => ...
set => ...
}
后者可以显示一个文本框,或者一个大的绿色复选标记的图像,或者一个大的红色十字。或者也许您想要一个通知操作员的消息框。
后一个是如何在不更改模型的情况下更改视图的一个很好的示例。
现在我们要做的就是为您的模型和视图创建一个适配器。请注意,处理 button1 上的点击的事件处理程序也是 viewModel。如果您决定不需要按钮,而是启动检查的菜单项,则模型和视图都不应更改。
// your Button1_Click is renamed to what it does, not who starts it
void CheckUserNamePassword(object sender, EventArgs e)
{
var checker = new UserNamePasswordChecker();
this.DisplayedOk = checker.IsUserNamePasswordOk(
this.DisplayedUserName, this.displayedPassword);
}
因为我们使我们的程序变得坚实,并且因为我们将模型与视图分开,所以我们所有的方法都是小方法。每种方法只有一个特定的任务。这使得很容易理解每个方法的作用。测试这个简单的功能很容易。更改模型很容易,而无需更改视图,反之亦然。重用该模型很容易,例如在管理员的程序中列出所有用户名和密码。
缺点:方法很多。
如果您想将所有内容都放在一个大方法中,那么变化并不大。这样效率并不会提高很多;然而它会降低可读性,更不用说其他优点了。