c# - 不支持关键字:尝试打开 SQL 连接时出现“提供者”
问题描述
我是 C# 和 SQL 的新手,所以非常感谢您的帮助和建议。
我的想法是通过SQL连接连接到数据库,选择密码和电子邮件,然后通过电子邮件(SMTP 连接)将其发送给想要恢复帐户密码的人。
我的问题是我正在尝试通过 SQL 为我的 C# 项目创建密码恢复选项,但出现以下错误:“不支持关键字:'provider'”。
这是我的 app.config 文件:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="Sales_and_Inventory_System__Gadgets_Shop_.Properties.Settings.POS_DBConnectionString" connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\bin\Debug\POS_DB.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.SqlClient" />
<add name="Restaurant_Management_System.Properties.Settings.SIS_DBConnectionString" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\bin\Debug\SIS_DB.accdb" providerName="System.Data.OleDb" />
<add name="Restaurant_Management_System.Properties.Settings.POS_DBConnectionString" connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\bin\Debug\POS_DB.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.SqlClient" />
<add name="Restaurant_Management_System.Properties.Settings.RMS_DBConnectionString" connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\bin\Debug\RMS_DB.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.SqlClient" />
</connectionStrings>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite" />
<add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data></configuration>
这是我的密码恢复类文件:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Net.Mail;
namespace Restaurant_Management_System
{
public partial class frmRecoveryPassword : MetroFramework.Forms.MetroForm
{
String cs = "Provider=Microsoft.ACE.Sql.12.0;Data Source=|DataDirectory|\\SIS_DB.accdb;";
public frmRecoveryPassword()
{
InitializeComponent();
}
private void RecoveryPassword_Load(object sender, EventArgs e)
{
txtEmail.Focus();
}
private void RecoveryPassword_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
frmLogin frm = new frmLogin();
frm.txtUserName.Text = "";
frm.txtPassword.Text = "";
frm.txtUserName.Focus();
frm.Show();
}
private void timer1_Tick(object sender, EventArgs e)
{
Cursor = Cursors.Default;
timer1.Enabled = false;
}
private void metroTile1_Click(object sender, EventArgs e)
{
if (txtEmail.Text == "")
{
MessageBox.Show("Enter your email", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txtEmail.Focus();
return;
}
try
{
Cursor = Cursors.WaitCursor;
timer1.Enabled = true;
DataSet ds = new DataSet();
SqlConnection con = new SqlConnection(cs);
con.Open();
SqlCommand cmd = new SqlCommand("SELECT User_Password FROM Registration Where Email = '" + txtEmail.Text + "'", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
con.Close();
if (ds.Tables[0].Rows.Count > 0)
{
MailMessage Msg = new MailMessage();
// Sender e-mail address.
Msg.From = new MailAddress("abcd@gmail.com");
// Recipient e-mail address.
Msg.To.Add(txtEmail.Text);
Msg.Subject = "Your Password Details";
Msg.Body = "Your Password: " + Convert.ToString(ds.Tables[0].Rows[0]["user_Password"]) + "";
Msg.IsBodyHtml = true;
// your remote SMTP server IP.
SmtpClient smtp = new SmtpClient();
smtp.Host = "smtp.gmail.com";
smtp.Port = 587;
smtp.Credentials = new System.Net.NetworkCredential("abcd@gmail.com", "abcd");
smtp.EnableSsl = true;
smtp.Send(Msg);
MessageBox.Show(("Password Successfully sent " + ("\r\n" + "Please check your mail")), "Thank you", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Hide();
frmLogin LoginForm1 = new frmLogin();
LoginForm1.Show();
LoginForm1.txtUserName.Text = "";
LoginForm1.txtPassword.Text = "";
LoginForm1.ProgressBar1.Visible = false;
LoginForm1.txtUserName.Focus();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void metroTile2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
解决方案
您的帖子正文中有五 (5!) 个连接字符串。您使用的唯一一个是在frmRecoveryPassword
类中硬编码为cs
字段的那个。
SqlConnection
抱怨,因为它不能识别至少一个名称/值对。那是因为它是 OLEDB 的连接字符串,而不是 SQL Server。
根据您发布的内容,您似乎正在尝试连接到 Access 数据库。为此,您需要使用OleDbConnection
. 但是,根据评论,情况并非如此。您确实想连接到 SQL Server 数据库,所以SqlConnection
是正确的,而您的连接字符串是错误的。
从您发布的内容开始(而不是您此后所做的更改),您唯一需要更改的是这一行:
SqlConnection con = new SqlConnection(cs);
将其更改为:
// Pick one of the three names in app.config that refers to a SQL Server database.
// I chose the first one for this example.
string connectionStringName = "Sales_and_Inventory_System__Gadgets_Shop_.Properties.Settings.POS_DBConnectionString";
string connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
SqlConnection con = new SqlConnection(connectionString);
这将通过 中指定的名称检索连接connectionStringName
并将其提供给SqlConnection
.
确保您使用正确的连接字符串来执行此单击处理程序正在执行的操作。
连接字符串
现在让我们检查连接字符串。
app.config 中的那些似乎是正确的,因为它们对于providerName
每个属性中指定的属性都有正确的外观值。第二个指向 Access 数据库,另外三个指向 SQL Server 数据库。
屏幕截图中的那个(来自评论)是错误的。它是一个混合体:您已经指定了一个OleDb
提供程序(用于 Access)和一个 SQL Server 数据库文件。看起来您从 app.config 中的第一个连接字符串复制了 .mdf 文件的路径,并在 .accdb 文件所在的第二个连接字符串中使用了该路径。 SqlConnection
会给您与原始问题相同的异常,因为它无法识别"Provider"
密钥,并且OleDbConnection
会给您“无法识别的数据库格式”。任何一种连接都是错误的。
无论您使用的是 SQL Server 还是 Access 数据库——看起来你的应用程序都连接到两者——你需要确保为该数据库使用正确的连接类型和连接字符串。
现在让我们深入研究其他一些问题:
SQL 注入
不要连接字符串来构造 SQL。
请务必阅读Little Bobby Tables。避免 SQL 注入不应该是事后的想法。从一开始就保护您的应用程序。这样做的方法是参数化您的查询。
暂时忽略这个确切查询的危险,这就是你应该做的:
SqlCommand cmd = new SqlCommand("SELECT User_Password FROM Registration WHERE Email = @Email");
// ^^^^^^ parameter
cmd.Parameters.Add(new SqlParameter
{
Name = "@Email",
SqlDbType = SqlDbType.NVarChar,
Size = 100, // or whatever length your Email column is.
Value = txtEmail.Text
// ^^^^^^^^^^^^^ paramter's value
});
您可以使用其他Add
重载,但我更喜欢这个重载,因为与许多其他重载相比,您设置的属性非常清晰。我见过人们使用错误的“数据库类型”并得到运行时错误而不是编译错误,例如,SqlDbType
当他们使用 Oracle 或 MySql 时传递一个值,它会默默地将该值作为object value
参数传递。您将在 SO 的许多答案中遇到这些重载的示例。
您还会看到AddWithValue
大量使用 -不要使用它。麻烦多于其价值,至少对于 SQL Server 而言。
找回密码
这就是我所说的那个确切查询的危险的意思......
切勿以纯文本形式存储密码。
您永远不应该向某人发送他们的密码,因为您永远无法找回它。相反,您的数据库应该包含密码的哈希值。您的User_Password
列不仅对人眼而且对用于检索它的任何系统来说都应该看起来像垃圾。
当您正确操作时,“密码恢复”是一个不准确的术语。处理忘记密码的现代做法实际上是“访问恢复”,它们通常涉及允许您提供新密码的短期令牌。
诸如密码散列和访问恢复过程之类的主题都有很好的文档记录,因此我将不再赘述。
推荐阅读
- javascript - 字体系列在系统打印中不起作用
- algorithm - 给定递归关系计算函数的复杂度
- c# - 错误是 C# DateTime.Now 返回波斯日期
- java - volatile 对象中的非 volatile 字段是否与 volatile 原始变量具有相同的效果?
- javascript - 为什么我收到此 jquery 代码的 NaN 错误?
- bash - 如何更改 KDE 中 bash/shell 脚本中新选项卡的默认窗口?
- php - 使用变量访问路由/目录而不阻止其他路由
- javascript - 如何在 Laravel 刀片中的反应组件中获取 Laravel 验证错误
- javascript - Node.js - 定义了一个函数但引发错误
- c# - 无法在 ASP.NET Core 操作筛选器中获取正确的 DbContext 引用