Nirman
Nirman

Reputation: 6793

Runtime errors when using multiple versions of ServiceStack assemblies in a same solution

Following is the exact scenario in my application.

ServiceStack.Interfaces.dll (4.5.4), ServiceStack.Text.dll (4.5.4), ServiceStack.Common.dll (4.5.4)

ServiceStack.Interfaces.dll (3.9.48), ServiceStack.Text.dll (3.9.48), ServiceStack.Common.dll (3.9.48)

I know this is not an ideal architecture, but I am having very less options considering this was developed by someone else, and we have to resolve the issue with very minimum impact.

Currently we are getting following error while trying to run Project A:

enter image description here

Actually, the project structure and dependencies (i.e., ServiceStack versions) were setup more than a year ago, and till date the solution was building and running properly without any issues.

However, since last week all developers started facing this issue, which make me wonder that the issue may be something else.

I know one way to resolve this issue is to upgrade all packages to same version obviously, but that would require some code changes (due to some breaking changes in ServiceStack 4+). Also, a lot of testing time, as Project A is being used at many places across the solution.

Could anyone please suggest a better approach, or shed some lights which can help me in further investigations?

Upvotes: 3

Views: 304

Answers (2)

Always Learning
Always Learning

Reputation: 2753

Having dealt with this issue on numerous occasions (I'm looking at you Newtonsoft), I can both empathize with what you are going through along with providing some insight. Many times as part of a release, you'll use MSBuild.exe from the command line executed via some application lifecycle management tool such as TFS, TeamCity, Jenkins, etc. When executing MSBuild from the command line, you'll typically end up with a folder structure such as this:

enter image description here

What happens is that your applications will typically exist in the _PublishedWebsites folder. However, when MSBuild executes, it puts all the needed dll files for your entire solution into a single parent folder and then partitions those out into the necessary sub-folders in _PublishedApplications. If a dll file is already built in the top-level folder, it is not built again as part of MSBuild.

The end result is that if you are referencing two dll files in the same solution that are different versions, the one that is built first will end up in the parent folder and thus in all the applications that depend on it regardless of whether it is the correct version for that particular application. In your case, the 4.5.4 versions are now getting built first and thus being used in all applications that depend on it.

Why you may have not seen this before can be for numerous reasons such as not publishing the affected application until recently, your build order being changed by the inclusion of new solution items, etc.

One way to play with this is to move the specific project that is using the 3.9.48 version higher in the build order than the project that uses 4.5.4. I'd bet that after you did that, the application that's currently throwing runtime errors for you will start working after republishing the dll files. However, now your other application is quite likely to have issues if you republish from the same output folder as it'll get the 3.9.48 version of the dll.

enter image description here

There are a couple ways to fix this:

  • Update the dll file dependencies to the same version in your solution.
  • Split off the applications that depend on this particular dll into their own solution files.

I'd recommend creating a separate solution for each application if possible to gain all the benefits of decoupling.

Upvotes: 1

Connor
Connor

Reputation: 897

You can't load multiple versions of the same assembly at runtime. Binding Redirects are typically used to resolve the issue you are experiencing.

To redirect all versions of an assembly to a specific version, you would add a redirect to your web.config or app.config like the one below. NuGet does this automatically. (Replace ABCD with the actual public key token of the assembly.)

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="ServiceStack.Interfaces" publicKeyToken="ABCD" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-4.5.4" newVersion="4.5.4" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

Since you mentioned that it was working until last week, something has likely changed in one or both of the projects. It's possible that:

  • You previously had redirects and they are no longer present
  • One of the projects upgraded to a newer version (e.g. from X to 4.5.4)
  • The code referencing ServiceStack in the two projects was using APIs that are consistent between the two versions (3.9.X and 4.5.X). Since then, code has been introduced that uses APIs that are not consistent between the two versions. When there are backwards-incompatible changes, this can lead to unexpected behavior that cannot be solved with binding redirects.

Upvotes: 4

Related Questions