SQLBulkCopy with CLR UDT는 " 'MyType'어셈블리의 'MyNamespace.MyType'유형에 대해 'Read'메서드를 찾을 수 없습니다."

sqlbulkcopy sqlclr sql-server sql-server-2012 user-defined-types

문제

SQL Server 2012에 SQL Server CLR 사용자 정의 형식 (UDT)을 작성했습니다. SQL 테스트 스크립트를 통해 액세스 할 수 있었으며이를 로컬 변수로 사용하고이를 테이블에 정의하고이를 통해 테스트했습니다. Visual Studio 및 SQL Server Management Studio.

우리는 SQLBulkCopy를 상당히 일반화 된 방식으로 사용하여 디렉터리에있는 파일을 선택하고 해당 내용을 적절한 테이블에 삽입하는 서비스를 제공합니다. 테이블 중 하나에 UDT를 열로 추가하면 WriteToServer (DataTable) 호출에서 오류가 발생합니다.

UDT의 Parse () 메서드를 SQL Server 내에서 호출하여 내부 형식으로 변환하기를 원하는 UDT 열이 System.String으로 전달됩니다. 또한이 클라이언트 프로그램 내에서 UDT 클래스를 선언하고 데이터를 UDT 유형으로 직접 전달하려고 시도했습니다.

두 경우 모두이 오류 메시지가 나타납니다 (내 독점적 이름을 제거하기 위해 편집 됨)

'MyType'어셈블리에서 'MyNamespace.MyType'유형의 'Read'메서드를 찾을 수 없습니다.

이 오류 메시지와 관련하여 많이 비슷한 질문을 검토했으며, 일반적으로 CREATE 문의 형식을 참조합니다. 또한 CLR 함수는 일반적으로 CLR 함수가 아니라 약간 다른 CLR 함수를 나타냅니다. 이 내 꺼야:

CREATE TYPE [dbo]. [MyType]
외부 이름 [MyType]. [MyNamespace.MyType]

이 문제가 아닌 것 같아요, 대신 SQLBulkCopy가 SQLCLR UDT와 상호 작용하는 방식과 관련이 있습니다. 이 특정 조합에 대한 자세한 설명을 찾는 것은 어렵습니다.

# 1 편집 - 사용자 지정 직렬화입니다.

[Serializable]  
[Microsoft.SqlServer.Server.SqlUserDefinedType( Format.UserDefined, MaxByteSize = -1 )]  
public struct MyType: INullable, IBinarySerialize  

편집 # 2 - 실행 권한이 부여됩니다.

GRANT EXECUTE 
ON TYPE :: MyType
TO PUBLIC 

3 번 수정 된 테스트 코드 편집

CREATE TABLE [dbo].[TestMyType]
(
    [SourceMachine]       [varchar](32)  NULL,
    [Output]              MyType NULL
)

및에 의해 업데이트 됨

try
{
    DataTable dataTable = new DataTable( "[TestMyType]" );
    dataTable.Columns.Add( "SourceMachine", typeof( System.String ) );
    dataTable.Columns.Add( "Output", typeof( MyNamespace.MyType ) );

    dataTable.Rows.Add( "Ron1", MyNamespace.MyType.Parse( "This is string 1" ) );
    dataTable.Rows.Add( "Ron2", MyNamespace.MyType.Parse( "This is string 2" ) );
    dataTable.Rows.Add( "Ron3", MyNamespace.MyType.Parse( "This is string 3" ) );

    SqlBulkCopy sqlBulkCopy = new SqlBulkCopy( conn );
    sqlBulkCopy.DestinationTableName = "[TestMyType]";
    sqlBulkCopy.WriteToServer( dataTable );
}
catch ( Exception ex)
{
    System.Diagnostics.Debug.WriteLine(ex.Message);                            
    throw;
}

위와 동일한 오류 메시지가 나타납니다.

# 4 편집 - 문제에서 SqlBulkCopy 제거
매개 변수가있는 INSERT를 사용하여 문제를 재현했습니다. UDT 인스턴스를 직접 사용하는 매개 변수로 클라이언트에서 서버로 UDT 객체를 전달하도록 설정했습니다.

string sInsert = "INSERT INTO TestMyType VALUES (?, ?)";
SqlCommand command = new SqlCommand(sInsert, conn);
SqlParameter parm1 = new SqlParameter("SourceMachine", "This is Machine 01");
SqlParameter parm2 = new SqlParameter("Output", MyNamespace.MyType.Parse( "This is INSERT 01" ) );
parm2.UdtTypeName = "MyType";
command.Parameters.Add(parm1);
command.Parameters.Add(parm2);
int nResult = command.ExecuteNonQuery();

주는

A first chance exception of type 'System.Data.SqlClient.SqlException'
    occurred in System.Data.dll
Additional information: Could not find method 'Read' for 
    type 'MyNamespace.MyType' in assembly 'MyType'

수락 된 답변

SqlBulkCopy 는 SQLCLR UDT (User-Defined Types)를 잘 처리 할 수 ​​있어야합니다. DbDataReaderDataTable 메서드를 모두 사용하여 성공했습니다.

여기 나를 위해 일한 것입니다 :

C # 코드 ( "클라이언트"는 SQLCLR 저장 프로 시저를 만들었습니다)

using System;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

public class xtra
{

    [SqlProcedure]
    public static void BcpTest(SqlInt32 TheID, SqlString TheConnectionString)
    {
        System.Data.DataTable _DataTable = new System.Data.DataTable();
        _DataTable.Columns.Add("ID", typeof(Int32));
        _DataTable.Columns.Add("SomeDate", typeof(DateTime));
        _DataTable.Columns.Add("SomeData", typeof(Type_HashTable));

        Type_HashTable _Bob = Type_HashTable.Parse(@"testKey=testVal");
        _DataTable.Rows.Add(TheID.Value, DateTime.Now, _Bob);

        _DataTable.Rows.Add(TheID.Value + 1, DateTime.Now,
           Type_HashTable.Parse(@"testKey2=testVal2"));

        SqlBulkCopy _BulkCopy = new SqlBulkCopy(TheConnectionString.Value);
        _BulkCopy.DestinationTableName = "dbo.BulkCopyUDT";

        try
        {
            _BulkCopy.WriteToServer(_DataTable);
        }
        finally
        {
            _BulkCopy.Close();
        }
    }
}

T-SQL 코드

-- DROP TABLE dbo.BulkCopyUDT;
CREATE TABLE dbo.BulkCopyUDT
(
  ID INT NOT NULL CONSTRAINT [PK_BulkCopyUDT] PRIMARY KEY,
  SomeDate DATETIME,
  SomeData [SQL#].[Type_HashTable]
);
GO

GRANT INSERT, SELECT ON dbo.BulkCopyUDT TO [Public];
GRANT EXECUTE ON TYPE::SQL#.Type_HashTable TO [Public];
GO

CREATE PROCEDURE dbo.SqlBulkCopy_Test
(
    @TheID INT,
    @TheConnectionString NVARCHAR(4000) = 
        N'Data Source=(local); Integrated Security=true; Initial Catalog=my_database;'
)
AS EXTERNAL NAME [my_assembly].[xtra].[BcpTest];
GO

ALTER ASSEMBLY [my_assembly] WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO

시험

EXEC dbo.SqlBulkCopy_Test 1;

SELECT *, SomeData.ToString() FROM dbo.BulkCopyUDT;

EXEC dbo.SqlBulkCopy_Test 3,
       N'Data Source=(local); User=test; Password=test; Initial Catalog=my_database;';

SELECT *, SomeData.ToString() FROM dbo.BulkCopyUDT;

SqlBulkCopy 와 매개 변수화 된 특수 쿼리를 사용하여 Console App에서이 작업을 수행했습니다.

using System;
using System.Data;
using System.Data.SqlClient;

namespace SqlBulkCopyUDT
{
    class Program
    {
        static void Main(string[] args)
        {
            int _TheID = Int32.Parse(args[0]);

            string _TheConnectionString = 
              @"Data Source=(local); Integrated Security=true; Initial Catalog=my_database;";
            if (args.Length > 1)
            {
                _TheConnectionString = args[1];
            }

            //DataTable _DataTable = new DataTable();
            //_DataTable.Columns.Add("ID", typeof(Int32));
            //_DataTable.Columns.Add("SomeDate", typeof(DateTime));
            //_DataTable.Columns.Add("SomeData", typeof(Type_HashTable));

            //Type_HashTable _Bob = Type_HashTable.Parse(@"testKey=testVal");
            //_DataTable.Rows.Add(_TheID, DateTime.Now, _Bob);

            //_DataTable.Rows.Add(_TheID + 1, DateTime.Now,
            //   Type_HashTable.Parse(@"testKey2=testVal2"));

            //SqlBulkCopy _BulkCopy = new SqlBulkCopy(_TheConnectionString);
            //_BulkCopy.DestinationTableName = "dbo.BulkCopyUDT";

            //try
            //{
            //    _BulkCopy.WriteToServer(_DataTable);
            //}
            //finally
            //{
            //    _BulkCopy.Close();
            //}

            using (SqlConnection _Connection = new SqlConnection(_TheConnectionString))
            {
                using (SqlCommand _Command = _Connection.CreateCommand())
                {
                    _Command.CommandType = CommandType.Text;
                    _Command.CommandText =
                        @"INSERT INTO dbo.BulkCopyUDT (ID, SomeDate, SomeData)
                         VALUES (@MyID, GETDATE(), @MyData);";

                    SqlParameter _ParamMyID = new SqlParameter("@MyID", SqlDbType.Int);
                    _ParamMyID.Value = _TheID;
                    _Command.Parameters.Add(_ParamMyID);

                    SqlParameter _ParamMyData = new SqlParameter("@MyData", SqlDbType.Udt);
                    _ParamMyData.UdtTypeName = "SQL#.Type_HashTable";
                    _ParamMyData.Value = Type_HashTable.Parse(@"testKey3=testVal3");
                    _Command.Parameters.Add(_ParamMyData);

                    _Connection.Open();
                    _Command.ExecuteNonQuery();
                }
            }
        }
    }
}

추신 : UDT 칼럼에 직접 데이터를 보내는 경우 소스 코드에 따라 SqlBulkCopy 전송하는 유일한 방법이므로 바이너리 형식이어야 합니다 .


인기 답변

나는 UDT의 두 가지 메소드에 대해 명시 적 인터페이스 표기법을 사용했다.

void IBinarySerialize.Read( BinaryReader r )
{
}

void IBinarySerialize.Write( BinaryWriter w )
{
}

그러나 그들은 다음과 같이 정의되어야했습니다.

public void Read( BinaryReader r )
{
}

public void Write( BinaryWriter w )
{
}

SQL Server가 SqlBulkCopy 중에 UDT에서 사용할 올바른 메서드를 식별하지 못하게하고 완전한 MyType 개체를 전달할 때 매개 변수화 된 INSERT를 사용하면 충분합니다.

이 문제는 Visual Studio를 사용하여 IBinarySerialize 인터페이스를 구현 한 스텁 루틴을 추가 할 때 시작되었습니다. struct 정의 맨 위에있는 인터페이스 이름을 마우스 오른쪽 버튼으로 클릭하고 "Implement Interface Explicitly"를 선택했습니다. 한정자없이 메소드 스텁을 생성하려면 "Implement Interface"를 선택해야합니다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.