2

I have created an extension to a package (AutoFixture), which implements one of its interfaces, ICustomization. I published the NuGet package with a compiled binary that references version 3.16 of AutoFixture.

When used in a project that holds a reference to version 3.18 of AutoFixture, the class that implements the ICustomization interface cannot be used. The compilation error given is that MyCustomization is not assignable to ICustomization.

By downloading and compiling the source to my extension, updating the AutoFixture reference, and including it in the new project, the compiler error goes away.

I would have thought with only a minor version increment (maintaining backwards compatibility) this would not have been an issue.

Why does my NuGet package only work with this version of AutoFixture, and is it possible to release a binary under NuGet that will work with all minor version increments (ie those that don't break backwards compatibility)?

2 Answers 2

5
+100

This is a consequence of type identity in .NET. A type isn't just identified by its namespace name and type name, it also includes the assembly it came from. Something called the "assembly qualified type name" in the framework. I can demonstrate it with a little sample program:

using System; class Program { static void Main(string[] args) { Console.WriteLine(typeof(IDisposable).AssemblyQualifiedName); Console.ReadLine(); } } 

Output:

System.IDisposable, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Note how the [AssemblyVersion] is included. All four numbers are checked, a type that has Version = 4.0.0.1 is not compatible and generates the compiler error message you got. This is what plays havoc on your code, the author modified his Properties\AssemblyInfo.cs file, it now reads:

 [assembly: AssemblyVersion("3.18.3.0")] 

Changed on April 13th from AssemblyVersion("3.18.2.0"). Version 3.18.2.0 was checked-in on April 12th, it survived for just one day :) Looking through the history, the author changes the version often, several times per month on average. Version 3.16.0.0 was released on November 30th, 2013. He has changed it fourteen times since then. For comparison, Microsoft has changed the version number for IDisposable only once in the past 9 years.

You cannot possibly keep up of course.

You ought to get in touch with the author to let him know about this. It is probably going to be difficult to talk him out of it though. You need to deal with this on your end by using a binding redirect. Lightly touched upon by this article about Nuget versioning. It can be done with the Add-BindingRedirect powershell command. This article shows how to run it automatically.

Sign up to request clarification or add additional context in comments.

3 Comments

Hans, having read the above it seems that AutoFixture already applies a BindingRedirect on installation (the latest version gives <bindingRedirect oldVersion="0.0.0.0-3.18.7.0" newVersion="3.18.7.0" />). Even with the BindingRedirect, the code will not compile as the MyCustomization class implements an older version of the interface (3.16.4). Or have I not understood what the BindingRedirect should contain?
Sure, that's the next casualty of a bindingRedirect like that. It pretends that every previous version is compatible. That's utter nonsense of course, a breaking change in a public interface will make your program keel over. Bad, bad practice. The only real thing you can do about it is keeping your code updated as fast as the library changes. You might want to lock-in a specific version so you don't have to dance that dance.
FWIW, AutoFixture follows Semantic Versioning, so there are no breaking changes between 3.16.4 and 3.18.7.
1

In order to understand the problem, I did this:

  1. Created a new F# Console Library
  2. From the Package Manager Console, I did this:

Install-Package AutoFixture.AutoEF

Update-Package

Add-BindingRedirect

Get-Package

Which produces this setup:

Id Version -- ------- AutoFixture 3.18.7 AutoFixture.AutoEF 0.2.0 Castle.Core 3.3.0 

and these assembly binding redirects:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-3.3.0.0" newVersion="3.3.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Ploeh.AutoFixture" publicKeyToken="b24654c590009d4f" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-3.18.7.0" newVersion="3.18.7.0" /> </dependentAssembly> </assemblyBinding> 

Then I wrote this code (that I realise is incorrect use of the EntityCustomization class):

open Ploeh.AutoFixture open AutoFixture.AutoEF [<EntryPoint>] let main argv = let fixture = Fixture().Customize( EntityCustomization( DbContextEntityTypesProvider( typeof<obj>))) printfn "%A" argv 0 // return an integer exit code 

This compiles without warnings or errors.

1 Comment

Thanks for your help, Mark. Strangely, in my demo app when I removed fixture.Customize(customization) and replaced it with customization.Customize(fixture), the app compiled - then I reverted the change and it compiled again! I'm still curious as to why this happened, but it's good to know that you saw no problems while setting up the package externally.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.