在应用会话中两次调用相同的查询方法时,我得到" DBCommandExcept"
作为一个实验,我决定在方法结束时处理连接对象,看看是不是问题。
我不再获取DBCommandExcept错误消息,而是获取," connectionstring属性尚未初始化。"
IOW,它现在是一种Catch-22的情况。相关代码是:
string query = "SELECT Bla FROM Blah";
SqlCeCommand cmd = new SqlCeCommand(query);
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow);
try
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
catch (Exception ex)
{
RRDR.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound(): {0}", ex.Message));
}
finally
{
myReader.Close();
//if (null != conn)
//{
// conn.Dispose();
//}
}
// Re:上面的注释掉的块:当它处于活动状态时,看不到DBCommandExcept问题;然而,我接着," connectionstring属性尚未初始化"
我认为上面唯一的非SQL-CE标准位是dbConn.GetConnection()。这里有一些代码:
SqlCeConnection objCon = null;
. . .
public SqlCeConnection GetConnection()
{
return objCon;
}
private DBConnection() // class constructor
{
try
{
. . .
objCon = new SqlCeConnection(conStr);
objCon.Open();
. . .
同样,在应用程序的一次运行期间,错误(任何一个,无论选择哪个"选择")都只能通过此方法第二次看到。第一次工作正常。
我添加了下面的代码,评论说明了悲惨的故事:
// With conn check only, still get two consecutive DBCommandExcepts
// With cmd check only, still get two consecutive DBCommandExcepts
// With both, still get two consecutive DBCommandExcepts; IOW, all have the same effect
if (null != conn)
{
conn.Close();
}
if (null != cmd)
{
cmd.Dispose();
}
根据unicron的建议,我尝试使用"使用。"
在三种情况中的两种情况下(SqlCeCommand和SqlCeDataReader),转换为"使用"没有差异;在另一个(SqlCeConnection)中,它引发了错误的消息," ConnectionString属性尚未初始化。"
尽管如此,使用这两个代码时代码更清晰,所以感谢你在最佳实践方向上的推动。
现在看来是这样的:
private bool PopulateControlsIfPlatypusItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
try
{
string PlatypusId = txtPlatypus.Text.ToString().Trim();
string PlatypusItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
string query = string.Format("SELECT ItemID, PackSize FROM PlatypusItems WHERE PlatypusID = {0} AND PlatypusItemID = {1}", PlatypusId, PlatypusItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
SqlCeConnection conn = dbconn.GetConnection();
if ((null != conn) && (!conn.State.Equals(ConnectionState.Open)))
{
conn.Open();
TTBT.LogMsgs.Append("Connection opened");
}
cmd.CommandType = CommandType.Text;//probably unnecessary
cmd.Connection = conn;
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
TTBT.LogMsgs.Append(string.Format("Exception in PopulateControlsIfPlatypusItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
我通过使用通用排序替换自定义连接代码,添加另一个"使用"更接近常态。混合:
private bool PopulateControlsIfVendorItemsFound()
{
const int ITEMID_INDEX = 0;
const int PACKSIZE_INDEX = 1;
bool recordFound = false;
DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
try
{
string vendorId = txtVendor.Text.ToString().Trim();
string vendorItemId = txtUPC.Text.ToString().Trim();
string itemID = string.Empty;
string packSize = string.Empty;
if ( dbconn.isValidTable( "VendorItems" ) == -1 )
{
DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
return false;
}
string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
using (SqlCeCommand cmd = new SqlCeCommand(query))
{
cmd.CommandType = CommandType.Text;
using (SqlCeConnection conn = new SqlCeConnection())
{
string filename = "\\badPlace2B\\CCRDB.SDF";
conn.ConnectionString = string.Format("Data Source = {0}", filename);
cmd.CommandType = CommandType.Text;//probably unnecessary/moot
cmd.Connection = conn;
conn.Open();
using (SqlCeDataReader myReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (myReader.Read())
{
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
}
txtID.Text = itemID;
txtSize.Text = packSize;
return recordFound;
}
}
catch (Exception ex)
{
DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0} - {1}\r\n", ex.Message, ex.InnerException));
return recordFound;
}
}
...但我仍然得到" DBCommandExcept" ...
至于"打开连接时停止前进,"是不是有必要这样做?上面的代码怎么可能/应该不同?
更奇怪的是,现在我的调试日志文件已停止写入。我一直在全局异常处理程序和主窗体的Closed事件()中写出来,并且它总是(至今)至少有几个条目,但在代码的最后几次更新中,它不再被写了...... ????
两个地方全局异常处理程序和主窗体的Closed事件(),代码是这样的:
public static bool inDebugMode = true;
. . .
if (CCR.inDebugMode)
{
DateTime dt = DateTime.Now;
string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
using (StreamWriter file = new StreamWriter(timeAsStr))
{
// If the app closes normally, this is how the file is written; if it doesn't,
// (it crashed) it's written in PDAClient.ExceptionHandler()
file.WriteLine(SSCS.LogMsgs.ToString());
}
}
答案 0 :(得分:2)
由于您正在对数据库文件进行多次调用(不会发生变化),因此我首先将您的连接字符串和SQL语句定义在类的顶部作为全局值:
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize " +
"FROM VendorItems " +
"WHERE VendorID=@VendorID AND VendorItemID=@VendorItemID";
这些永远不会改变,因此每次调用例行程序时都没有理由再次定义它们。
就个人而言,我不喜欢在SQL语句中插入值,就像您所示。相反,尝试使用参数。
要使用参数,您需要查看数据库以查看列VendorID
和VendorItemID
的列类型。我的猜测是它们都是 int 值,但这些值可能是 GUID ,需要VarChar
类型字符串。如果这些是字符串,则应记下列定义的大小。
例如:下面,我的Serial_Number
列为SqlDbType.NVarChar
,大小为50.此列的SqlCeParameter
为:
cmd.Parameters.Add("@Serial_Number", SqlDbType.NVarChar, 50).Value = txtSerial_Number.Text.Trim();
由于我不知道您使用的是哪种类型的数据,因此我创建了一个枚举类型来显示每种方法的使用方式。如果您无法访问该表的设计,最后的手段是“AddWithValue”(我个人讨厌那个,因为它让我看起来像我不知道我的数据库里面有什么)。
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
要使用此枚举类型,我修改了方法的签名以传递该值:
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
显然,你不需要这个,因为你应该知道你要编写什么技术。
我无法弄清楚你的dbconn
对象是什么。最初,我认为这是你的SqlCeConnection
,但是没有isValidTable
方法,所以我只是评论出来了:
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
说到SqlCeConnection
......
我将您的SqlCeCommand
实例与您的SqlCeConnection
实例合并。更少的代码通常意味着更少的错误:
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
默认情况下,CommandType
为CommandType.Text
,因此不需要此行:
// cmd.CommandType = CommandType.Text; (this is the default)
我将大部分变量读取移到try/catch
例程之外,因为这些都不会导致生成异常。
此外,我使用了更具针对性的SqlCeException
而非普通Exception
。块中唯一可能失败的是SqlCe
相关的内容,SqlCeException
将提供比一般Exception
对象更好/更具体的错误消息。
} catch (SqlCeException err) {
那么,它们看起来是什么样的?
enum ParamStyle { AddWithValue, AddIntegers, AddVarChar }
private const int ITEMID_INDEX = 0;
private const int PACKSIZE_INDEX = 1;
private const string SQL_CONN_STR = "Data Source=\\badPlace2B\\CCRDB.SDF";
private const string SQL_GET_VENDOR_ITEMS = "SELECT ItemID, PackSize FROM VendorItems WHERE VendorID=@VendorID AND VendorItemID=@VendorItemID";
private bool PopulateControlsIfVendorItemsFound(ParamStyle style) {
bool recordFound = false;
//DUCKBILL.LogMsgs.Append("Made it into frmEntry.PopulateControlsIfVendorItemsFound()\r\n");
string itemID = null;
string packSize = null;
//string vendorId = txtVendor.Text.Trim();
//string vendorItemId = txtUPC.Text.Trim();
//string query = string.Format("SELECT ItemID, PackSize FROM VendorItems WHERE VendorID = {0} AND VendorItemID = {1}", vendorId, vendorItemId);
//if (dbconn.isValidTable("VendorItems") == -1) {
// DUCKBILL.LogMsgs.Append("VendorItems not a valid table");//do not see this msg; good! VendorItems is seen as valid...
// return false;
//}
using (var cmd = new SqlCeCommand(SQL_GET_VENDOR_ITEMS, new SqlCeConnection(SQL_CONN_STR))) {
// cmd.CommandType = CommandType.Text; (this is the default)
if (style == ParamStyle.AddIntegers) { // Adding Integers:
cmd.Parameters.Add("@VendorID", SqlDbType.Int).Value = Convert.ToInt32(txtVendor.Text.Trim());
cmd.Parameters.Add("@VendorItemID", SqlDbType.Int).Value = Convert.ToInt32(txtUPC.Text.Trim());
} else if (style == ParamStyle.AddVarChar) { // Adding String Values
// NOTE: Here, you should look in your database table and
// use the size you defined for your VendorID and VendorItemID columns.
cmd.Parameters.Add("@VendorID", SqlDbType.VarChar, 25).Value = txtVendor.Text.Trim();
cmd.Parameters.Add("@VendorItemID", SqlDbType.VarChar, 50).Value = txtUPC.Text.Trim();
} else if (style == ParamStyle.AddWithValue) { // Adding as Objects (only if you don't know what the data types are)
cmd.Parameters.AddWithValue("@VendorID", txtVendor.Text.Trim());
cmd.Parameters.AddWithValue("@VendorItemID", txtUPC.Text.Trim());
}
try {
cmd.Connection.Open();
using (var myReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) {
if (myReader.Read()) {
itemID = myReader.GetString(ITEMID_INDEX);
packSize = myReader.GetString(PACKSIZE_INDEX);
recordFound = true;
}
}
} catch (SqlCeException err) {
//DUCKBILL.LogMsgs.Append(string.Format("Exception in PopulateControlsIfVendorItemsFound: {0}\r\n", err.Message));
// (I never return from a 'catch' statement) return recordFound;
} finally {
if (cmd.Connection.State == ConnectionState.Open) {
cmd.Connection.Close();
}
}
}
if (recordFound) { // set these last, and set them OUTSIDE of the try/catch block
txtID.Text = itemID;
txtSize.Text = packSize;
}
return recordFound;
}