SQL 服务器 CLR Int64 到 SQLInt64 指定的转换无效
SQL Server CLR Int64 to SQLInt64 Specified cast is not valid
我正在尝试编写允许我在 SQL 服务器上 运行 WMI 查询的 CLR。
using System;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Management;
public class WMIQuery
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable InitMethod()
{
ManagementScope scope = new ManagementScope();
scope = new ManagementScope(@"\localhost\root\CIMV2");
scope.Connect();
SelectQuery query = new SelectQuery("SELECT Name, Capacity, Freespace FROM Win32_Volume WHERE DriveType=3");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection retObjectCollection = searcher.Get ( );
return retObjectCollection;
}
public static void FillRow(Object obj, out SqlString Name, out SqlInt64 Capacity, out SqlInt64 Freespace)
{
ManagementObject m = (ManagementObject)obj;
Name = new SqlString((string)m["name"]);
Capacity = new SqlInt64((Int64)m["Capacity"]);
Freespace = new SqlInt64((Int64)m["Freespace"]);
}
}
当 运行 宁 table 值函数时,我得到以下错误:
An error occurred while getting new row from user defined Table Valued
Function : System.InvalidCastException: Specified cast is not valid.
System.InvalidCastException: at WMIQuery.FillRow(Object obj,
SqlString& Name, SqlInt64& Capacity, SqlInt64& Freespace) .
我已经发现问题出在转换上:
Capacity = new SqlInt64((Int64)m["Capacity"]);
Freespace = new SqlInt64((Int64)m["Freespace"]);
我希望有人知道如何解决上述问题?
我测试这个 CLR 的代码是:
CREATE FUNCTION [dbo].[WMIQuery]()
RETURNS TABLE (
[Name] [nvarchar](4000) NULL,
[Capacity] [bigint] NULL,
[Freespace] [bigint] NULL
) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [MyFirstAssembly].[WMIQuery].[InitMethod]
GO
select * from WMIQuery()
您应该使用并检查该行和列是否具有可以转换为 Int64 的正确值。试试如何检查这个 Here.
投射前请执行以下操作
bool success = Int64.TryParse(Convert.ToString(m["Capacity"]), out long number);
if (success)
{
Capacity = new SqlInt64((Int64)m["Capacity"]);
}
else
{
Capacity = 0;
}
正如您所发现的,错误是由于 m["Capacity"]
是一个无符号的 Int64。要修复,只需使用 Convert
class 如下:
Capacity = new SqlInt64(Convert.ToInt64(m["Capacity"]));
Freespace = new SqlInt64(Convert.ToInt64(m["Freespace"]));
我用您的代码对此进行了测试,在进行任何更改之前遇到了同样的错误,然后进行了上面推荐的更改,现在我得到了正确的输出。
虽然不是这里问题的一部分,但只是一般问题(最初有一个 String Querytext
输入参数):对于输入参数/ return 类型,请改用 Sql*
类型大多数数据类型的原生类型(SQL_VARIANT
的 object
和 DATETIME2
的 DateTime
是明显的例外)。因此,使用 SqlString
而不是 String
作为 Querytext
参数(或者只删除未使用的输入参数)。您可以使用 Value
属性(例如 Querytext.Value
)从参数中获取本机 .NET string
,所有 Sql*
类型都具有(return在每种情况下都是预期的本机类型)。
有关一般使用 SQLCLR 的更多信息,请访问:SQLCLR Info
然而, 并且可能更重要的是:查看您通过 WMI 查询的确切内容,看起来您正在获取已经有 DMV 的信息,sys.dm_os_volume_stats
.您可以使用以下查询为您已有文件的所有驱动器/卷获取完全相同的信息:
SELECT DISTINCT vol.[volume_mount_point], vol.[volume_id], vol.[logical_volume_name],
vol.[file_system_type], vol.[total_bytes], vol.[available_bytes],
(vol.[total_bytes] - vol.[available_bytes]) AS [used_bytes]
FROM sys.master_files files
CROSS APPLY sys.dm_os_volume_stats(files.[database_id], files.[file_id]) vol
磁盘 m["Capacity"] 的类型是 UInt64。
我用这个函数来找出使用的数据类型。
m["Capacity"].GetType().ToString();
我修改了 CLR 以仅输出用于该目的的数据类型。
了解类型后,我研究了如何将 UInt64 转换为 Int64,并最终找到了答案:
Int64 int64cap;
Int64.TryParse(m["Capacity"].ToString(), out int64cap);
我不知道这是否是正确的解决方案,但它对我有用。
完整代码如下
public class WMIQuery
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable InitMethod()
{
ManagementScope scope = new ManagementScope();
scope = new ManagementScope(@"\localhost\root\CIMV2");
scope.Connect();
SelectQuery query = new SelectQuery("SELECT Name, Capacity, Freespace FROM Win32_Volume WHERE DriveType=3");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection retObjectCollection = searcher.Get ( );
return retObjectCollection;
}
public static void FillRow(Object obj, out SqlString Name, out SqlDecimal Capacity, out SqlDecimal Freespace)
{
ManagementObject m = (ManagementObject)obj;
Name = new SqlString((string)m["name"]);
Int64 int64cap;
Int64.TryParse(m["Capacity"].ToString(), out int64cap);
decimal decCap;
decCap = int64cap / 1073741824; // to GB
decCap = Math.Round(decCap, 2);
Capacity = new SqlDecimal(decCap);
Int64 int64Free;
Int64.TryParse(m["Freespace"].ToString(), out int64Free);
decimal decFree;
decFree = int64Free / 1073741824; // to GB
decFree = Math.Round(decFree, 2);
Freespace = new SqlDecimal(decFree);
}
}
SQL到运行这个东西:
CREATE ASSEMBLY [MyFirstAssembly]
FROM 'C:\MyFirstAssembly.dll'
WITH PERMISSION_SET = UNSAFE
GO
CREATE FUNCTION [dbo].[WMIQuery]()
RETURNS TABLE (
[Name] [nvarchar](4000) NULL,
[Capacity] decimal(18,2) NULL,
[Freespace] decimal(18,2) NULL
) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [MyFirstAssembly].[WMIQuery].[InitMethod]
GO
select * from WMIQuery()
我正在尝试编写允许我在 SQL 服务器上 运行 WMI 查询的 CLR。
using System;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Management;
public class WMIQuery
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable InitMethod()
{
ManagementScope scope = new ManagementScope();
scope = new ManagementScope(@"\localhost\root\CIMV2");
scope.Connect();
SelectQuery query = new SelectQuery("SELECT Name, Capacity, Freespace FROM Win32_Volume WHERE DriveType=3");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection retObjectCollection = searcher.Get ( );
return retObjectCollection;
}
public static void FillRow(Object obj, out SqlString Name, out SqlInt64 Capacity, out SqlInt64 Freespace)
{
ManagementObject m = (ManagementObject)obj;
Name = new SqlString((string)m["name"]);
Capacity = new SqlInt64((Int64)m["Capacity"]);
Freespace = new SqlInt64((Int64)m["Freespace"]);
}
}
当 运行 宁 table 值函数时,我得到以下错误:
An error occurred while getting new row from user defined Table Valued Function : System.InvalidCastException: Specified cast is not valid. System.InvalidCastException: at WMIQuery.FillRow(Object obj, SqlString& Name, SqlInt64& Capacity, SqlInt64& Freespace) .
我已经发现问题出在转换上:
Capacity = new SqlInt64((Int64)m["Capacity"]);
Freespace = new SqlInt64((Int64)m["Freespace"]);
我希望有人知道如何解决上述问题?
我测试这个 CLR 的代码是:
CREATE FUNCTION [dbo].[WMIQuery]()
RETURNS TABLE (
[Name] [nvarchar](4000) NULL,
[Capacity] [bigint] NULL,
[Freespace] [bigint] NULL
) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [MyFirstAssembly].[WMIQuery].[InitMethod]
GO
select * from WMIQuery()
您应该使用并检查该行和列是否具有可以转换为 Int64 的正确值。试试如何检查这个 Here.
投射前请执行以下操作
bool success = Int64.TryParse(Convert.ToString(m["Capacity"]), out long number);
if (success)
{
Capacity = new SqlInt64((Int64)m["Capacity"]);
}
else
{
Capacity = 0;
}
正如您所发现的,错误是由于 m["Capacity"]
是一个无符号的 Int64。要修复,只需使用 Convert
class 如下:
Capacity = new SqlInt64(Convert.ToInt64(m["Capacity"]));
Freespace = new SqlInt64(Convert.ToInt64(m["Freespace"]));
我用您的代码对此进行了测试,在进行任何更改之前遇到了同样的错误,然后进行了上面推荐的更改,现在我得到了正确的输出。
虽然不是这里问题的一部分,但只是一般问题(最初有一个 String Querytext
输入参数):对于输入参数/ return 类型,请改用 Sql*
类型大多数数据类型的原生类型(SQL_VARIANT
的 object
和 DATETIME2
的 DateTime
是明显的例外)。因此,使用 SqlString
而不是 String
作为 Querytext
参数(或者只删除未使用的输入参数)。您可以使用 Value
属性(例如 Querytext.Value
)从参数中获取本机 .NET string
,所有 Sql*
类型都具有(return在每种情况下都是预期的本机类型)。
有关一般使用 SQLCLR 的更多信息,请访问:SQLCLR Info
然而, 并且可能更重要的是:查看您通过 WMI 查询的确切内容,看起来您正在获取已经有 DMV 的信息,sys.dm_os_volume_stats
.您可以使用以下查询为您已有文件的所有驱动器/卷获取完全相同的信息:
SELECT DISTINCT vol.[volume_mount_point], vol.[volume_id], vol.[logical_volume_name],
vol.[file_system_type], vol.[total_bytes], vol.[available_bytes],
(vol.[total_bytes] - vol.[available_bytes]) AS [used_bytes]
FROM sys.master_files files
CROSS APPLY sys.dm_os_volume_stats(files.[database_id], files.[file_id]) vol
磁盘 m["Capacity"] 的类型是 UInt64。 我用这个函数来找出使用的数据类型。
m["Capacity"].GetType().ToString();
我修改了 CLR 以仅输出用于该目的的数据类型。
了解类型后,我研究了如何将 UInt64 转换为 Int64,并最终找到了答案:
Int64 int64cap;
Int64.TryParse(m["Capacity"].ToString(), out int64cap);
我不知道这是否是正确的解决方案,但它对我有用。
完整代码如下
public class WMIQuery
{
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable InitMethod()
{
ManagementScope scope = new ManagementScope();
scope = new ManagementScope(@"\localhost\root\CIMV2");
scope.Connect();
SelectQuery query = new SelectQuery("SELECT Name, Capacity, Freespace FROM Win32_Volume WHERE DriveType=3");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection retObjectCollection = searcher.Get ( );
return retObjectCollection;
}
public static void FillRow(Object obj, out SqlString Name, out SqlDecimal Capacity, out SqlDecimal Freespace)
{
ManagementObject m = (ManagementObject)obj;
Name = new SqlString((string)m["name"]);
Int64 int64cap;
Int64.TryParse(m["Capacity"].ToString(), out int64cap);
decimal decCap;
decCap = int64cap / 1073741824; // to GB
decCap = Math.Round(decCap, 2);
Capacity = new SqlDecimal(decCap);
Int64 int64Free;
Int64.TryParse(m["Freespace"].ToString(), out int64Free);
decimal decFree;
decFree = int64Free / 1073741824; // to GB
decFree = Math.Round(decFree, 2);
Freespace = new SqlDecimal(decFree);
}
}
SQL到运行这个东西:
CREATE ASSEMBLY [MyFirstAssembly]
FROM 'C:\MyFirstAssembly.dll'
WITH PERMISSION_SET = UNSAFE
GO
CREATE FUNCTION [dbo].[WMIQuery]()
RETURNS TABLE (
[Name] [nvarchar](4000) NULL,
[Capacity] decimal(18,2) NULL,
[Freespace] decimal(18,2) NULL
) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [MyFirstAssembly].[WMIQuery].[InitMethod]
GO
select * from WMIQuery()