使用用户定义的表类型在另一个数据库上调用过程

时间:2013-10-10 12:14:23

标签: c# sql-server ado.net

我与数据库的连接与另一个数据库的连接。我想在另一个具有用户表数据类型参数的数据库上调用一个过程。但是,无论我尝试什么,都找不到用户表数据类型。

  • 我尝试在[dbo]。[myType]前面使用数据库名称,但它不是有效的语法。
  • 我尝试在当前数据库中创建相同的类型
  • 我尝试在模型数据库中创建相同的类型
  • 我尝试在我的SqlCommand.Text顶部添加“USE MyOtherDatabase”

一切都失败了(我真的羞辱了“USE ......”方法失败了。)

我该怎么做?

代码示例:

// While connected to MyOtherDatabase
CREATE TYPE dbo.typeClubMembersVersion AS TABLE (
    ID INT
    , UNIQUE(ID)

    , [version] INT
)
GO

CREATE PROCEDURE dbo.spCheckCMembersMods (
    @pVersions AS dbo.typeClubMembersVersion READONLY
    , @pWhoID AS BIGINT
)
AS
BEGIN
    [...]
END


        SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
        com.CommandText = @"
// While connected to CurrentDatabase
USE MyOtherDatabase

DECLARE @tbl AS dbo.typeClubMembersVersion

BEGIN TRANSACTION
    UPDATE dbo.tClubMembers
    SET
        Title = @Title
    OUTPUT inserted.ID, deleted.[version] INTO @tbl (ID, [version])
    WHERE IdMember = @IdMember

    EXEC dbo.spCheckCMembersMods @tbl, @whoID
COMMIT
";
        com.Parameters.Add("@Title", SqlDbType.NVarChar, 20).Value = this.Title;
        com.Parameters.Add("@IdMember", SqlDbType.BigInt).Value = this.Id;
        com.Parameters.Add("@whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;

        com.Connection.Open();
        try
        {
            com.ExecuteNonQuery();
        }
        catch (Exception exe)
        {
            throw exe;
        }
        finally
        {
            com.Connection.Close();
        }

2 个答案:

答案 0 :(得分:3)

首先,您所谓的“架构”实际上是SQL Server中的“数据库”。 “dbo。”在您的对象名称中是SQL Server中的“架构”。 “USE ..”命令仅适用于数据库。

其次,您无法引用或使用其他数据库中的类型,它必须在与其使用的相同数据库中定义。类型可以在其他SQL Server模式中,但是其他数据库中,这就是您实际尝试在此处执行的操作。


好的,正如您所说,您的类型在[myOtherDatbase]中定义,那么为什么它不起作用?可能是因为USE..和SQL命令字符串不像你想象的那样工作。每当您将这样的字符串传递给SQL Server并尝试执行它时:

com.CommandText = @"
// While connected to CurrentDatabase
USE MyOtherDatabase

DECLARE @tbl AS dbo.typeClubMembersVersion

BEGIN TRANSACTION
    UPDATE dbo.tClubMembers
    SET
        Title = @Title
    OUTPUT inserted.ID, deleted.[version] INTO @tbl (ID, [version])
    WHERE IdMember = @IdMember

    EXEC dbo.spCheckCMembersMods @tbl, @whoID
COMMIT
";

SQL Server将首先编译整个字符串,然后尝试执行它。这意味着所有命令在执行任何之前先编译 。这意味着在 DECLARE @tbl命令执行之前,您的UPDATE..USE命令已编译为。因此,当它们被编译时,您仍然仍然在之前的数据库中Type尚未定义。这就是导致语法错误的原因(来自编译器,不是来自执行)。

有三种可能的解决方案:

  1. 定义currentDatabase中的Type(我很确定这是有效的,但不是100%)。

  2. 使用指定“Initial Catalog = MyOtherDatabase”的连接字符串重新连接。

  3. 使用动态SQL重新执行USE命令后的所有内容。

  4. 其中我会推荐#2。


    愚蠢的我,我才意识到还有另一种选择:

    • 首先只执行USE命令,
    • 然后,在同一个连接上执行其余的SQL命令。

    当然这将使您进入[MyOtherDatabase],因此您可能希望通过执行另一个USE命令将其结束回原始数据库。

答案 1 :(得分:0)

自从我不得不使用SqlConnection.ChangeDatabase以来,已经有很长一段时间了。到目前为止,我总是能够使用“完全命名的对象”来使我的数据库相互交互。
因为我现在卡住了我会使用它,但我希望有人告诉我一种方法,不要强迫我放手当前的数据库连接。

    SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
    com.CommandText = @"
DECLARE @tbl AS dbo.typeClubMembersVersion

BEGIN TRANSACTION
    UPDATE dbo.tClubMembers
    SET
        Title = @Title
    OUTPUT inserted.ID, deleted.[version] INTO @tbl (ID, [version])
    WHERE IdMember = @IdMember

    EXEC dbo.spCheckCMembersMods @tbl, @whoID
COMMIT
";
    com.Parameters.Add("@Title", SqlDbType.NVarChar, 20).Value = this.Title;
    com.Parameters.Add("@IdMember", SqlDbType.BigInt).Value = this.Id;
    com.Parameters.Add("@whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;

    com.Connection.Open();
    try
    {
        com.Connection.ChangeDatabase("MyOtherDatabase");

        com.ExecuteNonQuery();
    }
    catch (Exception exe)
    {
        throw exe;
    }
    finally
    {
        com.Connection.Close();
    }