Kentico 10 是否使用与以前版本不同的组装策略 loading/probing/handling?

Does Kentico 10 use a different strategy for assembly loading/probing/handling than previous versions?

我试图了解似乎由 Kentico 10(与 9 及更早版本相比)驱动的程序集加载或探测差异,因此我可以解决程序集加载错误,我当前的示例如下...

Example: We've been using Expert PDF components in websites from http://www.html-to-pdf.net The product has a managed ephtmltopdf.dll assembly that relies on the unmanaged "helper" DLL epengine.dll also sitting alongside in the web app's bin/ folder, and sometimes an exception is thrown when the app starts up...

在 Kentico 9(及更早版本)下,每当网站启动时,都会抛出 epengine 异常并且 出现在 Kentico 事件日志中 .

在 Kentico 10 下,发生 epengine 异常并且 阻止网站完全 运行。

我试图关联同一组件配置下这两种行为之间的差异。


这是 epengine Kentico 9 及更早版本下的 CMS 事件日志条目(不阻止网站 运行):

Event type: Error
Event time: 7/18/2017 4:00:06 AM
Source: Discovery
Event code: E:\Kentico_V9\CMS\bin\epengine.dll
User ID: 65
User name: public
Description: Could not load file or assembly 'epengine.dll' or one of its dependencies. The module was expected to contain an assembly manifest.
The file E:\Kentico_V9\CMS\bin\epengine.dll is not an assembly or the assembly was compiled for a later version of the .NET runtime.
Machine name: OX
Event URL: /register/all
URL referrer: /Public-(1)/Search-Results
User agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; BOIE9;ENUS; rv:11.0) like Gecko

在 Kentico 10 下,这是阻止站点 运行 的 epengine 错误。

*** Assembly Binder Log Entry  (17/07/2017 @ 4:36:56 PM) ***

The operation failed.
Bind result: hr = 0x80131018. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Windows\SysWOW64\inetsrv\w3wp.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = epengine
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: epengine | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/inetpub/wwwroot/website/CMS/
LOG: Initial PrivatePath = C:\inetpub\wwwroot\website\CMS\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root2d45d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root2d45d4
LOG: AppName = f7cc5d08
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\inetpub\wwwroot\website\CMS\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine/epengine.DLL.
LOG: Attempting download of new URL file:///C:/inetpub/wwwroot/website/CMS/bin/epengine.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\inetpub\wwwroot\website\CMS\bin\epengine.dll
LOG: Entering download cache setup phase.
ERR: Error extracting manifest import from file (hr = 0x80131018).
ERR: Setup failed with hr = 0x80131018.
ERR: Failed to complete setup of assembly (hr = 0x80131018). Probing terminated.

*** Assembly Binder Log Entry  (17/07/2017 @ 4:36:56 PM) ***

The operation failed.
Bind result: hr = 0x80131018. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Windows\SysWOW64\inetsrv\w3wp.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = epengine
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: epengine | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/inetpub/wwwroot/website/CMS/
LOG: Initial PrivatePath = C:\inetpub\wwwroot\website\CMS\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root2d45d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root2d45d4
LOG: AppName = f7cc5d08
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\inetpub\wwwroot\website\CMS\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/root/672d45d4/f7cc5d08/epengine/epengine.DLL.
LOG: Attempting download of new URL file:///C:/inetpub/wwwroot/website/CMS/bin/epengine.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\inetpub\wwwroot\website\CMS\bin\epengine.dll
LOG: Entering download cache setup phase.
ERR: Error extracting manifest import from file (hr = 0x80131018).
ERR: Setup failed with hr = 0x80131018.
ERR: Failed to complete setup of assembly (hr = 0x80131018). Probing terminated.

每当我在 Kentico 9(或更早版本)和 10 中尝试以下任何操作时,epengine 错误总是在 Kentico 10 中更早出现,从而阻止网站 运行 而不是在 Kentico CMS 事件中显示日志.

错误表达时间的差异似乎是我在Kentico 9或10中使用这个组件。

我想知道的是,程序集加载、探测或异常处理是否存在差异,这可能有助于解释为什么此组件的错误会阻止 Kentico 10 ASP.NET 网站加载,但不会阻止 Kentico 9 网站加载相同的 IIS 和 .NET 配置。

(请注意,我还通过联系供应商来解决 PDF 组件错误 - 最终解决方案将是最佳解决方案)。

不理想。此解决方案解决了 ephtmltopdf.dll 和 epengine.dll 程序集在 Web 应用程序启动期间从应用程序的 bin/ 文件夹加载时抛出错误导致 Kentico 10 网站无法加载的问题。

此解决方案基于原始问题下的 @rocky's 评论。

这有效地导致 Expert PDF 组件在 Kentico 站点已经 运行 之后加载。

并非PDF生成器的所有用法都经过测试。此处的代码示例将在无法直接引用该组件的环境中使用 Expert PDF 组件成功将给定的 URL 下载为 PDF 文档。 (它适用于我的电脑)


解决步骤 -

  1. 从您的应用程序中删除 Expert PDF 组件的所有程序集引用;还删除了 C# using 语句。在代码中使用 PDF classes 和结构时,您将遇到编译错误。

  2. 在需要时从容器外部动态加载 Expert PDF 程序集(如代码示例中所示 - 参见 Assembly.LoadFile)。

  3. 动态实例化组件(如代码示例所示 - 参见 dynamic + CreateInstance )

  4. 您的 Expert PDF 代码的其余部分可以保持不变。

// In ~/TestPdf.aspx.cs code-behind page, inside a Kentico 10 website 

namespace CMSApp
{
    using System;
    //using ExpertPdf.HtmlToPdf; // << Namespace no longer available to C# compiler.
    using System.Reflection;

    public partial class test1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

            // var converter = new ExpertPdf.HtmlToPdf.PdfConverter(); // << Type no longer available to C# compiler.

            // Dynamically load the Expert PDF Assembly, Type and an instance...
            Assembly assemb = System.Reflection.Assembly.LoadFile(@"C:\KenticoBaseInstalls\Kentico10.2-app\LibMore\ExpertPdf-HtmlToPdf-v11.0.0\Bin\.NET_4.0\ephtmltopdf.dll");
            dynamic converter = assemb.CreateInstance("ExpertPdf.HtmlToPdf.PdfConverter", true);


            // Continue to use old PDF code but without compiler type checks and VS Editor Intellisense.

            byte[] pdfBytes = converter.GetPdfBytesFromUrl("https://www.iana.org/domains/reserved");
            Response.ClearHeaders();

            Response.ContentType = "application/octet-stream";
            Response.AppendHeader("Content-Disposition", "attachment; filename=example.pdf");
            Response.BinaryWrite(pdfBytes);
            Response.Flush();
            Response.End();
        }
    }
}

注意:ExpertPDF 程序集中还有其他构造会失败,您必须像上面的示例一样解决这些构造,例如:

  • 静态UnitsConverter.PixelsToPoints(..)
  • HtmlToPdfAreaclass
  • 图像区域class
  • PdfPageSize 枚举
  • 等等

解决方案的附录注释

在上面的代码示例中 System.Reflection.Assembly.LoadFile(..) 很容易在不同环境之间或文件移动时出现位置问题。更可靠的替代方法是使用应用程序基础子目录来获取程序集,如下所示。请注意,Kentico 10 使用此策略,我们正在利用他们的 CMS 文件夹结构:

在 ~/CMSDependencies 中创建一个额外的子文件夹,通过将 Expert PDF DLL(托管和非托管)添加到其中,可以按如下方式在网站中进行探测。你最终会得到这个树结构。

+---CMSDependencies
    +---ExpertPdfHtmlToPdf.11.0.0
            epengine.dll
            ephtmltopdf.dll

将 Expert Pdf 文件夹的名称添加到 web.config 中探测元素的 privatePath 中,不使用分号替换任何其他值。

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="CMSDependencies\Newtonsoft.Json.6.0.0.0;CMSDependencies\ExpertPdfHtmlToPdf.11.0.0"/>
    </assemblyBinding>
  </runtime>

通过简单名称在 C# 中加载程序集:

Assembly assemb = System.Reflection.Assembly.Load(new AssemblyName("ephtmltopdf"));

根据 Kentico Support 的建议,通过将网站修补到 Kentico 10.0.29(撰写本文时的最高修补程序)解决了这个问题。

通讯包含在下面以供参考。粗体是我对解决方案的强调。

From: Kentico Support
Sent: July-25-17 9:46 AM
To: John Kane
Subject: RE: Expert PDF component not compatible with Kentico 10 unless loaded dynamically Ticket:0072002734

Hello John,

Thank you for your message. Upon some investigation it is possible that this issue was fixed in Kentico 10 hotfix 10.0.25. Could you please try applying the latest hotfix? The bug was related to the application start and our developers think this could be related.

Also, could you please check all occurrences of native dll references, including a web.config file. This comment is seems to be related and accurate - Could not load file or assembly '\bin\ABCpdf8-64.dll' or one of its dependencies. The module was expected to contain an assembly manifest

Best regards,
Juraj Ondrus
Tech. support lead

我还通过下载 URL 并以编程方式将其转换为 PDF 文档,验证了 Expert PDF 组件 运行 在修补程序基础 Kentico 10 安装上是否正确。 (有关代码示例,请参阅 Expert PDF 网站和文档。)

回购本期的最快方式

因为我们正在将 Kentico 从 8.2 升级到 10 并解决了自定义代码和自定义组件的多个问题,所以我将在此处留下最简单的 repo 场景来证明使用 Expert PDF 时的问题,以及解决方案,适用于在升级或首次安装期间可能遇到类似问题的任何其他人。

回购问题

  1. 使用 Kentico 10 安装程序创建新的网站应用程序。
  2. 验证网站是 运行ning Kentico 10,没有修补程序,将报告为 10.0.0。
  3. 使用 Nuget,参考 ExpertPdfHtmlToPdf 包(版本 9.5 到 11 是我修改的)。
  4. 编译并 运行 网站 - 网站不会加载,而是显示 .NET 错误和 "epengine" 消息,如问题中所张贴的那样。

回购解决方案

  1. 按照 Kentico 修补程序说明,​​将最新的可用修补程序应用于 Kentico 10 安装。
  2. 验证站点现在报告为 10。0.x 其中 x 是应用的修补程序编号。如 Kentico 支持人员所述,确保它 >= #25。
  3. 编译并 运行 网站 - 错误消失,网站 运行 正常。