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

Drop support for .NET Framework and .NET Standard #112

Open
vers-one opened this issue May 26, 2024 · 0 comments
Open

Drop support for .NET Framework and .NET Standard #112

vers-one opened this issue May 26, 2024 · 0 comments
Assignees
Milestone

Comments

@vers-one
Copy link
Owner

vers-one commented May 26, 2024

Description

PR #71 added nullable reference type annotations. This required switching VersOne.Epub project to C# 10 and adding an explicit C# version override in the csproj file:

<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>

This approach worked with a few caveats described in the issue #65.

Making some properties of a class non-nullable means that such class cannot have the default constructor because C# compiler requires all non-nullable members to be initialized when an instance of the class is created. The solution to this problem is to create a custom constructor and pass the initialization data through the constructor parameters.

This in turn caused an issue with integration tests which rely on JSON deserialization to load the test data. JSON deserializer can't set the property values if they don't have a setter, and doesn't know how to invoke the custom constructor. JSON.NET is able to use custom constructors, but only when the number of properties is equal to the number of constructor parameters and they have matching names. This and some other limitations described in the issue #77 made it unusable for the integration tests. The solution was to switch from Json.NET to System.Text.Json and implement a custom object deserializer that would find the most suitable class constructor and use it to initialize non-writable object properties (see ObjectDeserializer.cs file in PR #78 for reference).

This approach works well for integration tests, but doesn't work when this custom deserialization cannot be reused. One of such scenarios is running unit tests from Visual Studio Test Explorer window. xUnit allows unit tests to be marked with the [Theory] attribute which will cause that test executed multiple times, once for every set of test data:

[Theory(DisplayName = "Converting EpubVersion enumeration to string should succeed")]
[InlineData(EpubVersion.EPUB_2, "2")]
[InlineData(EpubVersion.EPUB_3, "3")]
[InlineData(EpubVersion.EPUB_3_1, "3.1")]
[InlineData((EpubVersion)0, "0")]
public void GetVersionStringTest(EpubVersion epubVersion, string expectedVersionString)

Visual Studio Test Explorer will shows these test executions as separate test cases grouped under one test, will show the statuses (passed / failed) for every test case, and will let the user to restart individual test cases. The only restriction is to have all test data to be serializable. xUnit will serialize primitive types like int or string automatically, but it will skip all other data types. If xUnit is not able to serialize test data for any of the test cases, it will switch to the single-test mode which will cause Visual Studio Test Explorer to show all test cases as a single test in its window. Additionally, a failure in one test case causes the whole test fail, and there is no way to restart individual test cases. xUnit allows to supply custom serializers and deserializers for the test data, but using the same custom serializer implemented for the integration tests would be too cumbersome in this case.

A simpler solution to all these problems would be to add a default parameterless constructor, mark the properties as required, and add an init setter. This will solve the deserialization issue while retaining the nullable reference type information. Unfortunately, the init keyword was added only in C# 9 and required was added in C# 11. While it is possible to add an explicit C# version 11 override, like it was done before with C# 10, the resulting code will not compile because these features require certain types that exist only in newer .NET versions (init uses IsExternalInit class added in .NET 5 and required uses RequiredMember attribute from .NET 7, among other auxiliary types from .NET 7). One workaround is to copy these classes directly into VersOne.Epub project, but even then the consumers of the Nuget package will not be able to use the init-only properties, if they are not targeting .NET 7 or not using C# 11.

This means that in order to use required and init properties in VersOne.Epub project, the following changes need to be made to the supported .NET versions (see C# language versioning for more details):

  • .NET Framework support needs to be dropped, as the latest .NET Framework 4.8.1 supports only C# 7.3.
  • .NET Standard 1.0 and 2.0 support needs to be dropped, since even the latest .NET Standard 2.1 supports only C# 8.
  • .NET 7 needs to be made the minimum supported version as it's the first .NET version to support C# 11.

A quick search among the VersOne.Epub consumers shows that most of them will not be affected, but there are still a few projects that target older versions of .NET (mostly, .NET 6) or .NET Framework.

One additional benefit of adding default constructors and marking properties as required and init-only is that the VersOne.Epub consumers will be able to serialize and deserialize VersOne.Epub.Schema types without any issues.

Proposed solution

  1. Mention in the release notes that the 3.x version branches will be the last ones that support .NET Framework and .NET Standard.
  2. Change the minimum supported version to .NET 7 and increase the major version of the project (which will become 4.0).
  3. Migrate VersOne.Epub.WpfDemo project to .NET 9.
  4. Continue backporting new features and bug fixes into 3.x version branches until January 2026 to give the application developers time to migrate their projects to a newer version of .NET.
@vers-one vers-one self-assigned this May 26, 2024
@vers-one vers-one added this to the 4.0.0 milestone Dec 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant