0

I'm developing an application that detects and eliminates useless external references in a given C# project for example a package referenced but not used. For this I need to enumerate all assemblies used either at compile and runtime. For those used at a runtime, I can get names using reflection specifically calling Assembly.GetReferencedAssemblies(). But for those used only at compile-time I think that there is no way to list them without using Roslyn. Have you please any answers to my problematic?

Isolin
  • 845
  • 7
  • 20
Tahatoun
  • 21
  • 5

1 Answers1

0

This is just a simple approach, far from perfect. I am sure you can go deeper and play around with Roslyn as much as you want. I am also sure there are much better experts on compilers and Roslyn out there than me :)

Note that this approach does not handle dynamically loaded assemblies. It avoids running the project.

Referenced assemblies

You can use an XML parser instead of Roslyn to analyze the project or solution file. There you will find the list of all referenced packages and project, e.g.:

  <ItemGroup>
    <ProjectReference Include="..\Common\Common.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="MessagePack" Version="2.1.115" />
  </ItemGroup>

The above example comes from a .NET Core 3.1 project file.

Update

In order to collect transitive dependencies as well there are several options.

  • For .NET Core projects you may call dotnet restore and then read the following file $ProjectFolder/obj/project.assets.json. It is a json file that contains all references that were resolved containing both NuGet and runtime packages.

  • If you want to avoid restoring the project/solution you can start with the packages found in .csproj as described above and use the NuGet Server API to get an array of dependencies for each. Doing this recursively will yield the same result as dotnet restore. NuGet Core wraps the API as C# classes so you can call it directly from your code. Dependencies can be easily queried like here: How to get the dependencies of a NuGet package from private NuGet feed?

Required assemblies

Now you need to identify those that are really necessary. The compilation process takes care of that. It does not (to my humble knowledge) insert any unnecessary references into the compiled code.

Roslyn semantic model

Addressed in this question: Roslyn getting dependencies for class

IL option

You can use Roslyn to compile the given project, then load the generated IL and look for references like [System.Private.CoreLib]. Just throw all referenced assemblies into a HashSet and remove them once you find a matching call in the IL. The resulting set will contain the references that can be removed.

Maybe this project can help viewing the IL. Also SharpLab can be very handy when you need Roslyn output examples.

Load option

If you are fine with loading the compiled project, you can use Assembly.GetReferencedAssemblies() as you indicated in the question instead of analyzing the IL.

Community
  • 1
  • 1
Isolin
  • 845
  • 7
  • 20
  • thank you @Isolin for your answer, but this approach won't take care of transitives dependencies. – Tahatoun May 25 '20 at 15:04
  • Hi @Tahatoun, do you think it would be a problem in the referenced or in the required set? Could you please give me an example where it wouldn't identify transitive references? I am really curious to find out and possibly fix my approach. – Isolin May 25 '20 at 19:17
  • In .Net Core project transitives dependencies are managed by nuget during the restore. It would be a problem in the referenced, for example if the project reference a package A which in turn reference a package B. package B would not appear in the project file file – Tahatoun May 25 '20 at 19:54
  • I see, that's true. Are you fine with calling `dotnet restore` to gather all referenced packages? Unfortunately there is no _dry run_ for the `restore` command. – Isolin May 25 '20 at 20:27