19

I'm working on a project, and we need to know the version of a NuGet package we are using, and use it in the code.

One way I tried was to read from the packages.config file, parse it for the package I want, and parse the line for the version. Something like:

var lines = System.IO.File.ReadAllLines(@"..\..\packages.config");
foreach(var line in lines)
{
    if (line.Contains("My.Package"))
    {
         foreach(var part in line.split(' '))
         {
             if (part.Contains("version"))
             {
                 return part.Split('"')[1];
             }
         }
    }
}

Is there a better way to do this programmatically?

Note: The above code will not work, as packages.config is not deployed with the application.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
justindao
  • 2,273
  • 4
  • 18
  • 34
  • 1
    Reading it as XML would probably make more sense. At which point in time do you need to know the package version? You could probably determine it from the compiled assembly version as well. – Matthew Jan 13 '16 at 19:48
  • Not sure exactly what you mean by "at which point in time", but I'd like to grab the version during runtime, to use elsewhere in the code. – justindao Jan 13 '16 at 21:43
  • Do you include `packages.config` when you deploy your app? – Aleksandr Ivanov Jan 13 '16 at 22:38
  • @AleksandrIvanov No we don't, so unfortunately this will not work. – justindao Jan 14 '16 at 00:49
  • Related: *[How can I get the NuGet package version programmatically from a NuGet feed?](https://stackoverflow.com/questions/26677801/how-can-i-get-the-nuget-package-version-programmatically-from-a-nuget-feed)* – Peter Mortensen Jun 25 '20 at 08:57
  • You still did not select your answer, heh? – T.S. Jan 24 '23 at 15:51

5 Answers5

10

If the package has any classes you can just get the assembly from that class and then the version. However, if the package has no classes, e.g. does something like copying files using nuget.targets then it's impossible this way.

Console.WriteLine("Using Selenium.WebDriver: " + Assembly.GetAssembly(typeof(OpenQA.Selenium.By)).GetName().Version.ToString());
lastlink
  • 1,505
  • 2
  • 19
  • 29
  • Note: this only gets the major version. E.g Serilog 2.4.0 -> 2.0.0.0 – Haukland Oct 14 '21 at 11:42
  • Even more general approach, just drop this anywhere in a method (also works with static methods): `string applicationVersion = Assembly.GetAssembly(MethodBase.GetCurrentMethod().DeclaringType).GetName().Version.ToString();` be sure to add at the top `using System.Reflection` namespace. – Jan Mar 16 '22 at 09:52
2

You can read the version from the assembly:

string assemblyVersion = Assembly.LoadFile('your assembly file').GetName().Version.ToString();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Asad Mehmood
  • 135
  • 8
2

You can configure your project to record the data at compile time. Here is an MSBuild snippet that generates an assembly attribute called MyNamespace.MSBuildVersionAttribute with a single constructor argument.

<Target Name="ExtractMSBuildVersion" BeforeTargets="BeforeBuild">
    <ItemGroup>
        <MSBuildPackageVersion Include="@(PackageReference->'%(Version)')" Condition="'%(Identity)' == 'Microsoft.Build'" />
        <AssemblyAttributes Include="MyNamespace.MSBuildVersionAttribute">
            <_Parameter1>@(MSBuildPackageVersion)</_Parameter1>
        </AssemblyAttributes>
    </ItemGroup>
        
    <WriteCodeFragment AssemblyAttributes="@(AssemblyAttributes)" Language="C#" OutputDirectory="$(IntermediateOutputPath)" OutputFile="MSBuildVersion.cs">
        <Output TaskParameter="OutputFile" ItemName="Compile" />
        <Output TaskParameter="OutputFile" ItemName="FileWrites" />
    </WriteCodeFragment>
</Target>

You can now access that attribute at runtime.

Artfunkel
  • 1,832
  • 17
  • 23
  • interesting concept but, the package comes with multiple versions of the DLL. Moreover, if you have a component (your own DLL) that used a package DLL v1 and then you have an endpoint like a Console app or Web API, which uses v2 of that DLL. Now, you have 2 different attributes. Or if you have v1 in the endpoint and v2 in the component and then you use Binding redirect. So, you write v1 into the attribute but at runtime use v2. **This are the reasons why using package versions is a unholy proposition** And to know assembly used one can make query for file and assembly version and also PI – T.S. Jan 24 '23 at 15:08
1

Use AssemblyInformationalVersionAttribute.InformationalVersion. e.g.:

const string NewtonsoftNuGetId = "Newtonsoft.Json";
// JsonConvert is a class in Newtonsoft.Json
// https://www.newtonsoft.com/json/help/html/t_newtonsoft_json_jsonconvert.htm
var newtonSoftInformationalVersion = typeof(Newtonsoft.Json.JsonConvert).Assembly
    .GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
var newtonsoftVersion = newtonSoftInformationalVersion.Substring(0, newtonSoftInformationalVersion.IndexOf("+")); // 13.0.2
Bartleby
  • 1,144
  • 1
  • 13
  • 14
-1

This is still active and controversial after 8 years so the

BIG UPDATE

To know your package you need to read the packages from your project file. It could be a packages.config setup or package references. You shouldn't read from packages.config because this file is often out of sync. You should read Hint and PackageReference tags in the project file. And if your solution has multiple projects? One way - use Pre Build Event and a tool that you can write using PowerShell. Your input will be the project file, in which you can find tags, and then parse the digits out of them. Then you create an output - a file where you write a class and in that class a dictionary with package name and version. In fact, you can make a list and not parse the version out of the name. You can also have a template saved and only replace and re-save the part. so, save the template in the project like

using System;
using System.Collections.Generic;
namespace Packages.Versions
{
    public class PackageCollection
    {

        public PackageCollection ()
        {
            Packages = new List<string>();
            //PLACEHOLDER
        }
        public List<string> Packages { get; }
    }
} 

And in the PowerShell you compose a string like

"Packages.Add(\"MyPackage.Name.123.0.0\"); 
Packages.Add(\"MyPackage2.Name.987.0.0\");
Packages.Add(\"MyPackage3.Name.567.0.0\");"

And then you plug this into the //PLACEHOLDER and overwrite the file.

This will give you more precise result of what your project(s) use but not the dependencies of the packages themselves.

However!! The packages that are referenced at the design/compile time do not guarantee that these are the same ones used at runtime (and I am talking about DLLs in the packages). Different projects in your solution can reference different versions of the same package and only 1 version will be used (usually, because it is possible to have multiple versions.)

This is why I said below that answer to "how to collect package version" can actually mislead the whole operation. Because you might want to get Assembly Version and File Version rather than package version. These you can get reliably. And I have the links below.

END big update

Original answer below

Good question. But you have not said why you want to know the version.

I have to start my answer from pointing that you're on the wrong path. I suspect that you need to know at runtime the version of assembly in use.

But the whole NuGet concept and packages.config is a compile-time concept and not a runtime concept. You shouldn't even have packages.config in your output folder or published folder. At runtime you need to get loaded assemblies from AppDomain.CurrentDomain. I will not go in depth on it here, but here is the reference you can look at. I only say that you can get assembly version there.

Then, in code, if you want to do things differently based on certain assembly version. If you want to compile based on version of framework or assembly - check this post

Bottom Line:

The fact that packages.config exist or not, or what it has in it, has no correlation to which DLL file has been used at compile time. VS has an incredible ability to crawl through directories and finding matching DLL files. packages.config can be outdated or desynchronized with the actual dll. This is not a bullet-proof way to do things.

T.S.
  • 18,195
  • 11
  • 58
  • 78
  • 12
    The reason isn't important. This is a specific question that other people are searching for on the internet and a direct answer is the most helpful. – Warren Parks Aug 02 '18 at 22:24
  • 4
    @WarrenParks Reason (or, what in programming we call **motivation**) is super important; because most of the time there is an easy solution for a problem that is posted here. Only people go into wrong direction from the beginning. Let me tell you something about this question - the fact that packages.config exist or not, has no correlation to **which** dll has been used at compile time. packages.config can be outdated or desynchronized with actual dll. This is not bullet-proof way to do things and hence my answer. I am ok if you feel differently. Enjoy. – T.S. Aug 02 '18 at 23:27
  • 1
    **AGAIN**, there is no guarantee that assembly used at runtime is the same version as was used at compile time. This is why reflection needed to get assembly version. How you get it - is a different question. – T.S. Mar 04 '20 at 01:23
  • **Looks like people still don't understand** that when you go to your assembly at runtime `AppDomain.CurrentDomain.GetAssemblies()` this is what is executing at **runtime**. OP's question was however about *NuGet* packages version. The nuget package version is not a file version or assembly version. While building multi-project solutions, the DLLs can overwrite each other. And based on target framework a different DLL can be taken from different folders in the same nuget package. People use `DependencyRedirect` controls to deal with these differences. – T.S. Sep 30 '22 at 15:06