views:

121

answers:

4

I've got some data access layer code calls a stored proc and returns scalar values of various datatypes. The syntax is ExecuteDecimal, ExecuteString, etc. I want it to be Execute<string> or Execute<decimal>

I try this implementation and I can't compile, unless I'm doing casting using "(T) value", if I try to check the type and call a method to do the casting, no such luck.

UPDATED question Why do I have to convert to object before converting to T?

UPDATED code

internal T Execute<T>(string storedProcName, Hashtable parameters)
{
      //Next lines do compile (thanks to suggestions from answers!)
      if (typeof(T) == typeof(string))
          return (T) (object) ExecuteScalar(storedProcName, parameters).ToString();
      else if (typeof(T) == typeof(int))
          return (T)(object) Convert.ToInt32(ExecuteScalar(storedProcName, parameters));
      //Next line compiles, but not all things boxed in an object can
      //be converted straight to type T (esp db return values that might contain DbNull.Value, etc)
      return (T)ExecuteScalar(storedProcName, parameters);
}
+1  A: 
typeof(T) == typeof(string)

and for null value check against DBNull.Value

overall method:

internal T Execute<T>(string storedProcName, Hashtable parameters)
{
      object res = ExecuteScalar(storedProcName, parameters);
      if (Convert.IsDBNull(res))
         return default(T); //or handle somehow
      return (T)Convert.ChangeType(res, typeof(T));
}
Andrey
@Andrey - that won't compile.
dcp
You didn't check that! But thanks anyhow, it set me down the right road and I think I got it. I've updated my question with the new code.
MatthewMartin
Ah looks better
MatthewMartin
Actually, I did check your code in the compiler to see if your way would work, because it was definitely cleaner than doing typeof(T). But sadly, it's required. Downvote removed :).
dcp
@dcp I always confuse those types comparisons without IDE
Andrey
@MatthewMartin check the edit. i don't have IDE by hand to check that it works but i think it should :)
Andrey
+4  A: 

Try this:

var result = ExecuteScalar(storedProcName, parameters);
if(Convert.IsDBNull(result))
    return default(T)
if(result is T) // just unbox
    return (T)result;
else            // convert
    return (T)Convert.ChangeType(result, typeof(T));

Updated: fixed DBNull handling

max
Interesting, It looks like ChangeType supports only things that support IConvertable. I'd never seen that technique before. Thanks!
MatthewMartin
@MatthewMartin all primitive types support it. But this method is not good because max didn't check for DBNull
Andrey
Who the hell upvote for it? it throws exception on DBNull
Andrey
that's better now! un-downvoting
Andrey
+1  A: 

try the 'as' keyword

object o = ExecuteScalar(storedProcName, parameters);
string s;
int i;
// .. more

if ((s = o as string) != null)
{
  return s;
}
if ((i = o as int?) != null) // can't use as for value types, so use nullable
{
  return Convert.ToInt32(o);
}
return o as T;
paquetp
Convert.ChangeType does all the stuff for you. and you don't check for DBNull
Andrey
No. The 'as' keyword isn't valid for value types.
Joel Coehoorn
Thanks Joel, you're right. All better now
paquetp
A: 

Perhaps your function might instead be altered to allow the object conversion function to be passed in:

internal T Execute<T>(string storedProcName, Hashtable parameters, Func<object, T> resultConverter)
{
}

Then you could create overloads for the ones you know about:

internal string ExecuteForString(string storedProcName, Hashtable parameters)
{
    return Execute(storedProcName, parameters, (o) => o.ToString());
}

internal int ExecuteForInt(string storedProcName, Hashtable parameters)
{
     return Execute(storedProcName, parameters, Convert.ToInt32);
}

In the case of your DBNull checks, you could either return default(T) or you could add another overload to pass in a default value if DBNull is returned - e.g. in the case of an int it might be better to get back -1 instead of 0.

Reddog
Unfortunately you cannot overload on return types, the argument types need to differ in type and/or number.
Chris Taylor
Interesting I've never seen default(T) of T before I think I will use that. Remove that part that won't compile so I can upvote your answer.
MatthewMartin
Have renamed functions to avoid compilation issues since I think that's preferred rather than ending up with a case of a type that you can't resolve for the caller...
Reddog