C# 为什么我的SQL Server CE代码失败?
在我的WindowsCE/Compact Framework(.NET1.1)项目中,我需要在代码中创建一个新表。我想我可以这样做:C# 为什么我的SQL Server CE代码失败?,c#,sql,sql-server-ce,compact-framework,.net-1.1,C#,Sql,Sql Server Ce,Compact Framework,.net 1.1,在我的WindowsCE/Compact Framework(.NET1.1)项目中,我需要在代码中创建一个新表。我想我可以这样做: if (! TableExists("table42")) { CreateTable42(); } public static bool TableExists(string tableName) { try { using (SqlCeConnection sqlConn = new SqlCeConnection(@
if (! TableExists("table42"))
{
CreateTable42();
}
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters[0].Value = tableName;
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (Exception ex)
{
MessageBox.Show("TableExists ex.Message == " + ex.Message);
MessageBox.Show("TableExists ex.ToString() == " + ex.ToString());
MessageBox.Show("TableExists ex.GetBaseException() == " + ex.GetBaseException());
return false;
}
}
…但对TableExists()的调用失败;并向我展示:
TableExists ex.Message ==
TableExists ex.ToString() == System.Data.SqlServerCe.SqlCeException at System.Data.SqlServerCe.SqlConnection.ProcessResults(Int32 hr) at ...at Open(boolean silent) ...
TableExists ex.GetBaseException() == [same as ex.ToString() above]
“Int32小时”???那是什么东西
正如前面在这些环境中所描述的,我无法单步完成这个项目,所以我依赖于对MessageBox.Show()的调用
如果可能感兴趣,相关代码的其余部分是:
public static void CreateTable42()
{
try
{
using (SqlCeConnection con = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
{
con.Open();
using (SqlCeCommand com = new SqlCeCommand(
"create table table42 (setting_id INT IDENTITY NOT NULL PRIMARY KEY, setting_name varchar(40) not null, setting_value(63) varchar not null)", con))
{
com.ExecuteNonQuery();
WriteSettingsVal("table42settingname","table42settingval");
}
}
}
catch (Exception ex)
{
MessageBox.Show("CreateTable42 " + ex.Message);
}
}
public static void WriteSettingsVal(string settingName, string settingVal)
{
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string dmlStr = "insert into tabld42 (setting_name, setting_value) values(?, ?)";
SqlCeCommand cmd = new SqlCeCommand(dmlStr, sqlConn);
cmd.CommandType = CommandType.Text;
cmd.Parameters[0].Value = settingName;
cmd.Parameters[1].Value = settingVal;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show("WriteSettingsVal " + ex.Message);
}
}
}
更新
对Brad Rem评论的答复:
我认为没有必要将参数括在引号中,因为其他工作代码如下:
cmd.Parameters.Add("@account_id", Dept.AccountID);
-以及:
(在循环中,第一次是一种方式,之后是另一种方式(不要问我为什么)
总之,为了让大家开心,我确实更改了TableExists()参数代码,如下所示:
cmd.Parameters[0].Value = tableName;
……为此:
cmd.Parameters.Add("@TABLE_NAME", tableName);
…但我还是得到了完全相同的结果
更新2
在这里()我发现了这样一句话:“注意,在打开SQL Server CE数据库时,必须指定SQL Server CE提供程序字符串。”
他们举了一个例子:
cn.ConnectionString = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=\Northwind.sdf"
我没有那么做,我的控制室是:
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\CCRDB.SDF"))
这可能是我的问题吗
更新3
我接受了这位先生的建议()并为SQLCEExceptions添加了一个陷阱,因此现在:
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\CCRDB.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TABLE_NAME";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters.Add("@TABLE_NAME", tableName);
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
MessageBox.Show("TableExists sqlceex.ToString() == " + sqlceex.ToString());
return false;
. . .
SqlCeException消息是:“存在文件共享冲突。其他进程可能正在使用文件[,,,,],然后“…processresults…open…getinstance…”
更新4
尝试使用ctacke的示例代码,但是:事务是绝对必要的吗?我必须将代码更改为以下内容以适应我的场景/环境,并且不知道应该是什么事务或如何构建它:
public static bool TableExists(string tableName)
{
string sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\HHSDB.SDF"))
{
SqlCeCommand command = new SqlCeCommand(sql, sqlConn);
//command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = sqlConn;
command.CommandText = sql;
int count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
return false;
}
}
更新5
在这段代码中,我得到的错误消息是:“此异常有一个错误消息可用,但无法显示,因为这些消息是可选的,并且当前未安装在此设备上。请安装…NETCFv35.messages.EN.cab”
更新6
非常典型的是,这个遗留的、古老的技术项目让我头疼。似乎一次只允许打开一个连接,而应用程序从一开始就打开一个;因此,我必须使用该连接。但是,它是一个DBConnection,而不是SqlCeConnection,所以我不能使用以下代码:
using (SqlCeCommand com = new SqlCeCommand(
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)", frmCentral.dbconn))
{
com.ExecuteNonQuery();
WriteSettingsVal("beltprinter", "ZebraQL220");
}
…因为作为参数传递给SqlCeCommand构造函数的已打开连接类型是DBCommand,而不是预期的/必需的SqlCeConnection
这段代码的触角太宽太深,根本无法从根上撕下,并进行重构以使其更合理:山麓的一个试探性的步骤会导致珠穆朗玛峰上的一场猛烈的雪崩。我认为这可能是信息架构系统视图的权限问题。请尝试以下方法
GRANT VIEW DEFINITION TO your_user;
为了好玩,我会尝试两件事。首先,将“?”参数替换为像“@tablename”这样的命名参数,看看这是否会改变情况。是的,我知道“?”应该会起作用,但这是一个混乱、丑陋的先例,可能因为它是一个系统表,所以不可靠。是的,这是一个延伸,但值得一试 我要做的第二件事是类似于OpenNETCF ORM的SQLCE实现中的这种方法:
public override bool TableExists(string tableName)
{
var connection = GetConnection(true);
try
{
using (var command = GetNewCommandObject())
{
command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = connection;
var sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
command.CommandText = sql;
var count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
finally
{
DoneWithConnection(connection, true);
}
}
请注意,我甚至没有费心进行参数化,主要是因为我怀疑它是否能提供任何性能优势(让抱怨SQL注入的大群人排队)。这种方法肯定有效——我们已经在许多实时解决方案中部署和使用了它
编辑
为了完整性(尽管我不确定这是否增加了清晰度)
哇…仍然在挣扎…我第一次开始使用手持设备SQL-CE时也是这样。我当前的项目是使用C#.Net 3.5运行的,但我认为您遇到的原则是相同的。以下是我的系统的工作原理,它与您的系统非常相似 首先,连接到手持设备的字符串。它只是
string myConnString = @"Data Source=\MyFolder\MyData.sdf";
没有对sql驱动程序的引用
其次,表格存在
SqlCeCommand oCmd = new SqlCeCommand( "select * from INFORMATION_SCHEME.TABLES "
+ " where TABLE_NAME = @pTableName" );
oCmd.Parameters.Add( new SqlCeParameter( "pTableName", YourTableParameterToFunction ));
“@pTableName”用于区分“TABLE_NAME”列,并绝对防止出现任何歧义问题。该参数没有额外的“@”。在SQL中,@表示要查找变量,“@pTableName”的SqlCeParameter必须与SQL命令中的参数匹配(但不带前导“@”)
我实际上不是调用ExecuteScalar,而是通过
DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 0;
这样,我要么取回记录,要么不取回记录……如果取回,记录数应该大于0。因为我没有执行“喜欢”,它应该只返回有问题的记录
在插入、更新和删除时,我总是尝试在参数前面加上类似于“@pWhateverColumn”的前缀,并确保SqlCeParameter的名称相同,但没有“@”。我没有遇到任何问题,这个项目已经运行多年。是的,它是一个.net 3.5应用程序,但连接和查询的基本基础应该是相同的
如果它都在您的应用程序中,我会尝试创建一个全局静态“连接”对象。然后,使用一个静态方法来处理它。然后,不是在每次“使用”尝试时都创建一个新连接,而是将其更改为
public static class ConnectionHandler
{
static SqlCeConnection myGlobalConnection;
public static SqlCeConnection GetConnection()
{
if( myGlobalConnection == null )
myGlobalConnection = new SqlCeConnection();
return myGlobalConnection;
}
public static bool SqlConnect()
{
GetConnection(); // just to ensure object is created
if( myGlobalConnection.State != System.Data.ConnectionState.Open)
{
try
{
myGlobalConnection.ConnectionString = @"Data Source=\MyFolder\MyDatabase.sdf";
myGlobalConnection.Open();
}
catch( Exception ex)
{
// optionally messagebox, or preserve the connection error to the user
}
}
if( myGlobalConnection.State != System.Data.ConnectionState.Open )
MessageBox.Show( "notify user");
// return if it IS successful at opening the connection (or was already open)
return myGlobalConnection.State == System.Data.ConnectionState.Open;
}
public static void SqlDisconnect()
{
if (myGlobalConnection!= null)
{
if (myGlobalConnection.State == ConnectionState.Open)
myGlobalConnection.Close();
// In case some "other" state, always try to force CLOSE
// such as Connecting, Broken, Fetching, etc...
try
{ myGlobalConnection.Close(); }
catch
{ // notify user if issue}
}
}
}
…在您的其他类/函数中
if( ConnectionHandler.SqlConnect() )
Using( SqlCeConnection conn = ConnectionHandler.GetConnection )
{
// do your stuff
}
…最后,当您的应用程序完成时,或您需要的任何其他时间
ConnectionHandler.SqlDisconnect();
这样可以使事情集中起来,而且您不必担心打开/关闭、连接字符串埋在什么地方等等。如果您无法连接,您就无法运行查询,如果查询甚至无法运行那么远,就不要尝试运行查询。您是否尝试过用单引号括住参数?或者,根据文档,我看起来你仍然需要调用
参数。添加和“准备”。对于更新5:@Brad Rem:我感觉像是用一个Rube Goldberg过滤器包围我的周界(我处理的代码经常让我想起卡夫卡式的Rube Goldberg装置)。SqlCeConnection继承自一个DbConnection,所以只需将它转换回SqlCeConnection即可(或使用frmCentral.dbco
if( ConnectionHandler.SqlConnect() )
Using( SqlCeConnection conn = ConnectionHandler.GetConnection )
{
// do your stuff
}
ConnectionHandler.SqlDisconnect();