Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I get an error after updating Castle.Core to 4.1.1 #418

Closed
skovsende opened this issue Jul 13, 2017 · 20 comments
Closed

I get an error after updating Castle.Core to 4.1.1 #418

skovsende opened this issue Jul 13, 2017 · 20 comments

Comments

@skovsende
Copy link

Using Moq 4.7.63, when upgrading Castle.Core to 4.1.1, the following code throws an exception:

myThingy = new Mock<IThingy> {DefaultValue = DefaultValue.Mock}.Object;

The exception is this:

System.TypeInitializationException : The type initializer for 'Moq.Mock`1' threw an exception.
  ----> System.TypeInitializationException : The type initializer for 'Moq.Proxy.CastleProxyFactory' threw an exception.
  ----> System.IO.FileLoadException : Could not load file or assembly 'Castle.Core, Version=4.1.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.
   at Moq.Mock`1.GenerateMockName()
   at Moq.Mock`1..ctor(MockBehavior behavior, Object[] args)
   at Moq.Mock`1..ctor(MockBehavior behavior)
   at Moq.Mock`1..ctor()
   at my code here...

It's an NUnit test, not that I can see that matteing, and it fails in the first line of my SetUp. It fails both in my Resharper test runner and on my build server which uses the NUnit commandline runner. What confuses me is that if I create a simple console program with the following it runs fine:

namespace MoqCastleCoreFailure
{
    class Program
    {
        static void Main(string[] args)
        {
            var mock = new Mock<IDisposable> { DefaultValue = DefaultValue.Mock }.Object;
        }
    }
}

A quick google search doesn't turn up anything sensible, so I'm inclined to think I'm missing something obvious.

Any ideas would be appreciated.

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

This is not an issue with Moq, but a general dependency management issue that can arise when working with NuGet, so I will close this issue. Hopefully, though, one of the following three suggestions will help you:

  1. I'd advise you not to update Castle.Core just for the sake of updating. Perhaps there is no need for you to provoke a version conflict in the first place? That is, if you don't actually need 4.1.1 (e.g. because you're making use of Castle.Core directly), then let Moq 4.7.63 have its Castle.Core 4.1.0. This is the easiest option.

  2. Try adding the required assembly version redirects to App.config or Web.config. (Sometimes, NuGet does this automatically for you.) See https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions.

    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
    </configuration>
  3. Wait another few days, then update Moq to the next version; I'll be pushing a Moq update to NuGet in the next few days which will use Castle.Core 4.1.1 (see d96684e).

@stakx stakx closed this as completed Jul 13, 2017
@skovsende
Copy link
Author

skovsende commented Jul 13, 2017

But would it not be more correct for moq to depend on a specific version of Castle.Core, by setting the dependency as:

<dependency id="Castle.Core" version="[4.1.0]" />

I am just asking out of curiosity here, so feel free to ignore me :)

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

No, in my opinion, that would not be more correct. (It would be correct if Moq were not a library, but an application; see links below.)

Pinning Moq to a very specific version of Castle.Core would be "correct" if you understand the version specification as a guarantee that Moq has been written against, and tested with, this exact version. The trouble here is that pinning only allows us to give one such guarantee, at the cost of making it much more likely that consumers will experience version conflicts like the one you've reported above. We want to avoid this if possible.

One way to avoid pinning is semantic versioning (semver), which most package managers these days use (including NuGet). Moq 4.7.63 declares that it requires Castle Core with any version 4.1.0 ≤ x < 5. This is more flexible than pinning one specific version, but it should still be valid because an increase in the minor version basically means more features (which Moq wouldn't know nor care about), but a new minor version may not introduce any breaking changes (which would affect Moq). Only an increase of the major version could introduce a breaking change (which is why we want to exclude version 5). Therefore, if Castle.Core follows semver rules correctly, we should have a guarantee that Moq can work with e.g. Castle Core 4.999.99 even though we've never explicitly tested it.

See also this question on Stack Overflow along with its answers, but especially this one. The question may be about Python, but the same principle is valid for other semver-compliant package management systems.

@skovsende
Copy link
Author

Yeah, I get the SemVer thing(but thanks alot for the references, they will come in handy! :)) - but just to make 100% sure that I have understand the problem. I only get the error I reported because Moq is signed, right? If it had been unsigned there would have been no problems changing the underlying dependency?

Thanks a bunch for taking the time to reply!

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

One aspect playing into this problem could be if you installed Moq and Castle.Core in a class library project. If you create a console app, install Castle.Core 4.1.1 and Moq 4.7.63, you'll see that NuGet automatically creates an assembly version redirect in App.config, and things just work. That obviously cannot happen in a class library, which doesn't have its own App.config file. (See also this Stack Overflow question, which claims that some test runners support a .dll.config file.)

I only get the error I reported because Moq is signed, right?

To be honest, I haven't considered this aspect, but yes, the fact that Moq's assembly is signed strong-named might indeed influence assembly resolution and provoke version conflicts when minor versions don't match. I can't remember the exact rules, but will check up on this.

@skovsende
Copy link
Author

A possibility could be to have a Moq-unsigned version I suppose. But it looks like I'm the only one with the problem, so hardly worth doing.

But again, thanks a bunch for the patience, that was one bit of world class open source support.

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

OK, I performed a quick test. If you have an App.config in your test project, and it contains the required assembly version redirect for Castle.Core, at least Visual Studio 2017's Test Explorer will apply that configuration. So if you add an App.config to your test project with the assembly redirect (as shown above; NuGet should've added that automatically), there shouldn't be any problem.

I don't think an unsigned version of Moq is necessary.

Thanks a bunch for the patience, that was one bit of world class open source support.

You're welcome. And thank you for the kind feedback, that is much appreciated!

@skovsende
Copy link
Author

Well, in my case the updating of the reference is automated, I have a build that does a nuget update on the solution, tries to compile with the new set of libraries, and if it works, tests pass etc., commit the result. In that case the automation story gets a little more complicated if we need to mess about with updating app.config as well. So not worth it in this case - it's easy enough to just lock the dependency in place for now.

@skovsende
Copy link
Author

I wondered, because I have other "root" assemblies that have updated automaticly this way for some time, without problems. So I looked into it a bit.

My application uses Serilog with the Serilog.Sinks.Seq sink, both are seperate nuget packages, and the Serilog package have had multiple patch updates without us running into the problem I had with Moq.

But when I look at the Serilog package I see that even though the package itself is at 2.5, the assembly version is frozen at 2.0.0.0 - so it doesn't follow the package or file-version. So even though the Serilog.Sinks.Seq package depends on Serilog>=2.3.0 in the nuspec, the reference always points to 2.0.0.0, which means that my build doesn't break when Serilog has an update.

So in this case it would be equivalent to Caste.Core fixing their assembly version to 4.0.0.0.

I have no idea if this is a standard way of doing this - it seems kind of hackish by the Serilog guys.

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

@skovsende: That's very intriguing. There definitely seems to be a gap between (a) how .NET assembly versioning was designed (with things like the GAC etc.) and (b) the way NuGet manages package versions. I'm not sure what the current best practices are to bridge that gap, but I intend to find out.

If you do find out the best practices around the issue we just discussed, and how to avoid such version conflicts, and you see that Moq isn't following that best practice, then please open an issue so we can discuss this further and take the required actions!

@skovsende
Copy link
Author

Will do.

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

P.S.: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ is the perfect summary for the discussion we've just had, and indeed the recommended solution is to pin the assembly version / specify only a major version.

It would be nice to know how common that practice is.

@skovsende
Copy link
Author

Yes, I found the same and was about to post it. It describes our exact problem!

@JohanLarsson
Copy link
Contributor

JohanLarsson commented Jul 13, 2017

@stakx If you use paket.exe it installs the highest version that satisfies the constraints, nuget.exe install the minimal version. Paket does the right thing here.
The result is that if you add Moq you need to add a binding redirect if the it is not in sync with latest Castle version. Not that adding a redirect is hard but writing xml is not awesome either. Neither paket.exe nor nuget.exe adds redirects to dll projects AFAIR.

It would be correct if Moq were not a library, but an application; see links below.

Not sure I follow here, pinning makes a lot of sense as redirects are needed unless the matching version of Castle is installed.

@stakx
Copy link
Contributor

stakx commented Jul 13, 2017

Neither paket.exe nor nuget.exe adds redirects to dll projects AFAIR.

Possibly. FWIW, Visual Studio 2017 does (I checked earlier today) at least sometimes.

Not sure I follow here [...]

I would recommend you take a look at the Stack Overflow answer I linked to above, it does an excellent job of explaining this. The gist is that applications are at the top of the dependency graph and can have full control over all dependencies; libraries are not in the same fortunate situation and thus shouldn't force consumers to use any specific version.

If you use paket.exe

I've heard good things about Paket, but I am not the one to decide which package manager people use. That being said, thanks for sharing that bit of knowledge; perhaps switching to Paket can help some people avoid some conflicts! :)

@stakx
Copy link
Contributor

stakx commented Jul 17, 2017

@skovsende - For what it's worth, I've just pushed a new NuGet version of Moq (4.7.99) that uses Castle.Core 4.1.1, in case you want to upgrade and remove any assembly redirects you've had to set up.

@skovsende
Copy link
Author

Works like a charm. Cheers.

@Jothay
Copy link

Jothay commented Nov 7, 2017

I'm having this issue with 4.2.1 where specifying the version or leaving it off don't work, but the following made it happy:

<dependentAssembly>
  <assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-4.99999.99999.99999" newVersion="4.0.0.0" />
</dependentAssembly>

@masiton
Copy link

masiton commented Nov 15, 2017

Guys can this please be fixed? Currently if I attempt to add Moq through nuget manager in Visual Studio, it will always fail at runtime saying it was looking for Castle.Core at version 4.0.0.0, no redirects work. I've spent 4 hours trying to get it to work, it's just plain broken.

Did anyone get Moq to work through nuget?

@stakx
Copy link
Contributor

stakx commented Nov 16, 2017

This has been fixed to the extent that it can be fixed. Assembly versioning is difficult because .NET and NuGet use different versioning schemes, tooling is broken, and most people simply don't understand the intricacies of it all. Even Microsoft admits this is a pain point. See also #461 and dotnet/coreclr#14263.

Simple fix: upgrade Moq to the latest version (same for Castle Core if you're managing it explicitly, which you probably shouldn't unless your code makes direct use of it), and remove all binding redirects from your config file for these two libraries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants