Suppose i have this sql statement and I have executed a sql command to get a datareader:
"select 1 union select 2"
//.....
var rdr = cmd.ExecuteReader();
and now i want to read the value in the first column of the first row:
var myInt = (int)rdr.GetValue(0); //great this works
var myLong = (long)rdr.GetValue(0); //throws cast exception, even though you can cast int to long
So it appears the type you cast to in C# needs to match exactly the SQL type. I.E. If the sql type is bigint, you can only cast to long. If the sql type is int, you can only cast to int. No mix and match...
I just want to get something that works regardless of the type of integer c# asks for and sql returns, as long as you could theoretically cast one to the other. So if SQL Server gives me a floating point type, and I'm asking for an int, I want the truncated int you get from doing that cast.
My goal is to make this work with generics, so I can have this function work when the generic parameter doesn't exactly match the datatype in sql server:
List<T> GetFirstColumn<T>(string sql) where T : struct
{
//get connection, execute reader
// loop:
// lst.Add( (T) rdr.GetValue(0));
}
I'd like this to work for both statments:
var sql1 = "Select 1"; //sql int
var sql2 = "Select cast(1 as bigint)"; //sql equivalent of a long
var lst1 = GetFirstColumn<int>(sql1);
var lst2 = GetFirstColumn<int>(sql2);
Does anyone have a relatively painless way of doing this?
Like Fredrik says, the value from SqlDataReader is boxed. You can convert a boxed value to an int with Convert.ToInt32, like:
int i = Convert.ToInt32(read[0]);
This will try to convert even if SQL Server returns a bigint or a decimal.
System.Convert will take care of the conversion.
T GetValue<T>(SqlDataReader rdr)
{
var dbVal = rdr.GetValue(0);
var csVal = (T)System.Convert.ChangeType(dbVal, typeof(T));
}
Caveat: if T == Nullable<S>, you need to do some extra work with reflection to get the underlying type and call ChangeType with typeof(S) as the type parameter. Apparently, MS didn't update the ChangeType function with .NET 2.0 and the introduction of nullables. And if it's a nullable, and dbVal is DBNull, you can just return null.
object dbValue = 5;
//this throws
Convert.ChangeType(dbValue, typeof(int?));
//this works
if(dbValue == DBNull.Value || dbValue == null)
{
if(typeof(int?).IsNullable) //or is a class, like string
{return null;}
dbValue = null;
}
var type = GetUnderlyingType<int?>(); //== typeof(int)
Convert.ChangeType(dbValue, type);
I think your problem is that GetValue returns an object. This means that in the case of an int, you will get an int boxed in an object. Then you cannot directly cast it to a long but must first unpack it as an int:
var myLong = (long)(int)rdr.GetValue(0);
This will be quite tricky using generics, I would say. Well, you could make generic methods with two type arguments; one specifying what type the field is, and one specifying the type you want. But I don't really see the need; SqlDataReader already has methods for the various data types, such as GetInt32, GetInt64 and so on, so the generic method would not really give any added value in that case.
Related
Long time programmer, new to C#. I am in the process of converting a solution from VB.net to C#. This particular function "getdata" returns values from the first row in a sql select. For this example I've simplified the code.
Due to the unknown datatypes being fetched from sql, the "getdata()" parms are objects. VB allows calling a function with any explicit datatype byref parms into objects, so I can send a string or int parm into an object and return it with no issues.
In C#, this method works for passing parms by value. Any type of byref (ref/in/out) the compiler arrors with "cannot convert from ref string to ref object"
What I've tried:
Changing all manner of parm ref/var type (in/out/ref/nothing)
Changing datatypes of variables from explicit to object works but produces lots of issues downstream. ie. I don't want to define everything as an object/I much prefer explicit datatypes.
explicit cast before calling using (object) before the variable name.
Changing everything to Dynamic types works but same issues as object.
My best solution, unfortunately, changes the functionality enough that it's going to cause issues with further solution conversion. I came up with returning the a/b/c variables as an anonymous object that are that set to the actual variables upon return to the calling function.
Is there any way that calling function parms can be explicitly typed and passed to an implicit data type like object?
If not, any better solution than returning anonymous type?
VB code -- working
Private Sub test()
Dim a$, b%, c$
getdata(1, a, b, c)
MsgBox($"a={a}, b={b}, c={c}")
Dim x As DateTime, y As String, z As String
getdata(2, x, y, z)
MsgBox($"x={x}, y={y}, z={z}")
End Sub
Private Sub getdata(opt As Integer, ByRef val0 As Object, ByRef Optional val1 As Object = Nothing, ByRef Optional val2 As Object = Nothing) As Boolean
'the real implementation of this function will accept sql string and return first row of data columns
'since fetched data will be of different types, parms are defined as objects
If opt = 1 Then
val0 = "Apples"
val1 = 2
val2 = "Oranges"
ElseIf opt = 2 Then
val0 = now
val1 = "Dogs"
val2 = "Cats"
End If
End Function
C# code -- compiler error -
I am hand converting VB code to help with the C# learning curve but my last ditch solution was to use a VB->C# converter which is produced here.
private void test()
{
string a = null;
int b = 0;
string c = null;
getdata(1, ref a, ref b, ref c); ************** error occurs here
MessageBox.Show($"a={a}, b={b}, c={c}"); "cannot convert from ref string to ref object"
DateTime x = default(DateTime);
string y = null;
string z = null;
getdata(2, ref x, ref y, ref z); ************** error occurs here
MessageBox.Show($"x={x}, y={y}, z={z}"); "cannot convert from ref string to ref object"
}
private bool getdata(int opt, ref object val0, ref object val1, ref object val2)
{
//real function will accept sql string and return first row of data columns
//since fetched data will be of different types, parms are defined as objects
if (opt == 1)
{
val0 = "Apples";
val1 = 2;
val2 = "Oranges";
}
else if (opt == 2)
{
val0 = DateTime.Now;
val1 = "Dogs";
val2 = "Cats";
}
return true;
}
There are some fundamental things in this method which make me believe you should spend more time refactoring vs direct translation. Restoring type safety is one of them (VB.Net made it easier to hide some poor type safety choices in a file where you have Option Strict Off for a couple modules), but this also REALLY scares me:
//real function will accept sql string
Functions like that tend to cause HUGE security problems, as well as other issues, especially when you also have a bunch of arguments for output values. If you're not well-versed in SQL Injection, NOW is the time to learn about it. You must also provide a way to include input data for the SQL command that is completely separate from the SQL string itself, or you'll eventually find yourself in big trouble.
This code needs some serious refactoring, not just simple conversion!
I suggest refactoring around a method like this:
public class DB
{
private static string ConnectionString {get;} = "connection string here";
private static IEnumerable<IDataRecord> getdata(string sql, Action<SqlParameterCollection> addParameters)
{
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
}
Notice the method is private; this is because we're not done building the class yet. Once you create this class, and remove the old getdata() method, everywhere that currently calls this method will turn into a compiler error. This is good; it gives you an easy way to find all those places you had poor code of this type.
So now we start looking at the new compiler errors. Each one will represent a place where you used to call getdata(). There's probably other code nearby to build up the SQL string. You want to move each of these sections to a new static method in the DB class.
One of those methods might look something like this:
public static IDataRecord MyNewDataMethod(int ID)
{
string SQL = "SELECT ... WHERE ID = #ID";
return getdata(SQL, p => {
p.Add("#ID", SqlDbType.Int).Value = ID;
}).FirstOrDefault();
}
But we can (and should) take this a step further. Typically, these results will represent objects of some type. After all, they had to come from a table, or at least a set of related tables. If you don't already have a class for each of these things, you probably should. These classes should have static methods named something like FromDataRecord(), which accept an IDataRecord or DataRow as input and return the class type as output. They are Factory methods. And now we update the methods to look more like this:
public static MyObjectType MyNewDataMethod(int MyObjectTypeID)
{
string SQL = "SELECT ... WHERE ID = #ID";
return getdata(SQL, p => {
p.Add("#ID", SqlDbType.Int).Value = MyObjectTypeID;
}).Select(MyObjectType.FromDataRecord).FirstOrDefault();
}
Here's another example that might return several records:
public static IEnumerable<MyObjectType> MyNewDataMethod(string SearchKey)
{
string SQL = "SELECT ... WHERE SearchColumn = #SearchKey + '%'";
return getdata(SQL, p => {
p.Add("#SearchKey", SqlDbType.NVarChar, 80).Value = SearchKey;
}).Select(MyObjectType.FromDataRecord);
}
If you find you have a lot of these methods, you can convert the private getdata() method to protected, put it in it's own class library project in the solution, and use separate public classes in the same project that can still access that method to divide the data access into logical areas.
I agree with Joel's sentiments; throw this code away rather than trying to salvage it. It's garbage.
If you add a reference to the Nuget package Dapper, your life will get a lot easier. With Dapper, you write SQL and it maps to objects for you. It looks something like this:
using(var c = new SqlConnection(connection_string_here){
var person = c.QueryFirst<(string Na, string Ad, int Ag)>("SELECT name, address, age FORM person WHERE id = #id", new { id = 123 });
}
There's a lot going on in this, so i'll unpack it:
The first line just creates a database conenction in a using, so it will be disposed of. You don't need to bother about anything else; Dapper will open the connection, use it, close it
The second line has some parts:
var person = - like Dim x = 1 in VB, var declares a variable that is type-detected by the compiler from whatever type is on the right hand side
c.QueryFirst<(string Na, string Ad, int Ag)> - QueryFirst is a Dapper extension method that runs a select query and pulls the first row. Dapper maps the query columns to the type you give in angle brackets. Here I've given a ValueTuple which is a way to get the C# compiler to "fake" a class for you based on the ValueTuple class. A discussion about how it works is a bit out of scope, but suffice to say when the compiler encounters (string X, string Y, int Z) it transforms behind the scenes into something that you can refer to as an object with those named/typed properties. Suffice to say, when all is done, you'll be able to say person.Na or person.Ad in your code
"SELECT name, address, age FORM person WHERE id = #id" - is a parameterized SQL. It looks up a person with some ID and pulls their data out in that order, name, address, age. The order in this case is important because AFIAWA dapper maps ValueTuples positionally, not by name. This is different to other things (example later) where it does map by name. The tuple has name/address/age, so the query pulls them in the same order
new { id = 123 } - is creating a C# anonymous type, a sort of internal-only compiler generated class (different to a valuetuple) that has no name, but does have a property called id with value 123. Dapper will scan your SQL string looking for parameters, and find one called #id, so it will pull the value 123 out of the supplied anonymous type's id property (name based this time, not positional)
If you have a class Person lying around, as you probably should if you're doing any reasonable amount of database-to-c#-and-back-again work, then the call can look like this:
class Person{
public string Name {get; set;}
public string Address {get; set;}
public int Age {get; set;}
}
...
c.QueryFirst<Person>("SELECT age, name, address FROM ... WHERE id = #i", new { i=123 });
This time we pass a full class Person - Dapper will map the proeprties by name, which is why they're in a different order in the SQL (it could even be SELECT * and dapper will just ignore the 10+ columns in our person table that arent represented by an class property) and it still works. If your SQL names don't match your class names, the simplest thing to do is alias them in the SQL:
c.QueryFirst<Person>("SELECT firstname+' '+lastname as name, ... FROM ... WHERE id = #i", new { i=123 });
I don't think there's an elegant solution - you can keep your 'getdata' method unchanged if you add extra baggage to every method call:
private void test()
{
string a = null;
int b = 0;
string c = null;
object temp_a = a;
object temp_b = b;
object temp_c = c;
getdata(1, ref temp_a, ref temp_b, ref temp_c);
a = (string)temp_a;
b = (int)temp_b;
c = (string)temp_c;
MessageBox.Show($"a={a}, b={b}, c={c}");
}
I ended up with the following
Hashtable gd = getData();
string location = (string)gd["location"];
int locationid = (int)gd["locationid"];
string frutata = (string)gd["frutata"];
where getData() just builds a hashtable of objects with the datareader columns.
My end goal was to create a simple callable function w/o a bunch of code to handle return values.
Dapper seems pretty cool and I will def. check that out.
public List<Workflow> GetMyWorkflows(int[] MyRoles)
{
int[] myWorkflowIDs = new int[] { };
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID;
var distinctWorkflows = query.Distinct();
myWorkflowIDs = distinctWorkflows.toArray();
return myWorkflowIDs;
}
In this method I want to retrieve an array of workflows that a user can
access.
I get the following error : Cannot implicitly convert type 'int?[]' to 'int[]'
I want to retrieve an array of workflows
But your method must return a List<Workflow> or a List<int>.
So you should skip the array idea. The other issue is between int and int?. You can solve that in the select clause with select w.WorkflowID.Value or select w.WorkflowID ?? 0. Or simply select w for a List<Workflow>.
Also it is a good idea to dispose a context when it becomes unreachable.
public List<int> GetMyWorkflows(int[] MyRoles)
{
using (RapidWorkflowDataContext context = new RapidWorkflowDataContext())
{
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID ?? 0;
// select w; to return a List<WorkFlow>
var distinctWorkflows = query.Distinct();
return distinctWorkflows.ToList(); // ToList because we are closing the Context
}
}
I'm going to guess that WorkflowID is of type int?. If you are certain that it cannot be null, change your central query to:
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID.Value;
This will ensure that query is now of type IEnumerable<int> instead of IEnumerable<int?>, with the int following on throuhh the Distinct() and ToArray() functions.
This seems like a pretty good error to me
Cannot convert type 'int?[]' to 'int[]'
You must have an array of type int? and be trying to implicitly convert it to int.
Therefore you have two options - stop trying to implicitly convert, and allow the result to be int?[], like this:
int?[] myWorkflowIDs = new int?[] { };
or force the convert to take place, like this:
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select (int)w.WorkflowID;
// or w.WorkflowID ?? 0; as necessary
So int? can also be written Nullable<int> which is basically an int that can take null values. For example:
int? nullableNumber = 5; // Set to a value
nullableNumber = null? // Set to null (which is possible because int? is nullable).
As you can imagine, Nullable<int> is useful for databases because sometimes you might have a column that has null values, and so this type gives a useful means of mapping to this sort of value. The problem, though is that in your code you have to deal with two different types, int vs. int?. You can cast between the two values by using:
// If the nullable-integer is not-null then use it's value, else default to `0`.
int nonNullable = nullableNumber ?? 0;
which will replace nulls with 0 if the value is null. Or you can just store your myWorkflowIDs in a nullable value (Nullable<int>[] or int?[]), which semantically better reflects what the column value in the database actually is.
I have the following method:
public static T ExecuteScalar<T>(
string query,
SqlConnection connection,
params SqlParameter[] parameters) where T : new()
{
// Create SqlCommand
SqlCommand command = CreateCommand(query, connection, parameters);
// Execute command using ExecuteScalar
object result = command.ExecuteScalar();
// Return value as expected type
if (result == null || result is DBNull) return default(T);
return (T)result;
}
I want to have the MIN_ACTIVE_ROWVERSION of the database as an ulong. The strange thing is.. the First method call below generates an error but the second method call works fine.
Method call 1 generates an error:
ulong minActiveRowversion =
SqlUtils.ExecuteScalar<ulong>(
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
Error:
System.InvalidCastException: Specified cast is not valid.
Method call 2 works fine:
ulong minActiveRowversion =
(ulong)SqlUtils.ExecuteScalar<long>(
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
I don't understand how that is possible because the result of the command.ExecuteScalar() method is this:
object result | 1955612
result.GetType() | {Name = "Int64" FullName = "System.Int64"}
Can someone tell me why the first scenario is not possible and the second scenario works?
Can someone tell me how I can solve it so I can use scenario 1.
Why
You can only unbox a value type to it's original type. In your case, the cast first needs to go to long from object and then to ulong.
See this question for more detail:
Why can't I unbox an int as a decimal?
It also links a blog post by Eric Lippert.
How
One way, as you know, is to cast to the original type before casting to T - unless, of course, the original type is T.
As mentioned in the comments, another way is to use conversion routines (Convert.ToUInt64) and not explicit casting.
This could potentially be achieved using a Func<object, T>:
public static T ExecuteScalar<T>(
Func<object, T> conversionFunctor,
string query,
SqlConnection connection,
params SqlParameter[] parameters) where T : new()
{
// Create SqlCommand
SqlCommand command = CreateCommand(query, connection, parameters);
// Execute command using ExecuteScalar
object result = command.ExecuteScalar();
// Return value as expected type
if (result == null || result is DBNull)
return default(T);
return conversionFunctor(result);
}
Making your call:
ulong minActiveRowversion =
SqlUtils.ExecuteScalar<ulong>(
Convert.ToUInt64,
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
Adam's answer correctly identifies the problem; here is a solution: you can use LINQ to unbox any type, as long as it can be cast to T with a built-in or a custom conversion.
static T UnboxUnchecked<T>(object obj) {
var pe = Expression.Parameter(typeof(object));
return Expression.Lambda<Func<object,T>>(
Expression.Convert(
Expression.Convert(pe, obj.GetType())
, typeof (T)
)
, pe
).Compile()(obj);
}
This method produces a LINQ expression that first unboxes the object to its actual type, and then applies the conversion. Replace the last line of your method
return (T)result;
with
return UnboxUnchecked<T>(result);
to make it work.
Here is a link to an article that explains how to make conversions of this kind more efficient by caching the compiled lambdas.
When reading data from ExecuteReader. It returns me data in IDataReader. When I want to fill this data into my entities I will be assigning Non Nullable values in most of the cases to either Nullable types or Non Nullable types, e.g.:
int id;
string name;
int? age;
byte[] image;
example e = new example();
e.id = (int)dr["id"]; //unbox
e.name = dr["name"].ToString() //will result in empty string if dr["name"] is DBNull
e.name = dr["name"] is DBNull ? null : dr["name"].ToString();
e.age = dr["age"] as int?; // way 1
e.age = dr["age"] is DBNull ? null : (int?)dr["age"]; //way 2
e.image = dr["image"] as byte?;
EDIT
if id is primary key for the table and is NonNullable. But it is being used in another table where this key could be NULL then what should be the approach. Should that entity become NULL or exception should be thrown.
If the object reference is null, the is operator always returns false because there is no object available to check its type.
if (o is Employee) {
Employee e = (Employee) o;
// Use e within the ‘if’ statement.
}
The as operator works just like casting except the as operator will never throw an exception. Instead, if the object can’t be cast, the result is null.
Employee e = o as Employee;
if (e != null) {
// Use e within the ‘if’ statement.
}
Check more : C# is and as operators
If you're sure in result type and it can't be null:
(int)dr["id"]
If result can be null and you know the type:
dr["age"] as int?;
dr["age"] as int? ?? -1; // default value
If result can't be null and you don't know the type:
Convert.ToInt32(dr["age"]);
If result can be null and you don't know the type:
object age = dr["age"]; // can be short, int, etc
!Convert.IsDBNull(age) ? Convert.ToInt32(age) : -1;
My assignments would look as such:
e.id = (int)dr["id"]; //unbox
e.name = dr["name"] as string; // String if string, null if DbNull
e.age = dr["age"] as int?;
e.image = dr["image"] as byte?;
The as operator works best for reference types (such as strings or nullables), because it will return null should the object I'm casting be something else, like a DbNull. This is exactly the same as doing the check manually, but more terse and easy to understand.
Look at the answers to this question for some good examples of generic helper functions for your problem.
In (comments) you asked for more info on my dapper comment; the point I was trying to make is that this is essentially a solved problem, and there are a range of tools to help here, from the complex and feature-rich ORMs (NHibernate, Entity Framework, LLBLGenPro), through middle-grounds (LINQ-to-SQL etc), through to stupidly simple (but very effective) micro-ORMs (dapper-dot-net, Peta.Poco, Simple.Data).
dapper-dot-net is the (freely available, OSS) micro-ORM that underpins stackoverflow/stackexchange. It is based on the ridiculously simple approach of mapping directly from returned column names to member names (fields or properties), such that the usage is simply:
var connection = ... // an open connection
int id = ... // a record to fetch
var singleItem = connection.Query<example>(
"select * from example where id = #id", new {id}).Single();
or
int customerId = ...
var orders = connection.Query<Order>(
"select * from Orders where Status = 'Open' and CustomerId = #customerId",
new {customerId}).ToList();
where example is the type in your code, with members name, id, etc. It takes care of all the heavy lifting:
mapping inputs to parameters (see #id and #customerId, and how their values are provided)
materializing returned records into objects in a very optimised way
a few other tricks like horizontal multi-mapping, multi-grid mapping, automatic IN handling, etc
this means that you don't have to worry about details like null, DBNull, Nullable<T> etc, since dapper already handles all that for you, and is (thanks to some IL voodoo) just as fast at runtime as writing the materialization code yourself.
I have a stored procedure. One of its input parameters is expecting a char(8). I try to convert a string "AAA" to this particular parameter type, which is a DBType.AnsiStringFixedLength.
object v = Convert.ChangeType("AAA", param.DbType.GetTypeCode());
// param is AnsiStringFixedLength
However, all I get is an exception: Input string was not in a correct format.
And the stack trace says: at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) [...]
Why is System.Convert trying to convert a string into a number, even though the prodecure's parameter is expecting a char(8)? How do I solve this? I don't want to use one huge switch case mapping all SQL types to CLR types...
EDIT:
This is the code in question: (A generic method to call any MS SQL stored procedure)
using (SqlConnection conn = new SqlConnection(this.config.ConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = this.config.StoredProcedureName;
conn.Open();
SqlCommandBuilder.DeriveParameters(cmd);
foreach (SqlParameter param in cmd.Parameters)
{
if (param.Direction == ParameterDirection.Input ||
param.Direction == ParameterDirection.InputOutput)
{
try
{
string rawParam = param.ParameterName.Replace("#", "");
if (this.config.Parameters.ContainsKey(rawParam))
{
try
{
param.Value = Convert.ChangeType(this.config.Parameters[rawParam],
param.DbType.GetTypeCode());
}
catch(Exception oops)
{
throw new Exception(string.Format("Could not convert to '{0}'.", param.DbType), oops);
}
}
else
throw new ArgumentException("parameter's not available");
}
catch (Exception e)
{
throw;
}
}
}
cmd.ExecuteNonQuery();
}
}
The actual parameter values are provided by this.config.Parameters - all of them are strings. I iterate through SqlCommand's parameter list and set them accordingly. Converting the string values to the parameter's Sql type is necessary here, and as far as I can see, the Sql type is provided by param.DBType.
You seem to mix up some things here, or I don't get what you try to do. The DbType (an enumeration) inherits Enum and that implements IConvertible -> You can call GetTypeCode(). But - you are now calling Enum.GetTypeCode(), which returns the underlying type. If you didn't specify it (and DbType didn't) any Enum is backed by an int.
What are you trying to solve with the code anyway? Why would you want to change the type of a string if the parameter is a string (although with a fixed length)?
Looking at the question some more it seems even more odd. You have an object v (probably for value?) - what do you care about the type?
object v1 = "Foo";
object v1 = 42;
What is the difference for you? I guess you want to pass the values to something else, but - if you only reference the value as object you might still need to cast it.
Please update your question and explain what you really want to do, what you expect to gain.
Regarding the comment:
I'm using Convert.ChangeType(object
value, TypeCode typeCode), so it's not
really converting into an Enum/int. At
least that's what I thought...
See above: DbType.GetTypeCode() is not what you want. Try it, give me the benefit of the doubt: What do you expect to get from DbType.AnsiStringFixedLength.GetTypeCode()? What is the actual result, if you try it?
Now to your code: You try to set the SqlParameter.Value property to the "correct" type. Two things: According to the documentation you probably want to set the SqlParameter.SqlValue, which is the value using SQL types according to the docs. SqlParameter.Value, on the other hand, is the value using CLR types and allows to infer both DbType and SqlValue. Sidenote, implementation detail: The SqlParameter.SqlValue setter just calls the setter of SqlParameter.Value again...
I would expect that the ADO.NET stuff converts the value on its own, if at all possible. What error are you getting without jumping through this hoops?