3

Microsoft Azure 技术支持已确认SQL Server 托管实例 CLR支持所有版本的 .Net 4.XX,包括 4.7.2 。

我们正在使用 4.7.2 和最新的 EF 6.2 转换业务层的某些部分

在 CLR 存储过程中使用实体框架

https://patrickdesjardins.com/blog/how-to-use-third-party-dll-reference-in-a-sql-clr-function

因此,为了利用对经过良好测试的代码的现有投资,我们希望将某些业务层移动到数据库中。

但是,我们在发布时遇到了问题。

正在创建 [System.Dynamic]...

警告:Microsoft .NET Framework 程序集 'system.dynamic, version=4.0.0.0,culture=neutral, publickeytoken=b03f5f7f11d50a3a.'

您正在注册的内容未在 SQL Server 托管环境中进行全面测试,因此不受支持。将来,如果您升级或维修此程序集或 .NET Framework,您的 CLR 集成例程可能会停止工作。有关详细信息,请参阅 SQL Server 联机丛书。

(47,1):SQL72014:.Net SqlClient 数据提供者:

消息 6218,级别 16,状态 2,第 1 行为
程序集“System.Dynamic”创建程序集失败,因为程序集“System.Dynamic”验证失败。检查引用的程序集是否是最新的并且受信任(对于 external_access 或不安全)以在数据库中执行。
如果有任何 CLR 验证器错误消息将跟随此消息 [ :
System.Dynamic.ArgBuilder::MarshalToRef][mdToken=0x6000002][offset 0x00000000] 代码大小为零。[:System.Dynamic.ArgBuilder::UnmarshalFromRef][mdToken=0x6000003][偏移量

PS(恕我直言,EF核心也没有关系,尽管我们正在尝试使用EF6.2)

编辑:已授予我所指的所有程序集的 UNSAFE 权限:以下是所有依赖项:

在此处输入图像描述

<ItemGroup>
    <Reference Include="Microsoft.CSharp">
      <HintPath>..\packages2019\dotnet 4.7.2\Microsoft.CSharp.dll</HintPath>
                <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
      <IsModelAware>True</IsModelAware>
      <SpecificVersion>True</SpecificVersion>
    </Reference>
    <Reference Include="System.Dynamic">
      <HintPath>..\packages2019\dotnet 4.7.2\System.Dynamic.dll</HintPath>
                <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
      <IsModelAware>True</IsModelAware>
      <SpecificVersion>True</SpecificVersion>
    </Reference>
    <Reference Include="System.Runtime.Serialization">
      <HintPath>..\packages2019\dotnet 4.7.2\System.Runtime.Serialization.dll</HintPath>
                <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
      <IsModelAware>True</IsModelAware>
      <SpecificVersion>True</SpecificVersion>
    </Reference>
    <Reference Include="SMDiagnostics">
      <HintPath>..\packages2019\v4.0.30319\SMDiagnostics.dll</HintPath>
                <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
      <IsModelAware>True</IsModelAware>
      <SpecificVersion>True</SpecificVersion>
    </Reference>
    <Reference Include="System.ServiceModel.Internals">
      <HintPath>..\packages2019\v4.0.30319\System.ServiceModel.Internals.dll</HintPath>
                <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
      <IsModelAware>True</IsModelAware>
      <SpecificVersion>True</SpecificVersion>
    </Reference>
      <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
            <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <SpecificVersion>True</SpecificVersion>
      <IsModelAware>True</IsModelAware>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
    </Reference>
    <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
            <SqlPermissionSet>UNSAFE</SqlPermissionSet>
      <SpecificVersion>True</SpecificVersion>
      <IsModelAware>True</IsModelAware>
      <GenerateSqlClrDdl>True</GenerateSqlClrDdl>
    </Reference>

编辑 3:Azure SQL Server MI 中的不安全代码 在此处输入图像描述

编辑4:

  • 表演停止者是:System.RunTime.Serialization
CREATE ASSEMBLY [System.Runtime.Serialization]
    AUTHORIZATION [dbo]
    FROM 0x4D5...
    WITH PERMISSION_SET = UNSAFE;

这阻止我将实体框架 UNSAFE 程序集创建到数据库中。我们可以通过 system.Runtime.Serialization 吗?

GO
CREATE ASSEMBLY [EntityFramework]
    AUTHORIZATION [dbo]
    FROM 0x4D5A90...
    WITH PERMISSION_SET = UNSAFE;


GO
PRINT N'Creating [EntityFramework.SqlServer]...';


GO
CREATE ASSEMBLY [EntityFramework.SqlServer]
    AUTHORIZATION [dbo]
    FROM 0x4...
    WITH PERMISSION_SET = UNSAFE;

警告:Microsoft .NET Framework 程序集“system.runtime.serialization,version=4.0.0.0,culture=neutral,publickeytoken=b77a5c561934e089。” 您注册的内容未在 SQL Server 托管环境中进行全面测试,因此不受支持。将来,如果您升级或维修此程序集或 .NET Framework,您的 CLR 集成例程可能会停止工作。有关详细信息,请参阅 SQL Server 联机丛书。消息 6218,级别 16,状态 2,第 11 行为程序集“System.Runtime.Serialization”创建程序集失败,因为程序集“System.Runtime.Serialization”失败 确认。检查引用的程序集是否是最新的并且受信任(对于 external_access 或不安全)以在数据库中执行。如果有任何 CLR 验证器错误消息将跟随此消息 [ : System.AppContextDefaultValues::PopulateDefaultValues][mdToken=0x6000001] [offset 0x00000000] 代码大小为零。

4

2 回答 2

2

SQL Server 的 CLR 主机将使用系统上安装的最高版本的 .NET Framework,用于链接到的 CLR 版本。SQL Server 2005 - 2008 R2 链接到 CLR 2.0 版,因此它们将使用 .NET Framework 2.0、3.0 和 3.5 版。SQL Server 2012 和更新版本都链接到 CLR 4.0 版,因此将使用 .NET Framework 4.x 版。

话虽如此,一些框架库是内置的,不需要手动添加。这些都在这里列出:

支持的 .NET Framework 库

如果您需要不在该列表中的库,您可以自己添加,但这并不意味着您可以添加任意库。SQL Server 只允许纯 MSIL 库,不允许混合模式(混合 = 包含托管和非托管代码)。如果您需要一个混合模式的库,那么您无法将其加载到 SQL Server 中。另外,请记住,即使今天的库是纯 MSIL,这并不意味着它不能在未来的框架更新中转换为混合模式(是的,这已经发生了)。

考虑到所有这些,错误消息中的以下项目:

代码大小为零。

可能表明您正在尝试加载参考库。您需要加载实际的库,而不是它的参考版本。

我在 SQL Server 2017 上尝试了以下操作,并且能够全部加载,尽管并非所有内容都需要显式加载。有些自动加载了其他的,因为它们在同一个文件夹中:

CREATE ASSEMBLY [Microsoft.CSharp]
FROM 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.CSharp.dll'
WITH PERMISSION_SET = UNSAFE;
-- includes System.Dynamic


CREATE ASSEMBLY [System.Runtime.Serialization]
FROM 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Runtime.Serialization.dll'
WITH PERMISSION_SET = UNSAFE;
-- includes SMDiagnostics, System.ServiceModel.Internals

然后检查:

SELECT * FROM sys.assemblies;
/*
Microsoft.CSharp
System.Dynamic
System.Runtime.Serialization
System.ServiceModel.Internals
SMDiagnostics
*/

请注意,目前,我将数据库设置为TRUSTWORTHY ON. 这不是我通常推荐的东西,也可能没有必要,但在我完成关于如何正确轻松地处理这个问题的帖子之前,TRUSTWORTHY就足够了。

我没有要加载的 EntityFramework 库,但是您遇到的错误是第一个错误,而我在上面发布的语句没有收到该错误。如果您能够执行这 2 个语句以加载所有 5 个库,然后在 EntityFramework 上出现错误,那么我们将查看具体的错误消息。

但是,即使您能够加载所有这些 DLL,包括用于 EntityFramework 的两个 DLL,也不能保证您实际上能够使用 EF。EF 可能无法在 SQLCLR 中运行。我不记得具体的 EF,但我知道 SMO,例如,包含用于检测它是否在 SQL Server 中运行的代码,如果是,那么它将引发一个异常,指出它不允许运行在 SQL Server 中。

于 2019-06-30T15:43:33.083 回答
1

Microsoft Azure 技术支持已确认 SQL Server 托管实例 CLR 支持所有版本的 .Net 4.XX,包括 4.7.2

托管实例使用最新版本的 .NET Framework 来托管 SQL CLR 程序集。这并不意味着它支持加载未经测试的 .NET Framework 程序集。请参阅此处的支持声明。

此外,托管实例不允许 UNSAFE CLR 程序集,因为这些程序集使您能够运行任意代码并直接访问服务器资源。

即使您可以将所有这些 .NET Framework 程序集加载到您的数据库中,它也不是托管实例中受支持的解决方案。正如上面的支持声明所表明的那样,您必须使加载到数据库中的 .NET 框架程序集的副本与服务器上的 .NET 框架版本保持同步。服务器上的 .NET Framework 在 Windows 更新中进行了更新。当您管理服务器时,让您的程序集与 Windows 中的版本保持同步已经够难的了。但是当微软给服务器打补丁时,你无法知道你需要更新你的程序集。

在 Azure VM 上运行时,您将能够安装 EF 所需的所有程序集。VM 中的唯一限制是您无法加载混合模式程序集,但我认为 EF(当前)不依赖于其中任何一个。如果您使用涉及加载到数据库中的 .NET Framework 程序集的解决方案进行生产,您可能应该实现一个启动存储过程或计划任务,以便在每次 SQL Server 启动时从 Windows .NET Framework 文件夹中刷新您的数据库程序集。

无论如何,这是一个 powershell 脚本,我能够将 EF6 及其依赖项加载到 SQL Server 数据库中。但请记住,仅仅因为您可以加载程序集,并不意味着它会正常工作。您必须进行广泛的测试以确定您的 EF 代码是否真的可以工作。

但是在 SQL Server 上运行 .NET 代码是不寻常的,而且通常是个坏主意。它与您的数据非常接近,但同一 VNet 上的单独 VM 也是如此。它使您的 SQL Server 更难管理,并且通常有更简单的方法可以通过在 SQL Server 上运行代码来完成您希望获得的任何事情,

此外,如果您在 SQL Server 上本地运行代码,那么它可能没有充分的理由必须是 SQL CLR。您可以在控制台应用程序中运行代码并使用xp_cmdshell或 SQL 代理作业触发它。

无论如何,这是用于注册程序集的 powershell:

$constr = "server=localhost;database=clrtest;integrated security=true"
$folder = "C:\Users\dbrowne\Source\Repos\SqlClrTest\ClassLibrary1\bin\Debug"
$netfx = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319"

$dlls = @(
    "system.dynamic",
    "microsoft.csharp",
    "system.componentmodel.dataannotations",
    "smdiagnostics",
    "system.servicemodel.internals",
    "system.runtime.serialization", 
    "entityframework",
    "entityframework.sqlserver",
    "YourClassLibrary",
    "YourSqlClrProject"
    )


[System.Data.SqlClient.SqlConnection]$con = New-Object System.Data.SqlClient.SqlConnection $constr
$con.Open()

[System.Data.SqlClient.SqlCommand] $cmd = $con.CreateCommand()
$cmd.CommandText = @"

    if cast(serverproperty('ProductMajorVersion') as int) >= 14
    begin


        DECLARE @hash varbinary(64);

        SELECT @hash = HASHBYTES('SHA2_512', @assemblyFile);

        declare @description nvarchar(4000) = @name

        if not exists (select * from sys.trusted_assemblies where hash = @hash)
        begin
          EXEC sys.sp_add_trusted_assembly @hash = @hash,
                                           @description = @description;
          print 'trusted assembly added'
        end

    end

   declare @sql nvarchar(max) 

   if exists (select * from sys.assemblies where name = @name)
   begin

        set @sql =  concat('
        alter assembly ',quotename(@name),'
        FROM @assemblyFile
        WITH PERMISSION_SET = UNSAFE;  
        ')
       EXECUTE sp_executesql @sql, N'@assemblyFile varbinary(max)', @assemblyFile = @assemblyFile;
       print 'updated assembly ' + @name
   end
   else
   begin

        set @sql =  concat('
        create assembly ',quotename(@name),'
        AUTHORIZATION [dbo]
        FROM @assemblyFile
        WITH PERMISSION_SET = UNSAFE;  
        ')

       EXECUTE sp_executesql @sql, N'@assemblyFile varbinary(max)', @assemblyFile = @assemblyFile;
       print 'added assembly ' + @name

   end


"@

$pName = $cmd.Parameters.Add("@name", [System.Data.SqlDbType]::NVarChar, 1000)
$pAssemblyFile = $cmd.Parameters.Add("@assemblyFile", [System.Data.SqlDbType]::VarBinary, -1)



foreach ($targetDll in $dlls)
{
    try
    {
       $pName.Value = $targetDll
       if ([System.IO.File]::Exists("$folder\$targetDll.dll"))
       {
          $pAssemblyFile.Value = [System.IO.File]::ReadAllBytes("$folder\$targetDll.dll")
       }
       else
       {
          $pAssemblyFile.Value = [System.IO.File]::ReadAllBytes("$netfx\$targetDll.dll")

       }

       $result = $cmd.ExecuteNonQuery()

    }
    catch [System.Data.SqlClient.SqlException]
    {
       [System.Data.SqlClient.SqlException] $ex = $_.Exception

       write-host "$($ex.Class) $($ex.Number) $($ex.Message) "

       write-host ""

       continue;
    }
}
$con.Close()
于 2019-06-30T16:14:31.960 回答