C# 为什么我的SQL Server CE代码失败?

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(@

在我的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小时”???那是什么东西

正如前面在这些环境中所描述的,我无法单步完成这个项目,所以我依赖于对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();