2009-10-20 8 views
11

SQL 프로 시저 위에 '일반'래퍼를 만들고 있는데 필요한 모든 매개 변수의 이름과 sqltypes를 확인할 수 있지만 '기본'.NET 유형을 얻는 방법은 없습니다. GetNETType 내가해야 할 일이SQL에서 주어진 StoredProcedure 매개 변수의 .NET 유형을 검색하는 방법은 무엇입니까?

SqlParameter param; 
object value; 
object correctParam = param.GetNETType().GetMethod("Parse", 
    new Type[] { typeof(string) }).Invoke(value.ToString()); 
param.Value = correctParam; 

:

내 목표는 그런 짓을하는 것입니다. param.SqlDbType의 스위치로 작성 될 수 있다는 것을 알고 있습니다. 그러나 이것은 짧은 길이이며 짧은 주석으로 처리 된 코드는 낮은 유지 보수를 의미합니다. :)

+0

정확히하려는 것은 무엇입니까? SqlParameters 컬렉션을 가지고 있고 .net 값의 컬렉션을 가지고있을 때 나는 맞습니까? 그리고 값의 컬렉션을 SqlParameter.Value 속성으로 "변환"하고 싶습니까? 예 : SqlParamaters [0].값 = MagicConvert (값 [0]); – Zenuka

답변

12

불행히도이 매핑이 .NET Framework 내부의 코드에서 노출되지 않는다는 것을 알고있는 한, 불행히도. 이 전에 .NET Framework 참조 소스를 살펴본 결과, .NET 코드 내부에는 피하기 위해 노력하는 것과 같이 긴 per-type switch 문이 많이 있지만 그 중 아무 것도 보이지 않는 것으로 나타났습니다. 외부에 노출 될 수 있습니다.

SqlTypes에서 가장 likley .NET 형식으로 매핑하려는 경우 가장 좋은 방법은 매핑 테이블 in the MSDN docs을 코드로 변환하는 것입니다. MSDN의 테이블에는 적어도 두 가지 오류가 있습니다. # 1 : "DateTime2"라는 .NET 유형이 없습니다 (DateTime 사용). "Xml"(SqlXml 사용) 유형도 없습니다.

어쨌든, 내가 사용해온 매핑은 다음과 같습니다 - 스위치 대신 사전을 사용하여 별도의 방법없이 쉽게 액세스 할 수 있습니다.

public static Dictionary<SqlDbType, Type> TypeMap = new Dictionary<SqlDbType, Type> 
{ 
    { SqlDbType.BigInt, typeof(Int64) }, 
    { SqlDbType.Binary, typeof(Byte[]) }, 
    { SqlDbType.Bit, typeof(Boolean) }, 
    { SqlDbType.Char, typeof(String) }, 
    { SqlDbType.Date, typeof(DateTime) }, 
    { SqlDbType.DateTime, typeof(DateTime) }, 
    { SqlDbType.DateTime2, typeof(DateTime) }, 
    { SqlDbType.DateTimeOffset, typeof(DateTimeOffset) }, 
    { SqlDbType.Decimal, typeof(Decimal) }, 
    { SqlDbType.Float, typeof(Double) }, 
    { SqlDbType.Int, typeof(Int32) }, 
    { SqlDbType.Money, typeof(Decimal) }, 
    { SqlDbType.NChar, typeof(String) }, 
    { SqlDbType.NText, typeof(String) }, 
    { SqlDbType.NVarChar, typeof(String) }, 
    { SqlDbType.Real, typeof(Single) }, 
    { SqlDbType.SmallInt, typeof(Int16) }, 
    { SqlDbType.SmallMoney, typeof(Decimal) }, 
    { SqlDbType.Structured, typeof(Object) }, // might not be best mapping... 
    { SqlDbType.Text, typeof(String) }, 
    { SqlDbType.Time, typeof(TimeSpan) }, 
    { SqlDbType.Timestamp, typeof(Byte[]) }, 
    { SqlDbType.TinyInt, typeof(Byte) }, 
    { SqlDbType.Udt, typeof(Object) }, // might not be best mapping... 
    { SqlDbType.UniqueIdentifier, typeof(Guid) }, 
    { SqlDbType.VarBinary, typeof(Byte[]) }, 
    { SqlDbType.VarChar, typeof(String) }, 
    { SqlDbType.Variant, typeof(Object) }, 
    { SqlDbType.Xml, typeof(SqlXml) }, 
}; 

주 당신이 조심해야 한 가지 .NET 유형 (예를 들어, string)는하지 않지만 크기/precision-- 일부 SQL 유형 (예를 들어, varchar가), 크기 제한이 있음. 그래서 가장 가능성이있는 .NET 유형을 알 수 있다는 사실만으로는 충분하지 않습니다 ... 예를 들어, 드라이브 유효성 검사 규칙에이 기능을 사용하는 경우 사용자가 유효하지 않은 (예 : 너무 큰) 입력을 방지 할 수 있어야합니다.) 값은 정밀도와 같은 매개 변수에 대해 더 많이 알고 있습니다. SqlClient 소스를 살펴보면 특수 코드를 사용하여 해당 SQL 정밀도에서 Decimal 형식의 정밀도를 설정하는 등의 경우를 처리합니다.

.NET 형식이 필요한 유일한 이유는 저장된 proc 매개 변수에 데이터를 채울 수 있기 때문에 모든 .NET 값에서 ToString()을 사용하고 문자열을 채우고 SqlParameter의 Value 속성을 확인하고 프레임 워크가 변환/구문 분석을 수행하는지 확인합니다. 예를 들어 XML 또는 Date 매개 변수의 경우 문자열 대신 보낼 수 있습니다.

리플렉션을 사용하여 각 유형에 대해 Parse() 메소드를 찾는 대신 알려진 유형의 유형 목록이 있기 때문에 각 유형에 대해 강력한 유형의 구문 분석 코드를 사용하여 성능을 향상시킬 수 있습니다. 아래 코드를 참조하십시오. (몇 가지 유형 (예 : SqlDbType.UDT는) 반드시 당신이 사람들을 처리하는 방법을 파악해야합니다 method-- 명백한 파서이 없습니다.)

public static Dictionary<SqlDbType, Func<string, object>> TypeMapper = new Dictionary<SqlDbType, Func<string, object>> 
{ 
    { SqlDbType.BigInt, s => Int64.Parse(s)}, 
    { SqlDbType.Binary, s => null }, // TODO: what parser? 
    { SqlDbType.Bit, s => Boolean.Parse(s) }, 
    { SqlDbType.Char, s => s }, 
    { SqlDbType.Date, s => DateTime.Parse(s) }, 
    { SqlDbType.DateTime, s => DateTime.Parse(s) }, 
    { SqlDbType.DateTime2, s => DateTime.Parse(s) }, 
    { SqlDbType.DateTimeOffset, s => DateTimeOffset.Parse(s) }, 
    { SqlDbType.Decimal, s => Decimal.Parse(s) }, 
    { SqlDbType.Float, s => Double.Parse(s) }, 
    { SqlDbType.Int, s => Int32.Parse(s) }, 
    { SqlDbType.Money, s => Decimal.Parse(s) }, 
    { SqlDbType.NChar, s => s }, 
    { SqlDbType.NText, s => s }, 
    { SqlDbType.NVarChar, s => s }, 
    { SqlDbType.Real, s => Single.Parse(s) }, 
    { SqlDbType.SmallInt, s => Int16.Parse(s) }, 
    { SqlDbType.SmallMoney, s => Decimal.Parse(s) }, 
    { SqlDbType.Structured, s => null }, // TODO: what parser? 
    { SqlDbType.Text, s => s }, 
    { SqlDbType.Time, s => TimeSpan.Parse(s) }, 
    { SqlDbType.Timestamp, s => null }, // TODO: what parser? 
    { SqlDbType.TinyInt, s => Byte.Parse(s) }, 
    { SqlDbType.Udt, s => null }, // consider exception instead 
    { SqlDbType.UniqueIdentifier, s => new Guid(s) }, 
    { SqlDbType.VarBinary, s => null }, // TODO: what parser? 
    { SqlDbType.VarChar, s => s }, 
    { SqlDbType.Variant, s => null }, // TODO: what parser? 
    { SqlDbType.Xml, s => s }, 
}; 

이상 사용하는 코드를 예를 들면, 매우 간단합니다 :

 string valueToSet = "1234"; 
     SqlParameter p = new SqlParameter(); 
     p.SqlDbType = System.Data.SqlDbType.Int; 
     p.Value = TypeMapper[p.SqlDbType](valueToSet); 
3

여기 단계가 누락되었다고 생각합니다. 먼저해야 할 일은 sysObject 테이블에 대한 선택 호출 및 내부 조인을 통해 또는 관리 래퍼를 사용하여 저장된 proc의 정의를 데이터베이스에 쿼리하는 것입니다. 그런 다음 반환 된 정보를 기반으로 매개 변수의 유형을 "추론"할 수 있습니다. 당신은 당신이 정확히 무엇을 볼 수 있습니다 당신의 데이터베이스에 대해 두 번째 예에서 SQL을 실행하면 다음

시작하기 위해 MSO lin K와 직접 how to query the database structure

의 예입니다 최대 :

USE AdventureWorks; 
GO 
SELECT SCHEMA_NAME(SCHEMA_ID) AS [Schema], 
SO.name AS [ObjectName], 
SO.Type_Desc AS [ObjectType (UDF/SP)], 
P.parameter_id AS [ParameterID], 
P.name AS [ParameterName], 
TYPE_NAME(P.user_type_id) AS [ParameterDataType], 
P.max_length AS [ParameterMaxBytes], 
P.is_output AS [IsOutPutParameter] 
FROM sys.objects AS SO 
INNER JOIN sys.parameters AS P 
ON SO.OBJECT_ID = P.OBJECT_ID 
WHERE SO.OBJECT_ID IN (SELECT OBJECT_ID 
FROM sys.objects 
WHERE TYPE IN ('P','FN')) 
ORDER BY [Schema], SO.name, P.parameter_id 
GO 
+0

나는 그것이 (다른 사람의 코드를 확장하는) 어떻게 이루어 졌는지는 모르지만 SqlCommand.Parameters를 통해 중재하면 필요한 모든 매개 변수를 보여줍니다. 유형을 어떻게 추론 할 수 있습니까? – nothrow

+0

타입을 실제로 추론 할 수는 없습니다. 어떻게 든 유형을 알아야합니다. 일반적으로 타입은 하드 코딩되어 있지만 자동화 된 방법을 찾고 있으며 타입을 "알기"위해 DB를 쿼리하는 유일한 방법입니다. –

+0

그는 SQL 형식에서 해당 .NET 형식으로 매핑하는 방법이 필요하다고 생각합니다. 예 : Varchar => String, etc. – Knobloch

3

당신은 반드시 암시하고 정확하게이 매개 변수의 값에 따라 변경 될 수 있으므로 입력 ("기본") 올바른 .NET CTS를 추출 할 수 없습니다 - SqlParameter에의 .DbType 및 .SqlDbType을 변경할 수 있으며 프로그래머 (또는 코드 생성 엔진)가 명시 적으로 설정할 수 있습니다. 출력 매개 변수의 경우 .DbType/.SqlDbType은 올바른 시간이 지난 후에도 잘못 될 수 있습니다. 예를 들어 그 값이 갑자기 돌아 오면 .NET 용어에서 예상과 다른. 이 값은 데이터 저장소 및 .NET SqlParameter에 의해 처리되며 명시 적 유형과 함께 최선을 다합니다. SqlParameter의 데이터 값은 .NET 용어로 약하게 입력 된 것으로 간주되어야합니다 (parm.Value 속성의 System.Object 반환 값에서 확인할 수 있음).

가장 좋은 방법은

  1. 를 사용하여 다른 포스터에 의해 설명 된 매핑 방법 중 하나입니다 -는 SQL 매개 변수 유형은 항상 거기에 데이터를 올바른 것이라고 자신의 암시 적 가정을 가지고 물론.
  2. 출력 매개 변수에서 되돌아 오는 값을 테스트하고 연속 값이 같은 종류라고 가정합니다. 물론 그것은 실제로 데이터베이스에 달려 있습니다.
  3. Microsoft SQL 네임 스페이스에 의존하는 대신 다른 전략을 찾으십시오. 앞으로 더 많은 도움이 될 것입니다.

.NET CTS 유형의 값을 테스트하면 System.Type t = paramInstance.Value.GetType();과 같은 모양이됩니다. Null을 지정하면 예외가 발생합니다. 당신은 여전히 ​​스위치 또는 if/else를 사용하여 그것을 적절하게 던질 필요가 있습니다.

1

올바른 SqlType으로 확인할 수 있으면 Reflection이 .NET 형식에 대한 명시 적 캐스트를 가져옵니다. 반환 값은 기본 System.Type이됩니다. 결과 캐싱은 첫 번째 조회에서 perf를 보완해야합니다.

1

그들이 linq to sql t4에서 무엇을하는지보세요. ​​제대로 작동하는 것 같습니다.

코드를 보면 필요한 것을 찾을 수 있습니다.

4

아무에게도 이야기하고 싶지는 않지만 아무도하지 않는 것이 가장 좋은 방법 일 것입니다.

object correctParam = param.GetNETType().GetMethod("Parse", 
    new Type[] { typeof(string) }).Invoke(value.ToString()); 
param.Value = correctParam; 

당신은 당신이 매개 변수에 할당해야합니다 알고있는 문자열 값을 부여하고 있다는 말을하는지, 그리고 당신은 들어갈 수 있음이 어떤 방식으로 그 값을 채우고 싶어?

이유를 생각해보십시오.

param.Value = NetType.Parse(value.toString()) 

이보다 더 나은 이유 명확한 이유가 없습니다 : 다음과 같은 코드가 옳다고 가정하고있다

param.Value = value; 

을하지만, 당신이 그것을 할 싶어하기 때문에, 그것은 가정 안전 보인다 당신은 이것을 시도하고 진짜 문제가 value가 매개 변수를위한 적당한 유형이 아니다는 것을 것을을 발견했다.따라서 당신은 항상 value이 올바른 유형인지 확인하는 마법의 수정을 원합니다. 당신이 진정으로 원하는 것은 가능성이 함수의 매개 변수에 값을 거즈

SetParam(param, value);

. value이 실제로는 object 유형이 아니지만 실제 유형 (예 : int 또는 string) 인 경우 실제로 더 쉽게 만듭니다. SetParam(SqlParam param, int value) 또는 제네릭과 같은 메서드 오버로드를 사용하여 값 유형 SetParam<T>(SqlParam param, T value)을 유추 할 수 있기 때문입니다.

우리가 원하는 기능을 알고 있으므로 우리가 모르는 부분은 이유입니다. 가장 합리적인 시나리오에는 값 유형에 대한 아이디어가 있으며 매개 변수 유형에 대한 아이디어도 있습니다. 매개 변수와 일치하지 않는 값을 이해하지 못하는 매개 변수로 밀어 넣는 방법을 묻습니다.

나는이 요청에 대해 생각할 수있는 두 가지 이유가 있습니다

  1. 당신은 현실에서 유형이 호환되는지 알고, 많이 쓰기 방지하기 위해이 작업을 수행하는 일반적인 방법을 찾고 암호. 따라서 SqlInt 인 매개 변수에 long을 할당하려고 시도하고 있으며 유형 변환 안전 문제를 해결하기 위해 문자열 변환을 사용하고 있습니다.

  2. 실제로 사용하고있는 코드를 이해하지 못했고 수정 사항을 패치하여 작동하고 있습니다.

그것은 당신이있는 경우에 대해 자신에게 정직 정말 중요합니다. 당신이 첫 번째 경우에 경우에, 당신은 내가 비교적 쉽게 설명한 SetParam 같은 방법을 쓸 수 있습니다. switch 문을 작성해야합니다 (또는 위의 최상의 대답과 같은 사전 조회). 당신은 정밀도를 잃어 버리게 될 것입니다. (int로 long을 주조하는 것은 큰 수에는 효과가 없지만, 당신의 Parse는 작동하지 않을 것입니다.)하지만 작동 할 것입니다.

두 번째 경우에는 1 분 동안 중지하십시오. 미래에 더 많은 버그가 생길 수 있음을 인식하십시오 (문자열을 변환하면 유형을 이해하지 못하는 문제는 해결되지 않기 때문에). 당신은 도움이 필요하다는 것을 압니다, 그래서 당신은 Stack Overflow에 있고 도움을 줄 수있는 것이고, 이해하지 못하는 코드베이스를 다루고있는 것입니다. 당신의 질문에 대해 지금 당장 당신이 당신의 상황이라고 생각하는 것보다 더 깊은 구멍을 파고 있다는 것을 말할 수 있습니다. 왜냐하면 당신은 강한 이유없이 (파라미터 유형에 기초한 switch 문을하기 위해) 가장 좋은 대답을 이미 거절했기 때문입니다 .

두 번째 경우에는 실제 문제를보다 완벽하게 설명하지 않는 한 가장 많이 도움이되는 것은 스택 오버플로의 대답이 아닙니다. 가치관이 어디에서 오는지 이해하는 데 도움이되는 것은 무엇입니까 (UI입니까? 규칙은 어떤 규칙을 따르는가, 유형이 일치하지 않는 이유가 있습니까?) 그리고 그들이가는 곳 (호출중인 저장 프로 시저의 정의? 정의 된 매개 변수 유형은 무엇입니까?). 아마 당신이 SqlParam을 제공 한 사람이 이미 당신을 위해 그것을 적절하게 정의 해 놓은 것처럼 SQL을 사용하여 이것을 찾을 필요조차 없다고 상상해보십시오. 정의한 경우 실제로 SQL로 이동하여 즉시 파악해야합니다.

관련 문제