0

在我的 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 小时” ... ??? 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);
        }
    }
}

更新

回答布拉德雷姆的评论:

我认为没有必要将参数括在引号中,因为其他工作代码如下:

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 时,你必须指定 SQL Server CE 提供程序字符串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 的示例代码,但是: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

太典型了,这个遗留的、古老的技术项目让我头疼。好像一次只允许打开一个连接,app一开始就打开一个;所以,我必须使用那个连接。但是,它是一个 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。

这段代码的触角太宽太根深蒂固,无法从根部撕下并进行重构以使其更明智:山脚下的一个试探性步骤会导致珠穆朗玛峰上的猛烈雪崩。

4

3 回答 3

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();
        }
    }
于 2013-08-01T14:20:01.940 回答
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 命令中的一样(但没有前导“@”)。

而不是发出对 ExecuteScalar 的调用,我实际上是通过以下方式将数据下拉到 DataTable 中

DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 0;

这样,我要么取回记录,要么不取回记录……如果这样做,记录数应该> 0。由于我没有做“LIKE”,它应该只返回有问题的记录。

当您进入插入、更新和删除操作时,我总是尝试在参数前加上“@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();

这使事情保持集中,您不必担心打开/关闭,连接字符串埋在哪里等等......如果您无法连接,则无法运行查询,不要'如果它甚至不能走那么远,就不要尝试运行查询。

于 2013-08-01T17:07:59.607 回答
0

我认为这可能是 INFORMATION_SCHEMA 系统视图的权限问题。试试下面的。

GRANT VIEW DEFINITION TO your_user;

请参阅此处了解更多详细信息

于 2013-07-31T23:45:11.393 回答