Reputation: 34275
I want my library to work with a range of versions of a NuGet package with breaking changes in API between the changes. I haven't investigated it further, but this path looks promising:
While this may seem complicated, it has many benefits over a more straightforward approach of supporting every version in a separate assembly:
However, it's unclear whether it's possible at all. Even before getting to proxies and detecting current version, I'm stuck with the basics.
I can't even add multiple PackageReference
nodes to my .csproj, only one reference actually works. There's a workaround for adding extern aliases which aren't supported by NuGet directly, but I can't get to that point because I can't get two references. And if I somehow get two, I won't be able to tell them apart.
I'm working on CsConsoleFormat library for formatting Console output. I want to support all relevant versions of popular command-line packages directly, so that pretty command line help and stuff like this could be added with almost no coding, no matter what command line parsing library is used.
I guess declaring "I support only the latest version" is somewhat acceptable in my case, but I'd rather have wider support even if it's more complicated. Ideally, I want a NuGet package which declares dependency on the lowest supported version, but supports everything up to the latest version.
I kinda got it to work, but with many issues. See issue on GitHub NuGet Home for more details.
Upvotes: 16
Views: 14392
Reputation: 101433
If you insist on extern aliases - you can add multiple version references directly, as dll file, not as nuget package.
Suppose I want to take a dependency on Newtonsoft.Json package version 10.0.3+.
However if user has version 11 installed - I want to use generic JsonConverter<T>
class available only in this version (11). Then my csproj might look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<!-- Nuget reference -->
<!-- Only this one will be included as dependency to the packed nuget -->
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<!-- Direct reference to the specific version -->
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<!-- Path to v11 dll -->
<HintPath>Newtonsoft.Json.v11.dll</HintPath>
<Aliases>js11</Aliases>
<SpecificVersion>true</SpecificVersion>
</Reference>
</ItemGroup>
</Project>
Then I have proxy interface:
public interface ISerializer {
string Serialize<T>(T obj);
}
And two implementations, v10 (uses global, non-aliased namespace):
using System;
using global::Newtonsoft.Json;
namespace NugetRefMain {
internal class Js10Serializer : ISerializer
{
public string Serialize<T>(T obj)
{
Console.WriteLine(typeof(JsonConvert));
return JsonConvert.SerializeObject(obj);
}
}
}
And v11
extern alias js11;
using System;
using js11::Newtonsoft.Json;
namespace NugetRefMain {
internal class Js11Serializer : ISerializer {
public string Serialize<T>(T obj) {
// using JsonConverter<T>, only available in v11
Console.WriteLine(typeof(JsonConverter<T>));
return JsonConvert.SerializeObject(obj);
}
}
}
And finally factory which creates serializer depending on current available json.net version:
public static class Serializers {
public static ISerializer Create() {
var version = typeof(JsonConvert).Assembly.GetName().Version;
if (version.Major == 10)
return new Js10Serializer();
return new Js11Serializer();
}
}
Now if I pack this as nuget - it will have single dependency on Newtonsoft.Json
version 10.0.3
and that's all. However, if user installs Newtonsoft.Json
of version 11 - it will use capabilities available in this version.
Drawbacks:
Visual Studio \ Resharper intellisense doesn't like this approach sometimes and shows intellisense errors when in reality everything compiles just fine.
You might have "version conflict" warnings on compilation.
Upvotes: 15
Reputation: 1181
This is not a complete answer but I noticed on your GitHub issue page that you are referencing both .NET Standard and .NET Framework libraries in your project. This is known to NOT work correctly.
Quoting .NET Standard team's announcement,
.. Another symptom is warnings at build time regarding assembly versions ..
which may be what you are running into.
Upvotes: -1
Reputation: 100523
NuGet only resolves single package versions.
If you declare a dependency on the minimum supported version, any referencing project can upgrade the dependency to a newer version.
As long as the authors of the dependent package don't introduce breaking changes, it should work find.
Even if you use reflection to look at the actual assembly versions used, you will find that many package authors don't change the assembly version between releases. This is to avoid the need for binding redirects in classic .NET Framework projects as all versions are the same and NuGet will select the right DLL based on the resolved package version of the consuming project. Again, this is good for as long as there are no breaking changes.
A pattern you could use to support different packages is to provide many "platform" packages that the consumer can choose from. The platform-specific package would then reference a common package with shareable logic.
The "platform" would then be e.g. "MyLogic.XUnit" or "MyLogic.NUnit" (assuming test helpers as example) referencing "MyLogic.Common"
Upvotes: 3