"无法加载动态生成的序列化程序集" SQLCLR存储过程中的错误

时间:2015-07-22 18:05:47

标签: c# sql-server-2008-r2 sqlclr

当我尝试从SQL Server 2008 R2执行C#CLR存储过程时出现以下错误:

  

Msg 6522,Level 16,State 1,Procedure PrintShippingDocLabelsToPDF,Line 0
  执行用户定义的例程或聚合期间发生.NET Framework错误" PrintShippingDocLabelsToPDF":
  System.InvalidOperationException:无法加载动态生成的序列化程序集。在某些托管环境中,组件加载功能受到限制,请考虑使用预生成的序列化程序。

     

有关详细信息,请参阅内部异常。 --->

     

System.IO.FileLoadException:主机已禁用LoadFrom(),LoadFile(),Load(byte [])和LoadModule()。   System.IO.FileLoadException:

我的理解是,这正是因为我正在努力而发生的 写入网络共享上的文件。我查看了以下来源:https://msdn.microsoft.com/en-us/library/ms345106(v=sql.105).aspxAccessing Sql FILESTREAM from within a CLR stored procedure,但我仍然迷失了。

我需要做的就是将SSRS中的多个报告写入网络共享上的xxx.pdf文件(其中xxx表示自定义名称)。此功能的代码如下所示,我相当确定下面的部分是导致错误。

// Create a file stream and write the report to it
using (FileStream stream = File.OpenWrite(strFilePath + strFileName))
{
    stream.Write(results, 0, results.Length);
} // END using (FileStream stream = File.OpenWrite(StoredProcedures.strFilePath + StoredProcedures.strFileName))

这是完整的程序。请问有人能指点我吗?我正在紧张的截止日期前工作。作为临时解决方案,也许可以将这些报告写入临时表,然后使用SSIS包将它们移动到文件夹。如果有可能,有人可以给我一个关于如何存储这些文件的指针(varbinary(MAX这些文件大小约为60KB-150KB)?

public static void PrintShippingDocLabelsToPDF()
{
    // Put your code here  
    using (SqlConnection sqlConnTestDB = new SqlConnection("context connection=true"))
    {
        string sql4ConnectionString = "connection string";

        string sqlConnStrTestDB = sql4ConnectionString;

        // Select all unprinted SPSNumbers
        // If IsValidatedByWms == 0 --> SPS Not printed
        // If IsValidatedByWms == 1 --> SPS is printed
        string sqlCmdSlctTblPicking = @"SELECT SPSNumber, IsValidatedByWms AS LabelPrintStatus 
                                      FROM TestDB.dbo.tblPickingSlip WHERE IsValidatedByWms = 0";

        //SqlConnection sqlConnTestDB = new SqlConnection(sqlConnStrTestDB);
        SqlCommand sqlCmdSlctPicking = new SqlCommand(sqlCmdSlctTblPicking, sqlConnTestDB);

        // Select all SPSNumbers that have invoices against them
        // SPS number will be coming from tblPicking
        // If SPSNumber is NULL --> No invoice against SPS
        // If SPSNumber is NOT NULL --> There is an invoice against the SPS
        string sqlCmdStrSlctTblInvoiceItem = @"SELECT DISTINCT SPSNumber 
                                              FROM TestDB.dbo.tblInvoiceItem
                                              WHERE SPSNumber IS NOT NULL ";

        SqlCommand sqlCmdSlctTblInvoiceItem = new SqlCommand(sqlCmdStrSlctTblInvoiceItem, sqlConnTestDB);

        DataTable dtPicking = new DataTable();
        SqlDataAdapter sqlPickingAdapter = new SqlDataAdapter(sqlCmdSlctPicking);
        sqlPickingAdapter.Fill(dtPicking);

        DataTable dtTblInvoiceItem = new DataTable();
        SqlDataAdapter sqlTblInvoiceItemAdapter = new SqlDataAdapter();

        // Update print status of printed lables, labels only print for SPSNumbers that have invoices against them
        string sqlCmdStrUpdate = @"UPDATE TestDB.dbo.tblPickingSlip 
                                   SET IsValidatedByWms = 1
                                   WHERE SPSNumber = ";

        SqlCommand sqlCmdUpdateSlctPicking = new SqlCommand(sqlCmdStrUpdate, sqlConnTestDB);

        string strSpsNumber = null;  // keep track of the SPSNumber

        // Inspect the Picking table
        foreach (DataRow row in dtPicking.Rows)
        {
            if (Convert.ToInt32(row["LabelPrintStatus"]) == 0)
            {
                // a label has not been printed for the associated SPSNumber
                // check if the particualr SPSNumber has an assocaited invoice
                strSpsNumber = row["SPSNumber"].ToString();                    

                // add SPSNumber to query that selects all SPSNumbers that
                // have invoices against them
                string sqlCmdStrSlctTblInvoiceItem2 = sqlCmdStrSlctTblInvoiceItem + "AND SPSNumber = '" + strSpsNumber + "'";
                sqlCmdSlctTblInvoiceItem.CommandText = sqlCmdStrSlctTblInvoiceItem2;

                sqlTblInvoiceItemAdapter.SelectCommand = sqlCmdSlctTblInvoiceItem;
                sqlTblInvoiceItemAdapter.Fill(dtTblInvoiceItem);

                // Inspect tblInvoiceItem and print all SPSNumbers that have invoices against them 
                if (dtTblInvoiceItem != null)
                    if (dtTblInvoiceItem.Rows.Count > 0)
                    {
                        foreach (DataRow r in dtTblInvoiceItem.Rows)
                        {                                
                            // Write the report to the ExportFilePath
                            //WriteReport(strSpsNumber);

                            string ExportFilePath = @"\\testsrv\EXPORT\";  // locaiton where PDF reports will be written.
                            string ReportPath = @"/xxx/Report1";  //  Path to report on modabackupsql reportserver
                            string FileExtentionPDF = @".pdf";

                            PrintShippingDocLabelPDF.REService2005.ReportExecutionService _re;  //  proxy class for the report execution for 

                            // Report arguments 
                            string report = ReportPath;
                            string historyID = null;
                            string deviceInfo = null;
                            string format = @"PDF";
                            Byte[] results;
                            string encoding = String.Empty;
                            string mimeType = String.Empty;
                            string extension = String.Empty;
                            PrintShippingDocLabelPDF.REService2005.Warning[] warnings = null;
                            string[] streamIDs = null;
                            string strFilePath = ExportFilePath;  // location for writing PDF labels generated from executed reports
                            string strFileName;  // the name of pdf labels generated from executed reports

                            _re = new PrintShippingDocLabelPDF.REService2005.ReportExecutionService();
                            _re.Credentials = System.Net.CredentialCache.DefaultCredentials;

                            // Prepare Render arguments
                            PrintShippingDocLabelPDF.REService2005.ExecutionInfo ei = _re.LoadReport(report, historyID);
                            PrintShippingDocLabelPDF.REService2005.ParameterValue[] parameters = new PrintShippingDocLabelPDF.REService2005.ParameterValue[1];

                            // add the spsnumber as the report parameter
                            parameters[0] = new PrintShippingDocLabelPDF.REService2005.ParameterValue();
                            parameters[0].Name = "spsnumber";
                            parameters[0].Value = strSpsNumber;
                            strFileName = strSpsNumber + FileExtentionPDF;

                            // set the execution parameters
                            _re.SetExecutionParameters(parameters, "en-us");

                            // render the report
                            results = _re.Render(format, deviceInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);

                            // Create a file stream and write the report to it
                            using (FileStream stream = File.OpenWrite(strFilePath + strFileName))
                            {
                                stream.Write(results, 0, results.Length);
                            } // END using (FileStream stream = File.OpenWrite(StoredProcedures.strFilePath + StoredProcedures.strFileName))

                            // Set the IsValidatedByWms associated with SPSNumber to 1
                            // to indicate that the report has been printed.
                            //sqlConnTestDB.Open();

                            sqlCmdUpdateSlctPicking.CommandText = sqlCmdStrUpdate + "'" + strSpsNumber + "'";
                            sqlCmdUpdateSlctPicking.ExecuteNonQuery();

                            //sqlConnTestDB.Close();
                        }  // END foreach (DataRow r in dtTblInvoiceItem.Rows)

                        dtTblInvoiceItem.Clear();

                    } // if (dtTblInvoiceItem.Rows.Count > 0)

            }   //  END if (Convert.ToInt32(row["LabelPrintStatus"]) == 0)

        }  // END foreach (DataRow row in dtPicking.Rows)

    }  // END using (SqlConnection sqlConnTestDB = new SqlConnection("context connection=true"))
} // END public static void PrintShippingDocLabelsToPDF()

1 个答案:

答案 0 :(得分:0)

首先,关于这个问题的一些注意事项:

  •   

    无法加载动态生成的序列化程序集。

    序列化程序集用于通过Web服务进行通信

  •   

    System.IO.FileLoadException

    此错误不是来自您的代码,因为您没有加载文件。您正在尝试保存文件。两者都要求尝试操作的程序集PERMISSION_SET EXTERNAL_ACCESS,但加载和保存不是一回事。

  •   

    主机已禁用LoadFrom(),LoadFile(),Load(byte [])和LoadModule()。

    这很可能是因为没有将程序集的PERMISSION_SET设置为EXTERNAL_ACCESS。这需要将数据库设置为TRUSTWORTHY ON(不好)或根据您为程序集签名的密钥创建登录,并授予登录EXTERNAL ACCESS ASSEMBLY权限。

出于测试目的,您可以执行以下操作以查看它是否有效。将程序集部署为SAFE后,运行以下命令:

  • ALTER DATABASE [{db_name}] SET TRUSTWORTHY ON;
  • ALTER ASSEMBLY [{assembly_name}] WITH PERMISSION_SET = EXTERNAL_ACCESS;

然后尝试再次运行存储过程。你应该至少走得更远。但是,考虑到代码的当前形式,并且因为您说它已经作为控制台应用程序运行,我认为您最好保持这种方式并从xp_cmdshell运行它,或者如果尚未启用它,从SQL代理作业的Command步骤运行它。

我认为可能在SQLCLR中工作,但首先需要进行一些重组。

  • 不要在.NET代码中执行循环以获取spsnumber。通过CURSOR在T-SQL中获取该列表,然后在SPSNumber中调用此过程。
  • 控制台应用程序(和Windows窗体)与SQLCLR之间的一个区别是控制台应用程序作为Windows登录运行。但是,SQLCLR默认运行为SQLSERVER NT服务的登录帐户。该帐户需要对该文件夹具有写入权限,或者您需要启用模拟以将安全上下文切换到运行存储过程的任何人,但这仅适用于Windows登录,而不适用于SQL Server登录。

一般说明:

  • 请不要将字符串连接到查询中,因为这会打开您可能的SQL注入。相反,声明参数如下:

    SqlParameter _SpsNumber = new SqlParameter("@SpsNumber", SqlDbType.Int);
    sqlCmdUpdateSlctPicking.Parameters.Add(_SpsNumber);
    

    然后您更新sqlCmdStrUpdate@SpsNumber结束,而不是为每个项目更新sqlCmdUpdateSlctPicking.CommandText,而只需致电_SpsNumber.Value = strSpsNumber;(尽管您可能需要{ {1}}如果它抱怨)。

  • 您不需要两个Int32.Parse(strSpsNumber)语句:ifdtTblInvoiceItem != null。只需将它们合并到dtTblInvoiceItem.Rows.Count > 0

  • 即可