SQL Server CLR Int64 to SQLInt64 Указанное приведение недопустимо - PullRequest
0 голосов
/ 17 января 2019

Я пытаюсь написать CLR, который позволяет мне запускать WMI Query на SQL Server.

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"]);
    }  
 }

При запуске этой табличной функции я получаю следующую ошибку:

Произошла ошибка при получении новой строки из определенной пользователем таблицы. Функция: System.InvalidCastException: указанное приведение недопустимо. System.InvalidCastException: at WMIQuery.FillRow (Объект 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()

Ответы [ 3 ]

0 голосов
/ 17 января 2019

Как вы узнали, ошибка связана с тем, что m["Capacity"] является неподписанным, не подписанным, Int64. Чтобы исправить, просто используйте класс Convert следующим образом:

        Capacity = new SqlInt64(Convert.ToInt64(m["Capacity"]));
        Freespace = new SqlInt64(Convert.ToInt64(m["Freespace"]));

Я проверил это с вашим кодом, получил ту же ошибку, прежде чем вносить какие-либо изменения, а затем внес изменения, рекомендованные выше, и теперь я получаю правильный вывод.

Хотя это и не является частью проблемы, но в целом (изначально был входной параметр String Querytext): для входных параметров / типов возврата, пожалуйста, используйте типы Sql* вместо собственных типов для большинства типов данных (object для SQL_VARIANT и DateTime для DATETIME2 являются заметными исключениями). Таким образом, используйте SqlString вместо String для параметра Querytext (или просто удалите входной параметр, поскольку он не используется). Вы можете получить собственный .NET string из параметра, используя свойство Value (например, Querytext.Value), которое есть во всех типах Sql* (которое возвращает ожидаемый собственный тип в каждом случае).

Для получения дополнительной информации об использовании SQLCLR, пожалуйста, посетите: Информация SQLCLR

ОДНАКО, и, возможно, что еще более важно: глядя на то, что именно вы запрашиваете через 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
0 голосов
/ 18 января 2019

Тип диска m ["Емкость"] - 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()
0 голосов
/ 17 января 2019

Вы должны использовать и проверить, имеют ли эта строка и столбец правильные значения, которые можно преобразовать в Int64 или нет. Попробуйте как это проверить Здесь .

Пожалуйста, сделайте следующее перед кастом

bool success = Int64.TryParse(Convert.ToString(m["Capacity"]), out long number);
if (success)
{
   Capacity = new SqlInt64((Int64)m["Capacity"]);
}
else
{
   Capacity = 0;
}
...