.NET 代码合同 - ProjectA.exe 在同一解决方案中找不到 ProjectB.Contracts.dll

.NET Code Contracts - ProjectA.exe cannot find ProjectB.Contracts.dll when both are in same solution

我在 VS2013 中有一个 C# 解决方案,安装了 Code Contracts 扩展。在我的解决方案中,我有一个应用程序项目 (ProjectA) 和一个 class 库项目 (ProjectB)。 ProjectA 引用了 ProjectB,并且大多数 ProjectB 的 public 成员都有与之关联的合同。

我可以毫无问题地从 ProjectA 访问 ProjectB 的大部分成员,但是在一个 属性 上,我收到 FileNotFoundException,消息为 "Could not load file or assembly 'ProjectB.Contracts, Version=....' or one of its dependencies."

融合日志的内容如下:

=== Pre-bind state information ===

LOG: DisplayName = [SolutionName].ProjectB.Contracts, Version=1.0.5953.23121, Culture=neutral, PublicKeyToken=null (Fully-specified)

LOG: Appbase = [SolutionDir]/[SolutionName].ProjectA/bin/Debug/

LOG: Initial PrivatePath = NULL Calling assembly : [SolutionName].ProjectC, Version=1.0.5953.23122, Culture=neutral, PublicKeyToken=null.

xxx

LOG: This bind starts in default load context.

LOG: Using application configuration file: [MyDocuments][SolutionName][SolutionName].ProjectA\bin\Debug[SolutionName].ProjectA.vshost.exe.config

LOG: Using host configuration file:

LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\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 [SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts.DLL.

LOG: Attempting download of new URL [SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts/[SolutionName].ProjectB.Contracts.DLL.

LOG: Attempting download of new URL [SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts.EXE.

LOG: Attempting download of new URL [SolutionDir]/[SolutionName].ProjectA/bin/Debug/[SolutionName].ProjectB.Contracts/[SolutionName].ProjectB.Contracts.EXE.

此解决方案中唯一的引用是标准框架库、此解决方案中的其他项目和另一个第一方库(仅引用 System.dll)。我已尝试清理并重建此解决方案中的所有项目,以及其他第一方库。

我很困惑有几个原因。

  1. 在输出 window 中,我可以看到 ProjectA 和 ProjectB 的所有引用(及其引用)都已加载。
  2. 其他看似相似的方法和具有相似契约的属性不会引起任何问题。此外,几个月来我一直在几个解决方案中使用 Code Contracts,但我还没有 运行 参与其中。
  3. 合约代码不应该已经重新编译成ProjectB.dll了吗?为什么 ProjectA 甚至需要知道有一个 .Contracts 库? (智能感知和静态分析除外)

有人知道这里发生了什么吗?


编辑: 解决方案中的所有项目都针对 .NET 4.0 和 "Any CPU"。

以下是我尝试过但没有奏效的一些方法:

在 ProjectA 的项目设置中,在代码合同选项卡上,我尝试将 ...ProjectB\bin\Debug\CodeContracts 添加到字段 Extra Contract Library路径。这并没有改变任何东西。

我还尝试手动添加对 ProjectB.Contacts.dll 的引用。这使我的 FileNotFoundException 变成了 MissingMethodException。这让我觉得这个问题可能是由于代码合同重写者的错误造成的。输入 ILSpy.


导致问题的合同在原始源代码中基本上是这样的:

Contract.Requires<ArgumentNullException>(collection.All(x => x != null));

但我把它放在这样的合同缩写方法中:

public static class PreCondition {
    [ContractAbbreviator]
    public static void NotIsOrHasNull<T>(IEnumerable<T> collection){
        Contract.Requires<ArgumentNullException(collection != null,   
            "Collection cannot be null.");
        Contract.Requires<ArgumentNullException>(collection.All(x => x != null), 
            "Collection cannot contain null.");
    }
}

并在 class 中这样称呼它:

public class Class1{
    public void DoStuff(IEnumerable<T> collection){
        PreCondition.NotIsOrHasNull(collection);
        foreach (var x in collection){
            //Stuff
        }
    }
}

在 ILSpy 中查看麻烦的方法调用时,我看到它试图调用名为 Class1.NotIsOrHasNull 的实例方法,而不是静态方法 PreCondition.NotIsOrHasNull.


解决方法:

我更改了 PreCondition class 以将 LINQ 表达式移动到它自己的 Pure 函数中,并更改合约缩写以调用该方法。

public static class PreCondition {

    [Pure]
    public static Boolean CollectionContainsNull(IEnumerable<T> collection){
        Contract.Requires<ArgumentNullException(collection != null);

        if (typeof(T).IsValueType)
            return false;

        return collection.Any(x => Object.Equals(x, null));
    }

    [ContractAbbreviator]
    public static void NotIsOrHasNull<T>(IEnumerable<T> collection){
        Contract.Requires<ArgumentNullException(collection != null,   
            "Collection cannot be null.");
        Contract.Requires<ArgumentNullException>(!CollectionContainsNull(collection)), 
            "Collection cannot contain null.");
    }
}

现在 Class1 中的方法调用有效,并且 IL 在 ILSpy 中看起来是正确的。

+1 到 ILSpy!