SqlParameter에 null 할당


189

다음 코드는 "DBnull에서 int 로의 암시 적 변환이 없습니다"라는 오류를 표시합니다.

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;
parameters[0] = planIndexParameter;

4
당신은 ... 나는 생각 이의를 AgeItem.AgeIndex 캐스팅 할 필요가 stackoverflow.com/questions/202271/...을 (왜, BTW ==? 3 줄의 끝에)
그렉

답변:


341

문제점은 값을 ?:리턴 int하거나 DBNull 유형 값 (호환되지 않는)을 리턴하므로 운영자가 리턴 유형을 판별 할 수 없다는 것 입니다.

물론 AgeIndex 인스턴스를 요구 사항을 object충족시키는 유형으로 캐스팅 할 수 있습니다 ?:.

??다음과 같이 null 병합 연산자를 사용할 수 있습니다

SqlParameter[] parameters = new SqlParameter[1];     
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (object)AgeItem.AgeIndex ?? DBNull.Value;
parameters[0] = planIndexParameter; 

다음은 문제를 설명하는 운영자에 대한 MSDN 설명서 의 인용문입니다.?:

first_expression과 second_expression의 유형이 동일하거나 한 유형에서 다른 유형으로의 내재적 변환이 존재해야합니다.


null을 객체에 캐스트하려고 할 때 예외가 발생하지 않는 이유는 무엇입니까? 나는 그것이 있어야한다고 생각합니다AgeItem.AgeIndex as object
Niels Brinch

@ Niels Brinch, null은 객체이므로 역 참조를 시도하지 않는 한 완벽하게 합법적이므로 예외는 없습니다. 그러나이 예제에서는 오브젝트로 캐스트되는 것이 아니라 실제로 값 유형 인 DBNull.Value입니다. ?? 연산자는 'AgetItem.AgeIndex가 null이면 DBNull.Value를 반환하고 그렇지 않으면 AgeItem.AgeIndex를 returen'이라고 말하면 응답이 객체로 캐스팅됩니다. 자세한 내용은 null 병합 관리자를 참조하십시오. msdn.microsoft.com/ko-kr/library/ms173224.aspx
Chris Taylor

3
기술적으로, null-coalescing 연산자 ??를 사용하는 솔루션은 일반 삼항을 사용하는 것과 동일한 솔루션입니다. ?:여전히 AgeItem.AgeIndex객체 에 캐스트해야합니다 planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
newfurniturey

정규 삼항을 사용하여 ?:유형별 비교를 수행하는 경우 전체 표현식을 캐스팅하면 작동하지 않습니다. 당신은 같은 비 DBNULL 매개 변수를 캐스팅해야한다 :someID == 0 ? DBNull.Value : (object)someID
ingredient_15939

그것은 사실이지만 SqlParameter를 소비하는 함수의 입구 매개 변수로 null 가능 값을 사용해야하는 경우 null 인 경우이 방법으로 오류가 발생하지 않으며 간단한 If-Else 방식을 사용해야합니다. 예를 들면 다음과 같습니다. sample.Text.Trim ()! = ""? func (sample.Text) : DBNull.Value; ? : 및 ??로 작동하지 않습니다.
QMaster

105

허용 된 답변 은 캐스트를 사용하도록 제안합니다. 그러나 대부분의 SQL 유형에는이 캐스트를 피하는 데 사용할 수있는 특수 Null 필드가 있습니다.

예를 들어 SqlInt32.Null"SqlInt32 클래스의이 인스턴스에 할당 할 수있는 DBNull을 나타냅니다."

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;

2
제안이 유망 해 보였으므로 "System.Data.SqlTypes.SqlString.Null"을 시도했지만 작동하지 않습니다. 실제 문자열 "Null"( 'N', 'u', 'l', 'l')을 필드에 넣는 대신 true (널)로 비워 둡니다. 그러나 (object)와 함께 캐스트를 사용하는 이전 2010 "허용 된 답변"?? DBNull.Value가 올바르게 작동합니다. (내가 사용한 ADO.NET 공급자는 SQLite 였지만 차이가 있는지 확실하지 않습니다.) 다른 사람들이 Brian의 팁을 신중하게 테스트하여 null 동작이 예상대로 작동하는지 확인하는 것이 좋습니다.
JasDev

6
@ JasDev : 나는 높은 평가를받는 사용자 (Marc Gravell이라고 생각)에 대한 주석 에서이 트릭을 설명하고 그것이 Microsoft SQL Server에서만 작동한다고 들었습니다.
브라이언

@JasDev 공급자는 SQL Server에서 Brain point가 나올 때의 차이점입니다.
Lankymart

이 답변은 객체에 대한 명시 적 캐스트를 implicit.one으로 대체합니다. 샘플 코드에서 exampleNoCast가 선언 된 객체이므로 여전히 객체로 캐스트됩니다. OP의 코드에서와 같이 값이 개체 유형 인 SqlParameter.Value에 직접 할당 된 경우에도 여전히 캐스트를받습니다.
Scott

31

DBNull.Value저장 프로 시저 내에 기본값이 지정되지 않은 경우 (저장 프로 시저를 사용하는 경우) SQLCommand 내에서 널 매개 변수로 전달해야 합니다. 가장 좋은 방법은 DBNull.Value쿼리 실행 전에 누락 된 매개 변수 를 할당 하는 것이며 foreach를 수행하면 작업이 수행됩니다.

foreach (SqlParameter parameter in sqlCmd.Parameters)
{
    if (parameter.Value == null)
    {
        parameter.Value = DBNull.Value;
    }
}

그렇지 않으면이 줄을 변경하십시오.

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;

다음과 같이 :

if (AgeItem.AgeIndex== null)
    planIndexParameter.Value = DBNull.Value;
else
    planIndexParameter.Value = AgeItem.AgeIndex;

DBNull과 int가 서로 다르므로 조건문에 다른 유형의 값을 사용할 수 없기 때문입니다. 이것이 도움이되기를 바랍니다.


이 답변은 가능한 모든 방법으로 예시하기 때문에 정말 좋습니다. 나는 첫 번째 접근법을 좋아합니다. 나는 보통 EF를 사용하지만이 요구 사항에서는 그것을 할 수 없으며 많은 시간을 절약합니다. 감사!
Leandro

23

한 줄의 코드로 다음을 시도하십시오.

var piParameter = new SqlParameter("@AgeIndex", AgeItem.AgeIndex ?? (object)DBNull.Value);

5

이 시도:

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);

planIndexParameter.IsNullable = true; // Add this line

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex== ;
parameters[0] = planIndexParameter;

5

조건부 (삼항) 연산자를 사용하는 경우 컴파일러는 두 유형 사이에 암시 적 변환이 필요합니다. 그렇지 않으면 예외가 발생합니다.

따라서 다음 중 하나를 캐스팅하여 문제를 해결할 수 있습니다 System.Object.

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : (object) AgeItem.AgeIndex;

그러나 결과가 실제로 예쁘지 않고 항상이 캐스팅을 기억해야하므로 이러한 확장 방법을 대신 사용할 수 있습니다.

public static object GetDBNullOrValue<T>(this T val)
{
    bool isDbNull = true;
    Type t = typeof(T);

    if (Nullable.GetUnderlyingType(t) != null)
        isDbNull = EqualityComparer<T>.Default.Equals(default(T), val);
    else if (t.IsValueType)
        isDbNull = false;
    else
        isDbNull = val == null;

    return isDbNull ? DBNull.Value : (object) val;
}

그런 다음이 간결한 코드를 사용할 수 있습니다.

planIndexParameter.Value = AgeItem.AgeIndex.GetDBNullOrValue();

1

제 생각 에는 SqlCommand 클래스 의 Parameters 속성을 사용하여이를 수행하는 것이 더 좋습니다 .

public static void AddCommandParameter(SqlCommand myCommand)
{
    myCommand.Parameters.AddWithValue(
        "@AgeIndex",
        (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex);
}

그러나 값이 DBNull.Value이면 ADO.NET이 SqlDbType이 무엇인지 추측하는 데 다소 어려움을 겪을 수 있습니다 ..... 이것은 편리하지만 약간 위험합니다 ....
marc_s

1

사용 가능한 Nullable (T) 구조 사용을 고려하십시오. 값이있는 경우에만 값을 설정할 수 있으며 SQL Command 객체는 nullable 값을 인식하고 번거 로움없이 처리합니다.


1
if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0

이 시도:

if (AgeItem.AgeIndex != null)
{
   SqlParameter[] parameters = new SqlParameter[1];
   SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
   planIndexParameter.Value = AgeItem.AgeIndex;
   parameters[0] = planIndexParameter;
}

즉, 매개 변수가 null 인 경우 저장된 proc에 보내지 마십시오 (물론 저장된 proc이 null 매개 변수를 허용한다고 가정하면).


그러나 지금, 당신은 매개 변수를 생략하고 있습니다-저장 프로 시저가 이것에 대해 기뻐할 것입니다. .
marc_s

와. 거친. 매개 변수가 전달되지 않으면 (@AgeIndex int = 0) 저장된 proc을 기본값으로 설정하십시오. 항상 일어난다. 클라이언트는 기본값을 그대로 사용하거나 매개 변수를 전달하여 기본값을 무시할 수 있습니다. 왜 공감해야합니까?
Flipster

0

다음과 같이 해보십시오.

if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0
int? nullableValue = null;
object nullableValueDB
{
   get{
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   }
}

나는 그런 식으로 해결하고 있습니다.


0
if (AgeItem.AgeIndex== null)  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = DBNull);  
else  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = AgeItem.AgeIndex);

0

이것은 내가 단순히하는 일입니다 ...

        var PhoneParam = new SqlParameter("@Phone", DBNull.Value);
        if (user.User_Info_Phone != null)
        {
            PhoneParam.SqlValue = user.User_Info_Phone;
        }

        return this.Database.SqlQuery<CustLogonDM>("UpdateUserInfo @UserName, @NameLast, @NameMiddle, @NameFirst, @Address, @City, @State, @PostalCode, @Phone",
            UserNameParam, NameLastParam, NameMiddleParam, NameFirstParam, AddressParam, CityParam, StateParam, PostalParam, PhoneParam).Single();

0
            dynamic psd = DBNull.Value;

            if (schedule.pushScheduleDate > DateTime.MinValue)
            {
                psd = schedule.pushScheduleDate;
            }


            sql.DBController.RunGeneralStoredProcedureNonQuery("SchedulePush",
                     new string[] { "@PushScheduleDate"},
                     new object[] { psd }, 10, "PushCenter");

0

이를위한 간단한 확장 방법은 다음과 같습니다.

    public static void AddParameter(this SqlCommand sqlCommand, string parameterName, 
        SqlDbType sqlDbType, object item)
    {
        sqlCommand.Parameters.Add(parameterName, sqlDbType).Value = item ?? DBNull.Value;
    }

0

null 검사와 함께 간단한 방법을 사용합니다.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }

1
조건부 논리가 거꾸로되어 있습니까? DBNull.Value가 첫 번째 값이어야합니까?
Mark Schultheiss

확실합니다. 결정된. 감사.
Zhi An

0

실제 프로젝트에서 작업하는 내 코드 삼항 연산자를 살펴보기 전에 sqlparameter를 이것이 나에게 가장 좋은 방법인지 확인하십시오.

    public bool Key_AddExisting
    (
          string clave
        , int? idHito_FileServer
        , int? idTipoDocumental_Almacen
        , string tipoExp_CHJ
        , int idTipoExp_Verti2
        , int idMov_Verti2
    )
    {
        List<SqlParameter> pars = new List<SqlParameter>()
        {
              new SqlParameter { ParameterName = "@Clave", Value = clave }
    LOOK -> , idHito_FileServer == null ? new SqlParameter { ParameterName = "@IdHito_FileServer", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdHito_FileServer", Value = idHito_FileServer }
    LOOK -> , idTipoDocumental_Almacen == null ? new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = idTipoDocumental_Almacen }
            , new SqlParameter { ParameterName = "@TipoExp_CHJ", Value = tipoExp_CHJ }
            , new SqlParameter { ParameterName = "@IdTipoExp_Verti2", Value = idTipoExp_Verti2 }
            , new SqlParameter { ParameterName = "@IdMov_Verti2", Value = idMov_Verti2 }
        };

        string sql = "INSERT INTO [dbo].[Enlaces_ClavesCHJ_MovimientosVerti2] " +
            "( " +
            "  [Clave] " +
            ", [IdHito_FileServer] " +
            ", [IdTipoDocumental_Almacen] " +
            ", [TipoExp_CHJ] " +
            ", [IdTipoExp_Verti2] " +
            ", [IdMov_Verti2] " +
            ") " +
            "VALUES" +
            "( " +
            "  @Clave" +
            ", @IdHito_FileServer" +
            ", @IdTipoDocumental_Almacen" +
            ", @TipoExp_CHJ" +
            ", @IdTipoExp_Verti2" +
            ", @IdMov_Verti2" +
            ")";

        return DbBasic.ExecNonQuery(ref this.conn, sql, pars);
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.