Serenity 8.0.7 Release Notes (2023-12-07)

Transitioning to System.Text.Json from Newtonsoft.Json in .NET

We are migrating to System.Text.Json from Newtonsoft.Json, as it is the recommended and more performant library in .NET.

Please note that the transition initially occured in the 8.0.1 version. We patched some issues, and as of 8.0.7 it is stable.

Compatibility and Workarounds

There exist several compatibility issues between Newtonsoft and System.Text.Json. Check the following link for detailed insights: Migrate from Newtonsoft to System.Text.Json.

To ensure compatibility, we've implemented various workarounds such as using custom converters, specific serializer settings, etc.

For example, in versions before .NET 8, handling missing members by raising an exception was not feasible with System.Text.Json.

Update Requirements for Users

After the transition, users must add attributes specific to System.Text.Json to properties and classes that previously had Newtonsoft-specific attributes. For instance:

  • Replace Newtonsoft.Json.JsonProperty with System.Text.Json.Serialization.JsonPropertyName.
  • Modify converters and usage of attributes like JsonIgnore, among others.

An example includes the UserListRequest DataProtector property, which previously had a JsonIgnore attribute specific to Newtonsoft.

You may choose to keep Newtonsoft specific attributes while adding their System.Text.Json counterparts to keep compatibility and still use Newtonsoft.Json in some parts of your application.

Transition Guidelines

The converter classes unique to System.Text.Json reside in the Serenity.JsonConverters namespace and end with JsonConverter. For example:

  • The former converter specific to Newtonsoft.Json for rows was JsonRowConverter.
  • The new one for System.Text.Json is Serenity.JsonConverters.RowJsonConverter.

When using [JsonConverter(typeof(...))] attribute, users should exercise caution to specify the correct type, as the attribute name remains the same for both libraries while only the namespace is different.

To transition, users need to:

  • Remove the .AddNewtonsoftJson block in Startup.cs.

  • Replace it with the following line to configure serialization settings similar to the previous Serenity Newtonsoft.Json-based settings:

    services.Configure<JsonOptions>(options => JSON.Defaults.Populate(options.JsonSerializerOptions));
    
  • Additionally, remove this package reference from the project file:

    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
    

Behavior Changes

Unlike Newtonsoft.Json, IncludeNulls is replaced with WriteNulls as we will only ignore nulls during serialization. The parameter names are also changed to writeNulls to align with this behavior.

System.Text.Json will be used for JSON.Stringify methods, and there won't be any means to use Newtonsoft other than manually calling Newtonsoft.Json.JsonConvert.

Retaining Newtonsoft.Json Reference

We have not yet removed the Newtonsoft.Json reference as it is necessary for referencing serialization attributes, ensuring compatibility. While we may consider its removal in the future, it won't happen immediately.

Dashboard Page Rewrite with JSX-DOM

The StartSharp dashboard page, previously containing boilerplate markup within DashboardIndex.cshtml and DashboardPage.tsx files, has undergone a rewrite using jsx-dom.

All HTML markup in the DashboardIndex.cshtml file has been transformed into functional components, each residing within separate folders and files.

For instance, consider the ChatCard component located under Modules/Common/Dashboard/chat/chat-card.tsx:

import { messageList } from "./chat-mock-data";
import { ChatMessages } from "./chat-messages";
import { ChatHeader } from "./chat-header";
import { ChatSidebar } from "./chat-sidebar";

export const ChatCard = () =>
    <div class="card s-dashboard-card s-chat">
        <div class="row g-0" data-status="online">
            <ChatSidebar />
            <div class="col-12 col-lg-7 col-xl-8">
                <ChatHeader />
                <div class="position-relative">
                    <ChatMessages messages={messageList} />
                </div>
                <div class="flex-grow-0 py-3 px-4">
                    <div class="input-group">
                        <input type="text" class="form-control" placeholder="Type your message" />
                        <button class="btn btn-primary">Send</button>
                    </div>
                </div>
            </div>
        </div>
    </div>

Additionally, ChatSidebar, ChatHeader, and ChatMessages are also functional components.

This rewrite serves as a helpful example for jsx-dom, simplifying the creation and customization of your own dashboards. It marks a significant milestone in our transition from jQuery to jsx-dom.

The Default jsxImportSource Set to jsx-dom/min

In version 6.8.0, we adopted the JSX Automatic Runtime feature by configuring the jsx property in tsconfig.json to react-jsx:

{
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
}

Initially, the default import source remained as "preact" for those utilizing React or Preact based .tsx files in their projects. Consequently, we included a JSX pragma atop all our samples and components using jsx-dom:

/** @jsxImportSource jsx-dom/min */

Moving forward, it appears that jsx-dom will be our default library choice due to its lack of VDOM (virtual DOM) usage, aligning better with our historically designed widget system centered around jQuery UI widgets.

In certain limited scenarios, Preact might still find use. For instance, our EmailClient utilizes preact. Should you wish to utilize React or Preact in specific TSX files, simply include the pragma:

/** @jsxImportSource preact */

Any TSX file within our feature packages will default to assuming jsx-dom/min and will not include the pragma comment. Should your tsconfig.json file have a different default jsxImportSource configuration, either update the default to jsx-dom/min or add a pragma to any sample code you adopt from ours.

A New UI Library Based on jsx-dom

We are currently in the process of developing a new NPM package, @serenity-is/base, which will encompass core functions and classes from @serenity-is/corelib, predominantly from the old Q namespace.

This forthcoming base library will be shared between @serenity-is/corelib and a new UI library, tentatively named @serenity-is/ui. The UI library, likely based on jsx-dom, is in the planning phase and is expected to have no dependencies on jQuery or jQuery UI.

Presently, @serenity-is/base is integrated into @serenity-is/corelib, so no changes are required in this release.

Improvements to Sergen

We have incorporated MSBuild's new command-line build property extraction feature in Sergen for .NET 8+. This enhancement facilitates a more consistent determination of properties like OutDir, TargetFramework, etc., thereby enhancing Sergen's ability to locate your output assemblies.

Furthermore, Sergen now accommodates passing MSBuild properties to avoid manual parsing of property items from the project file. This modification contributes to cases where Sergen previously encountered difficulties determining properties such as the output path, configuration, etc. This update not only accelerates transformation speeds but also mitigates the need for Sergen to parse properties itself.

For instance, derived from our Directory.Build.targets file in the common-features repository:

<Target Name="TransformServerTypings" AfterTargets="AfterBuild">
    <Exec Command="dotnet sergen servertypings -p &quot;$(ProjectFileName)&quot; 
    -prop:Configuration=$(Configuration) 
    -prop:AssemblyName=&quot;$(AssemblyName)&quot; 
    -prop:OutDir=&quot;$(OutDir.Trim('\'))&quot; 
    -prop:RootNamespace=$(RootNamespace) 
    -prop:TargetFramework=$(TargetFramework)"  />
</Target>

Utilizing these properties already available in the current build context, Sergen can forego calling MSBuild or manually parsing your project file, resulting in accelerated transformations.

We have also implemented improved methods for Sergen to discover included files from tsconfig.json, aligning the algorithm more closely with TypeScript's resolution process.

Moreover, Sergen now utilizes a cached file system for TypeScript parsing transformations, significantly enhancing transform times for projects with numerous files.

Dynamic Loading Use JSON Fetch to DynamicData Endpoint

Functions such as getLookup, getForm, getColumns, etc., under ScriptData, will default to using ~/DynamicData (JSON format) instead of ~/DynJS.axd (script format).

This modification aims to avoid using eval, considered harmful, during the dynamic loading of lookups, etc. Additionally, async versions of ScriptData functions like getLookupAsync, getFormAsync, etc., now utilize fetch instead of jQuery's ajax/XHR.

Service Methods Return Types Are PromiseLike

Methods like serviceCall / serviceRequest and generated services now return PromiseLike instead of JQueryXHR. This change prepares for a future replacement of jQuery ajax calls with fetch. While it's currently possible to cast the return type to JQueryXHR, this behavior may not always persist.

Serenity.CoreLib.js no longer includes TypeScript Helpers [Breaking Change]

We have discontinued shipping TypeScript's tslib functions (e.g., helpers) with Serenity.CoreLib.js. If you still have namespaces (non-modular) TypeScript code, please remove the "noEmitHelpers": true line from your tsconfig.json.

IFileSystem Interface Has GetLastWriteTimeUtc Method [Breaking Change]

The IFileSystem interface now incorporates a new GetLastWriteTimeUtc method. Consequently, the extra IGeneratorFileSystem interface and the same property from ITemporaryFileSystem have been removed. If you have a custom implementation for IFileSystem, ensure the implementation of the GetLastWriteTimeUtc method.