.NET/C# Driver Strong Naming

In our most recent release of the MongoDB .NET/C# driver (v2.28), we chose to Strong Name our assemblies. This was a deliberate and thoughtful choice that we decided to make after taking into account many factors, some of which are listed below:

  • We had seen increasing demand over the years in the need for strong-named assemblies. Even though the request itself alludes to providing a strong named assembly in addition to a non strong named one, Microsoft does not recommend publishing two versions of the same library. Moreso, we did not want the overhead of maintaining two different flavors of our library as we work towards the next major version of our driver.
  • While Strong Naming does not provide any material benefits for .NET Core and .NET 5+, we still have a significant number of users using MongoDB with .NET Framework. Hence we wanted to release a 2.x version that supports Strong Naming so users can benefit from this change without needing a major version upgrade.
    We understand that this should’ve been listed as a breaking change at a minimum and we’ve rectified that now in our documentation.
  • The ecosystem has evolved since Strong Naming was initially introduced and the requests were made but the overarching benefits of Strong Naming still stand true.

While this change should not impact the majority of users, we recognize that this change may cause some issues in the following cases:

  • Your project has multiple dependencies that use the MongoDB.Driver library
  • Your project uses reflection for assembly loading

As a developer, if you are using a third-party NuGet package that includes or depends on our driver and want to update to the latest version of the driver, then the only available workarounds are:

  • Delay Updating: Hold off on updating the Driver until the dependent libraries have released their updated versions. While this may take some time, we are ready to assist in coordinating with library maintainers to expedite their updates. Please let us know how we can help.
  • Manual Update: Fork the third-party libraries and manually update the strong-named Driver. Though this is a more tedious process, it will enable you to use the latest Driver version.

We urge open source maintainers to update their dependent packages with Strong Named versions. We apologize for the inconvenience that this may cause but we appreciate your patience and understanding in this journey and can’t wait to share more updates with you that our team has been working on!

2 Likes

Sorry for the second post (the first one is still in waiting list).

You said that you did not choose to publish two diffferent version (signed and not signed) because microsoft Does not reccomend Strong naming and .NET libraries - .NET | Microsoft Learn

Actually if you look at that article you have two big red cross at the end.

The first one states DO NOT ADD … the strong naming key : And is the rule you broke. This is an important rule because Ms explicitly tells you that whi will break compiled code.

The second one states that you should not publish two version, signed and unsigned, becuase in the long run it can ecounter type conflict. This is a decision that you can have only when you first start publishing a libary.

You told also that you have increasing demand for strongly named assembly, it is strange because it is completely unused in .NET 5 and gives you compilation warning so you are talking only of legacy code.

In the end you decided that you need to have strongly singed assembly and introducing a big breaking changes. You examined the Microsoft link with the two rules and you decided that, instead of breaking rule 2, publishing both version, that will in the long run caused problem, you instead broke rule 1, introducing an immediately breaking of compiled code.

So instead of going in the road of publishing 2 version, and maybe tell the community that we have a, lets say, 1 year deadline and after that you will be publish only signed version, you decided to immediately break compiled code, right now, without even mentioning in the release notes.

I repeat: you had two rules, you decided to break the first and most impacting one, and did’n even mention in the release notes?

Guys in all my project if I update to the latest version the code does not compile.

My 2 cents.

2 Likes

I really cannot understand this decision because it is a major breaking change not even emphasyzed enough in the release notes.

It will cause all sort of compile time issues all around and there’s no simple workaround (none that I’m aware of) that can mitigate the problem.

About the proposed workaround:

  • Delay Updating: this might not be an option, especially if new versions of the drivers fix critical bugs.
  • Manual Update: forking libraries (how many?), updating drivers, republish to internal nuget feeds “can be ok”, but will increase the workload on teams that actually deliver products; moreover:
    • How many custom-compiled versions of the same packages we’ll see being published in nuget?
    • What about “closed source” code that depends on MongoDB? are we forced to pay for packages just to update to the latest version of the driver?

Microsoft itself admits Strong Naming is a thing of the past, quoting from:

Strong-names are left over from previous eras of .NET where sandboxing needed to differentiate between code that was trusted, versus code that was untrusted. However in recent years, sandboxing via AppDomains, especially to isolate ASP.NET web applications, is no longer guaranteed and is not recommended.

And you also recognize that it has no benefits at all for .NET Core and .Net 5+… It might have some “benefits” only for legacy code that still runs in .Net 4.x and only for very rare (and outdated imo) situations.

I’d really like to know the actual reasons that lead you guys to break the driver contract after 9 years of publishing an unsigned package.

Why signing it now that it has no actual benefit for the current and future .Net echosystem ?

2 Likes

Open source maintainer here. Strong Naming is a thing of the past. For .Net (4.x) you should release a specific build.

This driver will break all the modern .net ecosystem, mongo will be left behind.

From ms doc about SN:

For .NET Core and .NET 5+, strong-named assemblies do not provide material benefits. The runtime never validates the strong-name signature, nor does it use the strong-name for assembly binding.

Strong-names are left over from previous eras of .NET

Don’t be like IE, be like MongoDB.

1 Like

(post deleted by author)

It has been a while, and I haven’t tested it now, but I recall when a dependency became strong named (Polly???) I explicitly loaded the strong named version at start up (first statements of the application) and used AppDomain.AssemblyResolve Event to resolve references to the not strong named version.

Think of it as assembly binding redirect extended. :grinning:

The problem is that the code does not even compile. Signing an assembly generated completely different name. When My code needs to pass an instance of IMongoDatabase to a third party library you got compilation error because it cannot find the definition of IMongoDatabase unsigned.

https://jira.mongodb.org/browse/CSHARP-5206 for more reference, the problem is not binding redirect, it is the inability to even compile the code.

2 Likes

That happens when you are trying to reference both types (the strong named and the not strong named).

My “workaround” is for “fixing” the assembly binding.

I’m not referencing both types, I only reference the strongly typed one, but I use another nuget package that in turn reference the unsigned one. We know that we can perform assembly redirect via configuration file or other tricks, but the problem is the inability to compile, and that is the reason why it is always a bad practice to have a 10 years old library used by many and suddently add strongly typing :(.

Assembly redirect also is cited in the link above in this post, but we have a whole different problem, we needed to have a big bang update of all dependant libraries that uses old driver…

I’m not arguing the hassle of an assembly changing the string name (from null to something, in this case).

But you are having compile-time issues, because you are trying the use both. Otherwise, it would be a run-time issue for that library.

If you isolate the source code using one strong name from the other, you can explicitly load the assembly at startup and resolve the old assembly to the new one. You need code for that, because the assembly binding redirects configuration only redirects versions, not names and keys.

Actually if you are in this situation I’ve a simple github repository where I show how you can call the code through reflection

in .NET 6+ you can only use reflection and it will just work (Strongly Signed is not used anymore in .NET 6+)

In .NET 4.8 you still need to do some work to manually let referenced code to find the assembly since I was not able to fix this with only assembly redirect in App.Config (redirecting unsigned to signed seems not to work)

Assembly binding redirection only works on version number, not name or strong name.

1 Like

With a breaking change this impactful, I’m surprised you didn’t bump the major version number (https://semver.org/). We had absolutely no warning. Updated the NuGet’s in our dotnet8 projects and boom! everything no longer compiles. This seems like very bad behavior for such a highly popular package.

2 Likes

Thanks for the info. The link in the post on official documentation cites assembly redirection and I wanted to try it :grin: avoiding maybe missing something obvious.

If you look at the docs: Redirecting Assembly Versions - .NET Framework | Microsoft Learn, you’ll see that assembly binding redirection is composed of 2 parts:

  1. assemblyIdentity: name, publicKeyToken and culture
  2. bindingRedirect: oldVersion and newVersion

You cannot redirect from one assembly identity to another. Just from one version to another in the same assembly identity.

1 Like

That is the reason why related page in this original post https://www.mongodb.com/docs/drivers/csharp/current/upgrade/#version-2.28.0-potential-breaking-change is wrong because it explain how to do binding redirect, but binding redirect does not works (as you correctly pointed out) for unsigned → signed, because they are two different types.

1 Like

Is this a joke? We need to move to Atlas 8.0 as soon as it’s ready for operational use. This requires updating our driver to 2.29.0 once it’s released. We can’t wait for all our dependencies to update their MongoDB driver versions first.

Some libraries, such as MassTransit, have already updated to MongoDB driver version 2.28.0, which implies that this new version cannot be installed because the rest of the dependencies have not yet done so.

It is absurd to expect that all dependencies used in a project contain the same specific reference to another library.

We are quite surprised by this decision that blocks us and its implementation without notice. Please seriously reconsider removing this problem.

2 Likes

I would like to thank everyone for their input and personally apologize for the impact that adding a strong name to our assemblies has had on all of you. It was a unanimous decision from the .NET/C# Team here at MongoDB, to ship strong-named assemblies now and do so in 2.28.0 rather than 3.0.0. Mea culpa. Hopefully the explanation below provides some context.

The .NET/C# Driver 1.X releases were strong named. We removed strong naming with the release of the 2.0 Driver in 2015. We did this because of pain experienced by users with transitive dependencies and binding redirects. Soon after the release of 2.0, users started requesting strong named assemblies via CSHARP-1276. Although Microsoft no longer recommends strong names for applications, there is still a valid use case for libraries to be strong named. This is because some environments still require strong names such as SQL Server Integration Services, Microsoft Azure Data Factory, and others. Moreso, all .NET Core assemblies themselves are strong named for compatibility and serviceability.

So what have users requiring strong-named assemblies done for the past 9 years? Two community members went to the trouble of decompiling our assemblies, strong naming them, fixing up the inter-assembly references, relinking the assemblies, and building strong-named NuGet packages. They released unofficial strong-named packages such as MongoDB.Driver.signed. I would like to personally thank them for doing this for as long as they did. But for whatever reason they stopped publishing these strong-named packages with the 2.19.0 release. This left our strong-named users and partners without access to new features and bug fixes.

There are other 3rd party libraries like AspNetCore.Diagnostics.HealthChecks which have already been updated to the newer strong named version. Albeit painful, this is a one time change to be done. We understand the challenge with transitive dependencies and are open to reaching out to collaborate with other 3rd party open source developers to request them to update their MongoDB dependencies. Please let us know if we can help in doing so.

PS: MongoDB Atlas 8.0 does not necessarily need the driver version 2.29 and above, it will work with older versions of the driver as well. However, there are some capabilities that will be introduced in future versions of the driver like Range Indexes in v2.29 and Improved Bulk Write in v3.0 respectively.

It would be beneficial to continue to produce in the nuget package a .NET 5 version that is still not strongly typed. This because in .NET 5 and above, Strongly Named is not used anymore so you do not have any advantages, only problems.

At least everyone that is on .NET 5+ will be unaffected by the change. I know that this is more work for you, but actually it should be not very complicated to have two build. So you can produce .NET 4.8 signed .netstandard 2.0 signed, but .NET 5 unsiged

This will at least lessen the impact.

1 Like

.NET 5 is long out of support and I wouldn’t target that for anything. Starting next November, .NET 8.0 and 9.0 will be the only supported runtimes.

As inconvenient as this is, the problem was created when strong naming was removed, not now. After a while, this will no longer be a problem.

Nevertheless, since the issue was caused by MongoDB, MongoDB should provide proper complete and correct guidance. And that could be a repo that users can build the driver from.