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 furt
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.
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.
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"