40

I got tired of writing the following code:

/* Commenting out irrelevant parts
public string MiddleName;
public void Save(){
    SqlCommand = new SqlCommand();
    // blah blah...boring INSERT statement with params etc go here. */
    if(MiddleName==null){
        myCmd.Parameters.Add("@MiddleName", DBNull.Value);
    }
    else{
        myCmd.Parameters.Add("@MiddleName", MiddleName);
    }
    /*
    // more boring code to save to DB.
}*/

So, I wrote this:

public static object DBNullValueorStringIfNotNull(string value)
{
    object o;
    if (value == null)
    {
        o = DBNull.Value;
    }
    else
    {
        o = value;
    }
    return o;
}

// which would be called like:
myCmd.Parameters.Add("@MiddleName", DBNullValueorStringIfNotNull(MiddleName));

If this is a good way to go about doing this then what would you suggest as the method name? DBNullValueorStringIfNotNull is a bit verbose and confusing.

I'm also open to ways to alleviate this problem entirely. I'd LOVE to do this:

myCmd.Parameters.Add("@MiddleName", MiddleName==null ? DBNull.Value : MiddleName);

but that won't work because the "Operator '??' cannot be applied to operands of type 'string and 'System.DBNull'".

I've got C# 3.5 and SQL Server 2005 at my disposal if it matters.

David Murdoch
  • 87,823
  • 39
  • 148
  • 191
  • I wouldn't write the null instance at all - if the value is null, omit from the insert statement. – OMG Ponies Apr 08 '10 at 19:53
  • but that won't work. - please be specific, why people should guess? – Andrey Apr 08 '10 at 19:53
  • 1
    @Andrey: His statement won't compile (the compiler will say there is no implicit conversion between `DBNull` and `string`). – Adam Robinson Apr 08 '10 at 19:54
  • @OMG: That's nice in theory, but it's pretty rare to find people who will write different insert statements depending upon whether or not particular parameters are null. It adds a lot of maintenance overhead with little or no practical benefit (other than being able to take advantage of column default values, of course). – Adam Robinson Apr 08 '10 at 19:55
  • @Andrey: because the "Operator '??' cannot be applied to operands of type 'string and 'System.DBNull'" – David Murdoch Apr 08 '10 at 19:59
  • @OMG. People dynamically create their insert statements based on if the input is null? Man, i'm lazy. – David Murdoch Apr 08 '10 at 20:00
  • @Adam, passing DBNull.Value as a parameter will let the column default values do there thing regardless. – David Murdoch Apr 08 '10 at 20:03
  • Less to transfer over the wire == faster. – OMG Ponies Apr 08 '10 at 20:03
  • 1
    @OMG, how much faster could it possible be (seriously, I'm not familiar with how .net and SQL Server communicate)? With TCP I know most of the time would be spend in RTT for such small data packets. – David Murdoch Apr 08 '10 at 20:09

8 Answers8

59

Cast either of your values to object and it will compile.

myCmd.Parameters.Add("@MiddleName", MiddleName==null ? (object)DBNull.Value : MiddleName);
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 12
    Sweet: `MiddleName ?? (object)DBNull.Value` works! Or better yet `public static readonly object DBNullValue = (object)DBNull.Value;` with `MiddleName ?? DBNullValue`! You are my hero. – David Murdoch Apr 08 '10 at 19:58
  • Argh, i have to wait 3 more minutes before I can accept your answer. – David Murdoch Apr 08 '10 at 20:01
  • @David: Nice! Hadn't even considered coalescing. I'll have to start doing that in my code. I thought about caching the value as you describe, but wanted to keep the code to one line. – Adam Robinson Apr 08 '10 at 20:01
  • @DavidMurdoch: Rather than `MiddleName==null ? (object)DBNull.Value : MiddleName`, you can also use `MiddleName ?? SqlString.Null;` ([Documentation](http://msdn.microsoft.com/en-us/library/system.data.sqltypes.sqlstring.null.aspx)). This might not work on some databases. – Brian Jul 11 '14 at 20:17
26

You can avoid the explicit cast to object using SqlString.Null instead of DBNull.Value:

MiddleName ?? SqlString.Null

There are corresponding types for int, datetime, and so forth. Here's a code snippet with a couple more examples:

 cmd.Parameters.AddWithValue("@StartDate", StartDate ?? SqlDateTime.Null);
 cmd.Parameters.AddWithValue("@EndDate", EndDate ?? SqlDateTime.Null);
 cmd.Parameters.AddWithValue("@Month", Month ?? SqlInt16.Null);
 cmd.Parameters.AddWithValue("@FormatID", FormatID ?? SqlInt32.Null);
 cmd.Parameters.AddWithValue("@Email", Email ?? SqlString.Null);
 cmd.Parameters.AddWithValue("@ZIP", ZIP ?? SqlBoolean.Null);
Lukas S.
  • 5,698
  • 5
  • 35
  • 50
12

Personally this is what I would do with an extension method (make sure this goes into a static class)

public static object GetStringOrDBNull(this string obj)
{
    return string.IsNullOrEmpty(obj) ? DBNull.Value : (object) obj
}

Then you'd have

myCmd.Parameters.Add("@MiddleName", MiddleName.GetStringOrDBNull());
Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • I do like the name. So +1 for that. I like the accepted answer's solution better though. :-) – David Murdoch Apr 08 '10 at 20:11
  • 4
    See if you change your mind after instead of having 100 if statements you have 100 ternary operators. – Chris Marisic Apr 08 '10 at 20:54
  • 2
    I'm actually using a null coalescing operator. You have to agree that `MiddleName ?? DBNullValue` is pretty darn easy. But +1 for your comment too. – David Murdoch Apr 08 '10 at 21:00
  • +1 but I would prefer to use `DBNull.Value` only for actual `null` values and leave empty strings as is. I use `return (object) obj ?? DBNull.Value;` in my version of your extension method. – Justin May 07 '13 at 14:49
  • I have a lot of "such" parameters and extension method is the way to go in this case. Thanks! – Ankur-m Sep 10 '13 at 10:43
  • What's about another types Nullables like int?, decimal?, Datetime?, byte[], short?, etc ? Any full sample about it ? – Kiquenet Nov 28 '13 at 10:50
  • Since you would also do the same for other types, as @Kiquenet points out, I'd personally go for a common method name for all, eg. GetValueOrDbNull(). Makes typing easier, search/replace easier, and indicates they all do the same thing. – ingredient_15939 May 23 '14 at 16:42
6
myCmd.Parameters.Add("@MiddleName", MiddleName ?? (object)DBNull.Value);
hamid reza mansouri
  • 11,035
  • 2
  • 22
  • 32
2

@David Thanks for your suggestion. The following method works great!

MiddleName ?? (object)DBNull.Value
Sagar
  • 579
  • 5
  • 7
1

Yeap, we'd all love to do myCmd.Parameters.Add("@MiddleName", MiddleName ?? DBNull.Value);. Or better still, have the freakin' SqlClient layer understand that CLR null should be mapped to DBNull.Value when adding a parameter. Unfortunately the .Net type system closes the first alternative, and the implementation of SqlClient closes the second.

I'd go with a well known function name, like Coalesce or IsNull. Any DB developer will recognize what they do in an instant, from the name alone.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • 1
    There are good reasons that `null` doesn't map to `DBNull.Value`. Namely that it forces you to assign a value to every parameter, even if that "value" is a database null value. – Adam Robinson Apr 08 '10 at 19:57
  • Furthermore, having the ability to do string ?? DBNull.Value might be nice for this particular circumstance, you have to consider the issue from a compiler perspective: What is the return type of that expression? Should the compiler look for the most common ancestor, even if it's `object`? – Adam Robinson Apr 08 '10 at 20:00
  • Perhaps I wans't clear what I mean by 'the CLR type system': the impossibility to establish the compile time type of the expression `(type1 ?? type2)`. We're saying the same thing. – Remus Rusanu Apr 08 '10 at 20:05
  • I love how you still have a Pony for your avatar. – David Murdoch Apr 08 '10 at 20:05
  • 1
    @David: Yea, I grabbed it and made it persistent :) – Remus Rusanu Apr 08 '10 at 20:07
1

I'd rather give you two totally different suggestions:

  1. Use an ORM. There are plenty of non-intrusive ORM tools.

  2. Write your own wrapper for building commands, with a cleaner interface. Something like:

    public class MyCommandRunner {
      private SqlCommand cmd;
    
      public MyCommandRunner(string commandText) {
        cmd = new SqlCommand(commandText);
      }
    
      public void AddParameter(string name, string value) {
        if (value == null)
         cmd.Parameters.Add(name, DBNull.Value);
        else
          cmd.Parameters.Add(name, value);
      }
    
      // ... more AddParameter overloads
    }
    

If you rename your AddParameter methods to just Add, you can use it in a very slick way:

var cmd = new MyCommand("INSERT ...")
  {
    { "@Param1", null },
    { "@Param2", p2 }
  };
Fábio Batista
  • 25,002
  • 3
  • 56
  • 68
1

I would suggest using nullable properties instead of public fields and an 'AddParameter' method (don't know if this code is optimized or correct, just off the top of my head):


private string m_MiddleName;

public string MiddleName
{
  get { return m_MiddleName; }
  set { m_MiddleName = value; }
}

.
.
.

public static void AddParameter(SQLCommand cmd, string parameterName, SQLDataType dataType, object value)
{
  SQLParameter param = cmd.Parameters.Add(parameterName, dataType);

  if (value is string) { // include other non-nullable datatypes
    if (value == null) {
      param.value = DBNull.Value;
    } else {
      param.value = value;
    }
  } else { 

    // nullable data types
    // UPDATE: HasValue is for nullable, not object type
    if (value.HasValue) // {{{=====================================================
    {
          param.value = value;
    } else 
    {
          param.value = DBNull.Value;
    }
  }
}

.
.
.
AddParameter(cmd, "@MiddleName", SqlDbType.VarChar, MiddleName);

Kiquenet
  • 14,494
  • 35
  • 148
  • 243