在我的WindowsCE / Compact Framework(.NET1.1)项目中,我需要在代码中创建一个新表。我以为我可以这样做:
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 hr”...... ??? Hec Ramsey是什么?
正如之前在这些环境中所记录的那样,我无法单步执行此项目,因此我依赖于对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);
- 和
cmd.Parameters[0].Value = Dept.AccountID;
(它在循环中第一次以某种方式进行,之后是另一种方式(不要问我原因)。
无论如何,只是为了咧嘴笑,我确实改变了TableExists()参数代码:
cmd.Parameters[0].Value = tableName;
......对此:
cmd.Parameters.Add("@TABLE_NAME", tableName);
...但我仍然得到完全相同的结果。
这里(http://msdn.microsoft.com/en-us/library/aa237891(v=SQL.80).aspx)我发现:“注意当您打开SQL Server CE数据库时,必须指定SQL Server CE提供程序字符串。”
他们举了这个例子:
cn.ConnectionString = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=\Northwind.sdf"
我不这样做;我的conn str是:
using (SqlCeConnection sqlConn = new SqlCeConnection(@"Data Source=\my documents\CCRDB.SDF"))
这可能是我的问题吗?
我接受了这个绅士的建议(http://www.codeproject.com/Answers/629613/Why-is-my-SQLServer-CE-code-failing?cmt=487657#answer1),并为SqlCeExcpetions添加了一个捕获器,现在它就是:
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 ...”
尝试使用ctacke的示例代码,但是:交易绝对必要吗?我必须为我的scenario / milieu将代码更改为以下内容,并且不知道Transaction应该是什么或如何构建它:
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;
}
}
使用此代码,我得到的错误消息是,“错误消息可用于此异常,但无法显示,因为这些消息是可选的,并且当前未在此设备上安装。请安装... NETCFv35.Messages。 EN.cab“
通常情况下,这个古老的技术项目给我带来了麻烦。似乎一次只允许一个连接打开,应用程序从一开始就打开一个;所以,我必须使用那个连接。但是,它是一个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");
}
...因为作为arg传递给SqlCeCommand构造函数的已经打开的连接类型是DBCommand,而不是期望/必需的SqlCeConneection。
这段代码的触角太宽而且根深蒂固,无法通过根部和重构来消除它使其更加明智:山麓中的一个试探性步骤会导致珠穆朗玛峰的雪崩。
答案 0 :(得分:1)
为了好玩,我会尝试两件事。首先,替换'?'带有命名参数的参数,如'@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注入)。这种方式绝对有效 - 我们已经在许多实时解决方案中部署和使用它。
修改强>
为了完整性(虽然我不确定它是否增加了清晰度)。
protected virtual IDbConnection GetConnection(bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
var connection = GetNewConnectionObject();
connection.Open();
return connection;
case ConnectionBehavior.HoldMaintenance:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
if (maintenance) return m_connection;
var connection2 = GetNewConnectionObject();
connection2.Open();
return connection2;
case ConnectionBehavior.Persistent:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
return m_connection;
default:
throw new NotSupportedException();
}
}
protected virtual void DoneWithConnection(IDbConnection connection, bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.HoldMaintenance:
if (maintenance) return;
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.Persistent:
return;
default:
throw new NotSupportedException();
}
}
答案 1 :(得分:1)
首先,连接到手持设备的字符串。它只是
string myConnString = @"Data Source=\MyFolder\MyData.sdf";
没有引用sql驱动程序
接下来,TableExists
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命令中的匹配(但没有前导“@”)。
我实际上是通过
将数据拉入DataTable而不是发出对ExecuteScalar的调用。DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 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();
这样可以保持集中,并且您不必担心打开/关闭,连接字符串被埋在哪里等等...如果无法连接,则无法运行查询,如果它甚至无法达到目的,请不要尝试运行查询。
答案 2 :(得分:0)
我认为这可能是INFORMATION_SCHEMA系统视图的权限问题。请尝试以下方法。
GRANT VIEW DEFINITION TO your_user;