Gán null cho SqlParameter


188

Đoạn mã sau đưa ra lỗi - "Không chuyển đổi ngầm định từ DBnull sang 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
Bạn cần truyền AgeItem.Age Index cho đối tượng tôi nghĩ ... stackoverflow.com/questions/202271/ dọa (btw, tại sao ==ở cuối dòng thứ 3?)
Greg

Câu trả lời:


341

Vấn đề là ?:toán tử không thể xác định kiểu trả về vì bạn đang trả vềint giá trị hoặc giá trị loại DBNull, không tương thích.

Tất nhiên, bạn có thể sử dụng thể hiện của Age Index thành loại objectthỏa mãn ?:yêu cầu.

Bạn có thể sử dụng ??toán tử hợp nhất null như sau

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

Dưới đây là trích dẫn từ tài liệu MSDN cho ?:nhà điều hành giải thích vấn đề

Loại First_expression và second_expression phải giống nhau hoặc một chuyển đổi ngầm định phải tồn tại từ loại này sang loại khác.


Tại sao không có ngoại lệ được ném khi cố gắng bỏ null vào đối tượng? Tôi nghĩ nó nên như vậyAgeItem.AgeIndex as object
Niels Brinch

@Niels Brinch, sẽ không có ngoại lệ vì null là một đối tượng và miễn là bạn không thử làm điều đó, nó hoàn toàn hợp pháp. Tuy nhiên, trong ví dụ này, nó không được tạo thành đối tượng, đó là DBNull.Value thực sự là một loại giá trị. Các ?? toán tử nói 'nếu AgetItem.Age Index là null thì trả về DBNull. Giá trị khác là giữ lại AgeItem.Age Index' thì phản hồi được truyền tới đối tượng. Xem toán tử hợp nhất null để biết thêm chi tiết. msdn.microsoft.com/en-us/l Library / ms173224.aspx
Chris Taylor

3
Về mặt kỹ thuật, giải pháp của bạn bằng cách sử dụng toán tử hợp nhất null ??là giải pháp tương tự như khi bạn sử dụng ternary thông thường ?:- bạn vẫn cần truyền AgeItem.AgeIndexvào một đối tượng : planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
newf nội thất

Nếu bạn đã sử dụng ternary thông thường ?:để thực hiện so sánh cụ thể theo kiểu, thì việc truyền toàn bộ biểu thức sẽ không hoạt động. Bạn phải truyền tham số không phải là dbnull như vậy:someID == 0 ? DBNull.Value : (object)someID
thành

Điều đó đúng nhưng nếu bạn cần sử dụng giá trị null có thể làm tham số đầu vào của hàm dẫn đến việc sử dụng SqlParameter và nếu nó không có lỗi thì cách này không hiệu quả và bạn chỉ nên sử dụng cách If-Else đơn giản. ví dụ: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; sẽ không hoạt động như ?: và ??
QMaster

102

Câu trả lời được chấp nhận cho thấy sử dụng một diễn viên. Tuy nhiên, hầu hết các loại SQL có trường Null đặc biệt có thể được sử dụng để tránh truyền này.

Ví dụ: SqlInt32.Null"Đại diện cho một DBNull có thể được gán cho thể hiện này của lớp SqlInt32."

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

2
Đề xuất này có vẻ đầy hứa hẹn vì vậy tôi đã thử "System.Data.SqlTypes.SqlString.Null" nhưng nó không hoạt động. Nó đặt chuỗi thực tế của "Null" ('N', 'u', 'l', 'l') vào trường thay vì để trống bằng true (null). Tuy nhiên, "câu trả lời được chấp nhận" năm 2010 cũ sử dụng cast với (object) ?? DBNull.Value hoạt động chính xác. (Nhà cung cấp ADO.NET tôi đã sử dụng là SQLite nhưng tôi không chắc liệu điều đó có tạo ra sự khác biệt hay không.) Tôi đề nghị những người khác kiểm tra cẩn thận mẹo của Brian để đảm bảo hành vi null hoạt động như mong đợi.
JasDev

6
@JasDev: Tôi mơ hồ nhớ lại việc mô tả thủ thuật này trong một bình luận cho một người dùng đại diện cao (tôi nghĩ Marc Gravell) và được bảo rằng nó chỉ hoạt động trên Microsoft SQL Server.
Brian

@JasDev nhà cung cấp sẽ là điểm khác biệt mà công cụ này hoạt động trong SQL Server như Brain point.
Lankymart

Câu trả lời này chỉ thay thế một diễn viên rõ ràng cho đối tượng bằng một ẩn. Trong mã mẫu, exampleNoCastđược khai báo đối tượng, do đó, việc truyền vào đối tượng vẫn xảy ra. Nếu, giống như trong mã của OP, giá trị được gán trực tiếp cho SqlParameter.Value cũng thuộc loại đối tượng, thì bạn vẫn nhận được cast.
Scott

31

Bạn cần vượt qua DBNull.Valuedưới dạng tham số null trong SQLCommand, trừ khi giá trị mặc định được chỉ định trong quy trình được lưu trữ (nếu bạn đang sử dụng quy trình được lưu trữ). Cách tiếp cận tốt nhất là gán DBNull.Valuecho bất kỳ tham số bị thiếu nào trước khi thực hiện truy vấn và theo dõi foreach sẽ thực hiện công việc.

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

Nếu không thì thay đổi dòng này:

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

Như sau:

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

Bởi vì bạn không thể sử dụng loại giá trị khác nhau trong câu lệnh có điều kiện, vì DBNull và int khác nhau. Hy vọng điều này sẽ giúp.


Câu trả lời này thực sự tốt vì nó ví dụ theo mọi cách có thể. Tôi thích cách tiếp cận đầu tiên, tôi thường sử dụng EF nhưng trong yêu cầu này không thể thực hiện được và nó giúp tôi tiết kiệm rất nhiều thời gian. Cảm ơn!
Leandro

23

Với một dòng mã, hãy thử điều này:

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

5

Thử cái này:

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

Nếu bạn sử dụng toán tử có điều kiện (ternary), trình biên dịch cần một chuyển đổi ngầm giữa cả hai loại, nếu không bạn sẽ có một ngoại lệ.

Vì vậy, bạn có thể sửa nó bằng cách chuyển một trong cả hai sang System.Object:

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

Nhưng vì kết quả không thực sự đẹp và bạn luôn phải nhớ lần truyền này, nên bạn có thể sử dụng một phương thức mở rộng như vậy để thay thế:

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;
}

Sau đó, bạn có thể sử dụng mã ngắn gọn này:

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

1

Theo tôi, cách tốt hơn là làm điều này với thuộc tính Tham số của lớp SqlCommand :

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

Nhưng nếu giá trị là DBNull.Value, ADO.NET có thể hơi khó đoán SqlDbType có thể là gì ........ điều này thuận tiện - nhưng hơi nguy hiểm ....
marc_s

1

Xem xét sử dụng cấu trúc Nullable (T) có sẵn. Nó sẽ cho phép bạn chỉ đặt các giá trị nếu bạn có chúng và các đối tượng Lệnh SQL của bạn sẽ nhận ra giá trị nullable và xử lý tương ứng mà không gặp rắc rối nào.


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

Thử cái này:

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

Nói cách khác, nếu tham số là null, đừng gửi nó đến Proc được lưu trữ của bạn (tất nhiên, giả sử rằng Proc được lưu trữ chấp nhận tham số null ẩn trong câu hỏi của bạn).


Nhưng bây giờ, bạn chỉ đang bỏ qua một tham số - Tôi rất nghi ngờ quy trình được lưu trữ sẽ hài lòng về điều này .... rất có thể, cuộc gọi sẽ không nêu rõ "không có giá trị cho tham số @Age Index được cung cấp như mong đợi" .... .
marc_s

Ồ Khắc nghiệt. Chỉ cần viết Proc được lưu trữ để mặc định thành một giá trị nếu tham số không được thông qua (@Age Index int = 0). Xảy ra mọi lúc. Máy khách có thể chấp nhận mặc định hoặc ghi đè lên nó bằng cách truyền tham số. Tại sao các downvote?
Flipster

0

hãy thử một cái gì đó như thế này:

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;
   }
}

Tôi đang giải quyết như thế.


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

Đây là những gì tôi chỉ đơn giản làm ...

        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

Một phương pháp mở rộng đơn giản cho việc này sẽ là:

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

0

Tôi sử dụng một phương pháp đơn giản với một kiểm tra null.

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

1
Là logic có điều kiện ngược? DBNull.Value có nên ở cái đầu tiên không?
Mark Schultheiss

Chắc chắn là như vậy. Đã sửa. Cảm ơn.
Tử An

0

Mã của tôi, làm việc trong dự án thực tế Hãy nhìn vào toán tử ternary làm cho tham số sqlparameter đây là cách tốt nhất cho tôi, với các vấn đề:

    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);
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.