SqlDataReader 的转换问题

发布于 2024-09-16 23:05:27 字数 1082 浏览 4 评论 0原文

假设我有这个 sql 语句,并且我已经执行了一个 sql 命令来获取数据读取器:

"select 1 union select 2"
//.....
var rdr = cmd.ExecuteReader();

现在我想读取第一行第一列中的值:

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

所以看来您在 C# 中转换为的类型需要与SQL 类型。 IE如果sql类型是bigint,则只能强制转换为long。如果sql类型是int,则只能强制转换为int。没有混合和匹配...

我只是想得到一些可以工作的东西,不管 c# 要求的整数类型和 sql 返回的类型如何,只要理论上你可以将一个转换为另一个。因此,如果 SQL Server 给我一个浮点类型,并且我要求一个 int,我想要通过该转换获得的截断 int。

我的目标是让这个与泛型一起工作,所以当泛型参数与sql server中的数据类型不完全匹配时,我可以让这个函数工作:

List<T> GetFirstColumn<T>(string sql) where T : struct
{
    //get connection, execute reader
    // loop: 
    //    lst.Add(  (T) rdr.GetValue(0));
}

我希望这适用于两个语句:

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

有人有相对轻松的方法吗这样做?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

淡看悲欢离合 2024-09-23 23:05:27

就像 Fredrik 所说,SqlDataReader 的值被装箱。您可以使用 Convert.ToInt32 将装箱值转换为 int,例如:

int i = Convert.ToInt32(read[0]);

即使 SQL Server 返回 bigint 或小数,这也会尝试转换。

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.

那小子欠揍 2024-09-23 23:05:27

System.Convert 将负责转换。

T GetValue<T>(SqlDataReader rdr)
{
    var dbVal = rdr.GetValue(0);
    var csVal = (T)System.Convert.ChangeType(dbVal, typeof(T));
}

注意:如果 T == Nullable,您需要使用反射做一些额外的工作来获取底层类型,并以 typeof(S) 作为类型调用 ChangeType范围。显然,MS 没有使用 .NET 2.0 更新 ChangeType 函数并引入可空值。如果它是可空的,并且 dbVal 是 DBNull,则可以直接返回 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);

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);
愚人国度 2024-09-23 23:05:27

我认为您的问题是 GetValue 返回一个 object。这意味着,如果是 int,您将得到一个装在 object 中的 int。那么你不能直接将其转换为long,而必须首先将其解压为int

var myLong = (long)(int)rdr.GetValue(0);

我想说,使用泛型这将非常棘手。好吧,您可以创建具有两个类型参数的泛型方法;一个指定字段的类型,另一个指定您想要的类型。但我真的不认为有必要; SqlDataReader 已经具有针对各种数据类型的方法,例如 GetInt32GetInt64 等,因此通用方法不会真正提供任何添加的方法在这种情况下的价值。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文