有没有办法防止某些引用被包含在项目中

本文关键字:包含 项目 引用 有没有 | 更新日期: 2023-09-27 18:19:30

基本上,我想做一些预防性维护。有一些第三方库,我想防止在某个项目中作为引用被包括在内。有没有一种方法可以指定项目中禁止哪些引用?

我想要保护的项目是一个类库,我希望它具有来自一组非常特定的第三方库的功能。类库在其他解决方案中用于通用数据访问功能,因此如果引用了这些第三方库,也需要它们。其目的是保持该项目只是一个数据访问库;包装";轻量的

有没有办法防止某些引用被包含在项目中

我在一个大型开发团队工作,所有人都在同一个软件上工作,并且有类似的问题。

我们在一个大型领域驱动设计(DDD)架构中工作,该架构具有许多不同的有界上下文,不希望人们在上下文之间添加引用。

我们有指导方针、标准、体系结构文档、代码评审等,很多东西都阻止了添加IRL的引用(正如有人所说)。然而,我们有两位相对较新的首发球员,他们对目前的结构没有太多经验,只是不太了解一切。他们碰巧审查了彼此的代码,瞧,添加了不需要的引用。

我认为在防止错误发生和确保标准得到遵守方面没有什么严厉的措施。只是一种预防措施。这难道不是我们编写单元测试的部分原因吗?这样,未来的其他新人就可以意识到他们在不知不觉中打碎了什么?

我不太喜欢分析项目文件中的参考资料。我们可能会处理它的方法是定义一组单元测试,这些单元测试在每个被测试项目的程序集引用中爬行,当它们识别出不应该存在的引用时就会失败。

显然,只有当您有持续的集成/部署(包括运行单元测试)时,这才有效。

因此,即使新人在没有首先在本地运行单元测试(并意识到他们的错误)的情况下签入了一些东西,我们闪烁的亮红色构建状态灯或构建服务器电子邮件也会很快告诉团队中的每个人出了什么问题。

您可以使用Visual Studio层图来实现这一点(假设您拥有Visual Studio Ultimate)。绘制一个表示您的项目的框/层,将您的项目放在上面以链接它们,绘制一个代表禁止的程序集的框/图层,将这些程序集放在上面以便链接它们,就完成了(不在层之间绘制依赖项箭头,就向Visual Studio指示不允许依赖项)。

现在,通过设置MSBuild属性,在项目和/或TFS Build中启用"层图验证":ValidateArchitecture=True

这绝对是一个有效的问题,Ben的回答非常切中要害。然而,这个工具可以帮助自动执行您的参考约束:

NsDepCop

在我的另一个答案之后的几年里,我重新审视了这个库,我发现了这个库:

ArchUnitNET

它是ArchUnit for Java的.NET端口,允许您使用流畅的API编写单元测试,以描述您的体系结构约束。

所以你可以写这样的东西:

IArchRule rule = Types().That().ResideInNamespace("Model").Should()
                    .NotDependOnAny(Types().That().ResideInNamespace("Controller"));

目前,在一个基于DDD的大型解决方案中正在实践中使用它来强制执行项目引用,并监控nuget包的引入。

在检查所有规则的情况下为Roslyn编译器编写一个扩展,并将此Analyzer(作为NuGet包)放入解决方案中。你的分析将成为汇编的一部分。

这与NsDepCop的基本工作方式非常相似。

搜索允许的程序集并标记异常可能更实用,因为有人可以简单地将流氓程序集重命名为不在您列表中的名称并逃避检测。

用于构建程序集的.csproj文件是一个普通的XML文件,因此可以使用XPath语句轻松定位引用的程序集,其中谓词是允许的程序集名称。您可以设置一个触发器,每当将.csproj文件签入源存储库时,就会扫描该文件,并标记出任何罪魁祸首程序集。

使用这种方法,或任何类似的方法,都有可能邀请流氓开发者玩蛙跳游戏。你很可能会输掉这个游戏,因为开发者都是出色的蛙跳玩家。因此,一个更稳健的方法是减少对技术的依赖,更多地依靠管理手段。

这是一个很好的问题,它的适用性比作者预期的要广泛。

许多WPF项目已经变成了意大利面条状的粘稠物,因为n00b程序员不太了解MVVM,以及应用程序逻辑和表示逻辑之间分离的重要性。

大多数WPF程序员将视图和视图模型并排放在同一目录中。我见过的每一个WPF项目都是这样的结构。这可能很方便,因为源文件总是紧挨着,但这是完全错误的,因为这意味着视图和视图模型都位于同一个项目中,这意味着应用程序逻辑可以使用WPF程序集,这意味著应用程序逻辑和表示逻辑之间没有分离。(MVVM的发明主要是为了解决这个问题。)

这就是在CI/CD服务器上运行测试的方法,它永远被困在等待某人在应用程序模式消息框上单击确定,以及其他类似的搞笑情况

为了防止这种情况,谨慎的做法是将所有应用程序逻辑放在一个单独的项目中,并避免该项目引用任何WPF程序集。这可能需要一些基础设施工作,比如抽象MessageBox设施、Dispatcher等,并将这些抽象提供(注入)到应用程序逻辑中,但这是非常值得做的。

一旦您完成了应用程序逻辑和表示逻辑之间的分离,接下来的问题是如何防止n00b程序员再次将它们混合在一起。

例如,一旦有人需要在应用程序逻辑中使用颜色的概念,他们在引用WPF程序集开始使用System.Windows.Color之前甚至不会眨眼。问题是如何防止这种情况发生

这个问题的其他答案建议使用第三方工具,使用代码分析,甚至解析项目文件。用户";Ben"在他的回答中写道,他通过编程解决了这个问题,但没有显示任何代码。

所以,我不得不写代码。以下是我的做法:

Assert( noProhibitedAssembliesAreReferencedAssertion() );
.
.
.
    private static bool noProhibitedAssembliesAreReferencedAssertion()
    {
        ImmutableList<string> names = Assembly.GetExecutingAssembly()
                .GetReferencedAssemblies()
                .Select( a => a.Name )
                .Where( isProhibitedAssemblyName )
                .ToImmutableList();
        if( names.Count > 0 )
        {
            dumpReferencedAssembliesOfModule( "Executing assembly (should not reference prohibited assemblies)",
                    Assembly.GetExecutingAssembly() );
            dumpReferencedAssembliesOfModule( "Calling assembly (can reference any assembly)",
                    Assembly.GetCallingAssembly() );
            throw new AssertionFailureException( $"The application logic references prohibited assemblies: {names.MakeString( ", " )}" );
        }
        return true;
    }
    private static readonly string[] prohibitedAssemblyNames =
        {
            "System.Windows", //
            "PresentationFramework", //
            "WindowsBase", //
            "System.Xaml", //
            "PresentationCore", //
            "System.Printing", //
            "ReachFramework", //
            "MahApps.Metro", //
            "SciChart.Charting", //
            "SciChart.Charting3D", //
            "SciChart.Core", //
            "SciChart.Drawing", //
            "SciChart.Data"
        };
    private static bool isProhibitedAssemblyName( string assemblyName )
    {
        return prohibitedAssemblyNames.Contains( assemblyName );
    }
    private static void dumpReferencedAssembliesOfModule( string message, Assembly assembly )
    {
        logger.Info( $"{message}: {assembly.FullName}" );
        logger.Info( "     GetModules():" );
        foreach( Module module in assembly.GetModules() )
            logger.Info( $"        {module.Name} {module.Assembly}" );
        logger.Info( "     GetReferencedAssemblies():" );
        foreach( var referencedAssembly in assembly.GetReferencedAssemblies() )
            logger.Info( $"        {referencedAssembly.Name}" );
    }

上面的代码位于主应用程序逻辑对象(MainViewModel或等效对象)中,并检查应用程序逻辑程序集引用了哪些程序集。如果它发现任何";禁止的";程序集,它故意失败。此代码在应用程序启动期间运行,但也在应用程序逻辑测试中运行,当测试实例化MainViewModel时。

请务必在这段代码中附上巨大的警告注释,告诉任何人如果失败,他们应该在更改任何内容之前与您联系。

用正则表达式模式替换程序集名称留给读者练习。

如果项目具有某些引用(无论是直接程序集引用、项目引用还是包引用),您可以通过在repo根目录中的Directory.Build.targets文件中粘贴这样的内容来阻止项目的构建:

<Project>
  <Target Name="ValidateDisallowedReferences" BeforeTargets="CoreCompile">
    <Error
      Condition="'%(Reference.FileName)' == 'Newtonsoft.Json'"
      Text="Newtonsoft.Json is not supported. Please use System.Text.Json instead." />
  </Target>
</Project>