我有这个 MS Access 数据库表:
ID Long Integer 4
FirstName ShortText 255
ID
是主键,有索引
我还有一个存储过程
UpdateTest
,其中包含:
UPDATE Test
SET Test.FirstName = [@FirstName]
WHERE (((Test.ID) = [@ID]));
C#代码
public void updateTest(int ID,string FirstName)
{
OleDbCommand TestCmd = new OleDbCommand("UpdateTest", this.objConn);
TestCmd.CommandType = CommandType.StoredProcedure;
OleDbParameter ObjParam;
ObjParam = TestCmd.Parameters.Add("@ID", OleDbType.Numeric);
ObjParam.Direction = ParameterDirection.Input;
ObjParam.Value = ID;
ObjParam = TestCmd.Parameters.Add("@FirstName", OleDbType.BSTR);
ObjParam.Direction = ParameterDirection.Input;
ObjParam.Value = FirstName;
try
{
this.objConn.Open();
var result = TestCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Exception newEx = new Exception(ex.Message);
}
finally
{
this.objConn.Close();
}
}
问题:没有发生错误,但是数据库没有更新。执行后
results
为0。
在MS Access中执行存储过程时,工作正常。
我错过了什么?
谢谢
Access 中并没有真正的存储过程。如果您在 Access 中定义一个查询(例如使用创建存储过程 DLL 命令,那么如果您在 Access 中打开这样的查询,您会看到它是一个标准的选择或更新查询)。
因此,假设我们在 Access 中有此查询:
PARAMETERS [@ID] Long, [@FirstName] Text ( 255 );
UPDATE tblHotelsA SET tblHotelsA.FirstName = [@FirstName]
WHERE tblHotelsA.ID = [@ID];
然后在代码中,这样就可以正常工作了:
using (OleDbConnection conn = new OleDbConnection(Properties.Settings.Default.AccessDB))
{
using (OleDbCommand cmdSQL = new OleDbCommand("UHotels", conn))
{
cmdSQL.CommandType = CommandType.StoredProcedure;
cmdSQL.Parameters.Add("@ID", OleDbType.Integer).Value = 423;
cmdSQL.Parameters.Add("@FirstName", OleDbType.VarWChar).Value = "Albert";
conn.Open();
cmdSQL.ExecuteNonQuery();
}
}
所以,上面的效果很好。我还建议您不要尝试“持久”连接,而是重新创建并让 using 块销毁对象,包括连接对象。
这个建议似乎违反直觉,因为在我们的行业中,当使用基于文件的数据库引擎时,为了获得更好的性能,“非常”长时间的建议是创建一个有效的打开连接,然后在代码中保持该有效的连接对象打开,并且对于应用程序来说是全局的。
但是,随着 .net 的到来,连接池和该连接的管理现在是自动为开发人员“管理”的。
这意味着不需要“保持”某些连接对象打开并将其范围限定在应用程序范围内,因为 .net 实际上会自动为您重新循环这些连接。因此,在使用 .net 时,这种保持持久连接对象始终打开的“长时间”建议不再适用。
因此,您不仅可以在每次使用连接对象时自由地处理它,而且您作为开发人员现在也可以从必须在代码中管理数据库连接的任务中解放出来 - .net 将为您完成此操作,并自动重新-为您循环连接。
当我发布带有强参数输入的已保存查询时?
您可以省略参数设置。
但是!如果省略参数设置?
然后要非常小心,因为参数的顺序必须与参数设置相匹配,并且如果 Access SQL 中不存在参数语句,则添加参数的顺序必须与它们“使用”的顺序相匹配在 SQL 中。 因此,通过“微小”更改(删除参数),此代码不再起作用:
cmdSQL.CommandType = CommandType.StoredProcedure;
cmdSQL.Parameters.Add("@ID", OleDbType.Integer).Value = 423;
cmdSQL.Parameters.Add("@FirstName", OleDbType.VarWChar).Value = "Albert";
conn.Open();
cmdSQL.ExecuteNonQuery();
为什么它不会产生错误?因为您现在实际上匹配了名字上的 @ID,并且 FirstName 试图成为您的 ID 值。
因此,由于在上面,FirstName 是 SQL 中第一个使用的参数,那么您必须像这样更改顺序:
using (OleDbConnection conn = new OleDbConnection(Properties.Settings.Default.AccessDB))
{
using (OleDbCommand cmdSQL = new OleDbCommand("UHotels", conn))
{
cmdSQL.CommandType = CommandType.StoredProcedure;
cmdSQL.Parameters.Add("@FirstName", OleDbType.VarWChar).Value = "Albert";
cmdSQL.Parameters.Add("@ID", OleDbType.Integer).Value = 423;
conn.Open();
cmdSQL.ExecuteNonQuery();
}
}
上面的代码现在就可以工作了。因此,经常混淆参数的顺序不会产生任何错误,但实际上您尝试根据 FirstName 查找“id”,然后将 FirstName 更新为 @ID。
因此,无论好坏,SQL 中使用的参数的顺序必须与添加它们的顺序相匹配。
如前所述,您可以通过添加 SQL 参数语句来解决此顺序问题,就像我对保存的 Access 查询所做的那样。因此,参数的顺序现在是在 parmeters 语句中定义的,因此这是您需要在代码中使用的顺序,而不是实际 SQL 中参数的放置位置。
因此,虽然顺序仍然很重要,但无论参数在 SQL 中的位置如何,参数中使用的顺序都将定义正确的顺序。
但是,删除参数后,例如:
UPDATE tblHotelsA SET tblHotelsA.FirstName = [@FirstName]
WHERE (((tblHotelsA.ID)=[@ID]));
上面没有定义的参数,因此您的代码必须匹配它们在 sql 中使用的顺序。如上所示,@FirstName 是第一个参数,而不是 @ID,因此您必须按该顺序添加参数(@FirstName 现在必须在代码中排在第一位)。