C# 对另一个具有用户定义表类型的数据库调用过程

C# 对另一个具有用户定义表类型的数据库调用过程,c#,sql-server,ado.net,C#,Sql Server,Ado.net,我连接到一个数据库,并有权连接到另一个数据库。我想在另一个数据库上调用一个过程,该数据库具有用户表数据类型参数。但是无论我怎么尝试,都找不到用户表数据类型 我尝试在[dbo].[myType]前面使用数据库名称,但它不是有效的语法 我尝试在当前数据库中创建相同的类型 我尝试在模型数据库中创建相同的类型 我尝试在SqlCommand.Text的顶部添加“UseMotherDatabase” 一切都失败了(我真的为“使用…”方法失败而感到羞愧) 我该怎么做 代码示例: // While conn

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

  • 我尝试在[dbo].[myType]前面使用数据库名称,但它不是有效的语法
  • 我尝试在当前数据库中创建相同的类型
  • 我尝试在模型数据库中创建相同的类型
  • 我尝试在SqlCommand.Text的顶部添加“UseMotherDatabase”
一切都失败了(我真的为“使用…”方法失败而感到羞愧)

我该怎么做

代码示例:

// 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


首先,您所谓的“模式”实际上是SQLServer中的“数据库”。对象名称中的“dbo.”是SQLServer中的“模式”。“USE..”命令仅适用于数据库

其次,您不能引用或使用其他数据库中的类型,它必须在使用它的同一个数据库中定义。类型可以在其他SQL Server模式中,但不能在其他数据库中,这就是您在这里实际要做的


好的,正如您所指出的,您的类型是在[MyTherDatBase]中定义的,为什么它不起作用呢?可能是因为
使用..
和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
命令之前编译的。因此,当它们被编译时,您仍然在以前的数据库中,其中未定义
类型。这就是导致语法错误的原因(这些错误来自编译器,而不是它们的执行)

有三种可能的解决方案:

  • 还要在currentDatabase中定义类型(我很确定这是可行的,但不是100%)

  • 重新连接指定“Initial Catalog=MyOtherDatabase”的连接字符串

  • 使用动态SQL命令后重新执行所有操作

  • 我推荐其中2种


    我真傻,我才意识到还有另一个选择:

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

    当然,这会将您留在[MyOtherDatabase]中,因此您可能希望通过执行另一个
    USE
    命令将其返回到原始数据库来结束此操作。

    我已经很长时间没有使用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();
        }
    

    您有权访问其他架构????出现什么错误?列、参数或变量#34:找不到数据类型dbo.typeClubMembersVersion。必须声明表变量“@tbl”。参数或变量'@tbl'的数据类型无效。是的,我对这两个架构都有db owner权限。这两个架构在同一个数据库中?Serge:您所称的“架构”实际上是SQL Server中的“数据库”。对象名称中的“
    dbo.
    ”是SQL Server中的“模式”。“USE..”命令仅适用于数据库。这就是为什么所有潜在的回答者都会对你的问题感到困惑。但是为什么我先称之为“使用”却失败了呢?因为
    USE..
    只是更改了你当前的数据库。一旦进入“myOtherSchema”数据库,就不再定义类型(因为您只在以前的数据库中定义了类型)。您必须在每个需要使用它的数据库中重新定义它。用户表数据类型是在MyOtherDatabase中定义的(这不是您可能已经知道的真实数据库名称)。#1不起作用(这是我尝试过的事情之一)。如果有办法的话,我想避免像#2这样的事情。#2是最好的办法#3是可以做到的,但笨拙而复杂,特别是如果您想确保自己不受SQL注入的影响。
        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();
        }