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

时间:2013-07-31 23:23:35

标签: 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(@"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);

...但我仍然得到完全相同的结果。

更新2

这里(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"))

这可能是我的问题吗?

更新3

我接受了这个绅士的建议(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 ...”

更新4

尝试使用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;
    }
}

更新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");
}

...因为作为arg传递给SqlCeCommand构造函数的已经打开的连接类型是DBCommand,而不是期望/必需的SqlCeConneection。

这段代码的触角太宽而且根深蒂固,无法通过根部和重构来消除它使其更加明智:山麓中的一个试探性步骤会导致珠穆朗玛峰的雪崩。

3 个答案:

答案 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)

哇......还在挣扎......当我第一次开始使用手持设备SQL-CE时,我也做了。我目前的项目是使用C#.Net 3.5运行,但我认为您遇到的原则是相同的。以下是我的系统的工作原理与您的系统非常相似。

首先,连接到手持设备的字符串。它只是

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;

See here for more details