-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Bugfix: System.Xml.Serialization: Compiler.GetTempAssemblyName is not deterministic under .NET Core #46499
Bugfix: System.Xml.Serialization: Compiler.GetTempAssemblyName is not deterministic under .NET Core #46499
Conversation
… persistent under .NET Core The implementation of String.GetHashCode() was persistent by default in .NET Framework. allowing to predictably name an XmlSerializers.{HashCode}.dll containing the pre-generated serializers. In .NET Core / .NET 5, String.GetHashCode() is no longer persistent, so the value of ns.GetHashCode() will change during each run, preventing it from picking up the XmlSerializers dll correctly.
Tagging subscribers to this area: @buyaa-n, @krwq Issue DetailsThe implementation of String.GetHashCode() was persistent by default in .NET Framework. allowing to predictably name an XmlSerializers.{HashCode}.dll containing the pre-generated serializers. In .NET Core / .NET 5, String.GetHashCode() is no longer persistent, so the value of ns.GetHashCode() will change during each run, preventing it from picking up the XmlSerializers dll correctly.
|
@TalAloni would truncated SHA256 work as a workaround for you? It can produce deterministic results. |
Hi Krzysztof, using truncated SHA256 is a good solution but for backward compatibility sake I think there are benefits to using the same hash used by the .NET Framework. |
I talked this over with the team and wanted to recommend a different approach. Most of this is going to be pretty obvious, but just bear with me while I think out loud for a minute. It appears that this hash code is being appended only when working with a default namespace. Which makes sense - keeping assemblies separate for different namespaces. The hash code is meant to be a predictable and persistent way to keep namespaces separated without having to append an entire namespace string in the assembly name which could be ugly at best and could run into more impactful issues at worst. However, the original implementation fell into the trap of using String.GetHashCode() which was never meant to be persistent. In fact, it is not consistent across .Net Core and .Net 4.8. It isn't even consistent across 32 and 64-bit implementations of 4.8. String.GetHashCode() kind of worked by accident here, but was not really ever appropriate for the function it is performing here. Another thing to note is that generated assemblies do not really share between 4.8 and .Net Core due to netstandard.dll. So maintaining some resemblance with either 32-bit or 64-bit .Net 4.8 isn't much of a concern here. So there is a bit of a "green field" opportunity to get this right this time. Criteria to note: We want something that produces a repeatable, persistent hash. The hash should be of reasonable length to avoid collision, but it is not required to be exactly 32-bits. Also, since this is not a hot code path where every bit of performance counts, we don't need to worry about being super fast. Finally, we're using this hash for naming purposes, not cryptographic purposes. GetHashCode() meets all those criteria except for the most important - it's obviously not persistent. But rather than re-inventing an old wheel just because it feels familiar (although, this new wheel will not actually bring compatibility with the old cart)... lets use a tool that is proven and easily accessible. Use a truncated SHA512 hash. You could truncate to 32-bits as done previously. Or maybe use Span/Guid to truncate and get 128-bits with nice formatting? Perhaps @mconnew has more thoughts? Regardless, I believe you'll also want to change SGen to match this change as well. https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs#L523 |
Thanks for the feedback,
|
@TalAloni, there are some scenarios where sharing a generated assembly between .NET Core and XmlSerializer can work, but it's very fragile and there are many scenarios which fail and there's simply no solution to the problem. You are better off pre-generating serializers for .NET Framework using .NET Framework tooling, and creating serialzers for Core using Core tooling. Having a different hash code used for .NET Framework and Core would actually help in this situation as you can have both dll's next to your application and each platform would ignore the pre-generated serializer from the other one. |
Thank you. |
src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs
Outdated
Show resolved
Hide resolved
…Compiler.cs Co-authored-by: Stephen Toub <stoub@microsoft.com>
…Compiler.cs Co-authored-by: Stephen Toub <stoub@microsoft.com>
Hello @stephentoub! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
* Update GetTempAssemblyName according to #46499 In #46499 we corrected Compilation.GetTempAssemblyName in order for it to be not deterministic under .NET Core. In this PR we update the generated filename to match the new logic. * Update Microsoft.XmlSerializer.Generator.csproj * Update SGen.cs Avoid using external dependency * Update Microsoft.XmlSerializer.Generator.csproj Avoid using external dependency * Update Sgen.cs Fixed compilation
The implementation of String.GetHashCode() was persistent by default in .NET Framework. allowing to predictably name an XmlSerializers.{HashCode}.dll containing the pre-generated serializers.
In .NET Core / .NET 5, String.GetHashCode() is no longer persistent, so the value of ns.GetHashCode() will change during each run, preventing it from picking up the XmlSerializers dll correctly.