如何动态加载项目引用但代码未引用的程序集

时间:2019-06-18 20:38:12

标签: c# reflection .net-core

考虑一个引用NuGet包的.NET Core应用程序。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MyPackage" Version="1.0.0" />
  </ItemGroup>

</Project>

如果我的代码引用了MyPackage中的类型,那么MyPackage程序集将被加载。如果我打印出所有引用或加载的程序集,它将出现。

static void Main(string[] args)
{
    // Because I have a reference to a type in MyPackage, the assembly 
    // is loaded and will be printed out by both foreach statements below.
    var throwaway = typeof(MyPackage.Cars);
    foreach (var an in Assembly.GetEntryAssembly().GetReferencedAssemblies())
    {
        WriteLine(an.Name);
    }

    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
        WriteLine(assembly.FullName);
    }
}

但是,如果我放弃了该throwaway行,那么将加载程序集,因此GetReferencedAssembliesGetAssemblies都不可用

.NET Framework也有同样的问题,通常的解决方法是读取执行文件夹中的所有程序集并手动加载它们,例如:

Directory
    .GetFiles(executingFolder, "*.dll", SearchOption.TopDirectoryOnly)
    .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath));

但是,.NET Core将从其他位置加载程序集(例如NuGet缓存-到目前为止,我还没有找到关于新绑定过程的全面描述),因此上述方法不起作用。

问题

所以,我的问题是:如何动态加载csproj文件引用的所有DLL(作为NuGet PackageReferences)。我的用例相当神秘,因此我认为没有其他机制可以做到。

背景

好的,所以有人要问我的用例是什么,所以就在这里。

我们有一组定义消息的接口(IAuditEvent,IValidationEvent等)。我们还为不同的序列化格式(Protobuf,XML,JSON等)提供了这些接口的各种实现。每个都是独立的NuGet程序包(MyMessages.Proto,MyMessages.Xml)。

我们有一个工厂将创建适当的实现(factory.Create<IAuditEvent>()),但是它使用反射来实现-例如proto工厂找到一个实现IAuditEvent的类,但它也是Protobuf生成的类。如果没有首先加载程序集,它将无法工作...

2 个答案:

答案 0 :(得分:1)

这实际上取决于战略。如果您发现自己需要中介来反映和提供类型,则是考虑IoC容器的好时机。

在这里,您有几种选择:

答案 1 :(得分:1)

Assembly.GetReferencedAssemblies不会返回您的程序集,因为它实际上没有被引用。如果您查看exe文件的清单,将找不到引用,就像编译器对其进行了优化一样。

AppDomain.GetAssemblies返回实际加载的程序集,请考虑:

static void Main(string[] args)
{
    foreach (var an in Assembly.GetEntryAssembly().GetReferencedAssemblies())
    {
        WriteLine(an.Name);
    }

    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
        WriteLine(assembly.FullName);
    }

    LoadType();
}

static void LoadType()
{
    typeof(MyPackage.Cars);
}

在这种情况下,GetReferencedAssemblies调用的结果始终是相同的,但是GetAssemblies的结果取决于调用GetAssemblies之前或之后放置LoadType的位置。

在构建服务器上,您不是要构建解决方案,而是要发布解决方案,因此扫描程序集只是开发时间问题。您可以在构建后事件中添加以下内容,也可以在项目中的dev目标中添加以下内容,并使用Daniel建议的方法之一:

dotnet发布“ $(ProjectPath)” --no-build -o“ $(TargetDir)”

尽管这还远非理想,希望您能提出更好的建议。