Quino v8.0.0: ASP.NET Core, Web Client 2, Culture/Language improvements
Published by marco on
The summary below describes major new features, items of note and breaking changes.
The links above require a login.
- Quino-Web now targets ASP.NET Core (QNOWEB-149, QNOWEB-84, QNOWEB-55)
- Quino-Web has Serilog enabled by default (QNOWEB-147, QNOWEB-146, QNOWEB-145, QNOWEB-139)
- Enabled SourceLink for all packages (QNOWIN-262)
- Improved testing and debugging support. (QNO-6289, QNO-6282, QNO-6278, QNO-6277, QNO-6275, QNO-6255, QNO-6213)
- Improved culture and language-handling (QNO-6302, QNO-6303, QNO-6253, QNO-6230, QNO-6228)
- Extended expression functions with
CreateTime(). (QNO-6304, QNO-6305)
Before upgrading, products should make sure that they do not depend on any obsolete members in the current version (7.x).
Quino-Web 8.0 is a rewrite and is therefore mostly incompatible with 7.x.
- The controller returns data in a completely different format
- The Quino Client has been completely rewritten to accommodate it
- The startup and pipeline have been completely rewritten to integrate with ASP.NET Core
- Testing support has been considerably extended to accommodate end-to-end integration testing and in-process hosts
Quino-Web/Sandbox.Web project for a working example. This integrates the standard
SandboxApplication into a web site using the standard
MetadataController to provide data and UI to the generic Quino Client.
Some internal types in Quino-Standard have been moved to more appropriate namespaces and assemblies, but the impact on products should be non-existent or very limited.
The following types were moved from
The following types were moved from
Culture- and Language-Handling
Quino’s default culture-handling has been overhauled. Instead of tracking its own language, Quino now uses the standard .NET
CultureInfo.CurrentUICulture for the default language and
CultureInfo.CurrentCulture for default formatting (e.g. times, dates, and currencies). Many fields have been marked as obsolete and are no longer used by Quino.
The default languages in Quino have changed from “en-US” and “de-CH” to “en and “de”, respectively.
The reasoning behind this is that, while a _requested language_ should be as specific as possible, a _supported language_ should be as general as possible. The standard culture mechanisms and behavior (e.g. .NET Resources) “fall back” to a parent language when a more-specific language cannot be found. If an application claims to only support “en-US”, then a request for “en-GB” fails. If the supported language is “en”, then any request to a language in the “en” family (e.g. “en-US”, “en-GB”, “en-AU”) will use “en”.
An application that supports “en-US” and “de-CH” has, therefore, a more limited palette of languages that it can support.
Quino code runs in the context of a user, who has a list of preferred languages, in decreasing order of preference. This context can last the entire duration of an application (e.g. a standalone application like a console or desktop application) or last as long as a web request.
The application itself has a list of languages that it supports, as well as resources and metadata that defines text in these languages. The resources are standard .NET Resources with the standard fallback mechanism (i.e. a request for “en-US” can be satisfied by “en”). The metadata uses
DynamicString objects, which encapsulate a map from language codes (e.g. “en” or “de”) to strings.
During application startup or at the beginning of a web request, the
ILanguageResolver determines the language to use for a given set of requested languages. In ASP.NET Core, the requested languages come from the HTTP headers provided by the browser. In standalone applications, the
IRequestedLanguageCalculator provides the requested languages. The
ILanguageInitializer is responsible for coordinating this during application startup.
The rest of Quino uses the following singletons to work with languages.
IDynamicStringFallbackCalculator: Comes into play when a request is made for a language that is not directly supported. For example, if the application supports “en” and “de”, then a request for “en-US” will ask this singleton how to resolve the request.
IDynamicStringFactory: Creates a dynamic string to describe a given object. The default implementation uses .NET Attributes.
ILanguageResolver: Determines the culture to use from a list of available cultures and a list of requested/preferred cultures.
IRequestedLanguageCalculator: Provides the sequence of languages from which to choose during initial resolution (web requests _do not_ use this).
ILanguageInitializer: Integrates language-selection into the application startup.
ICaptionCalculator: Extracts a single caption for a culture from a given object. Appications should use the
IDynamicStringFactoryin most cases, instead.
An application can control fallback by registering custom
ILanguageResolver implementations (though this is almost certainly not necessary).
Opting in or out
Any product that calls
AddEnglishAndGerman() will automatically be upgraded as well. A product can avoid this change by calling
A product that uses the new languages will have to replace all fields in reports targeted at “en-US” and “de-CH” to target “en” and “de” instead.
A product that does use the new default languages will have to determine how to migrate database fields created for languages that are no longer explicitly supported. If the model includes value-lists (enums) or multi-language properties , the application will have to migrate the database schema to update multi-language fields (e.g. “caption_en_us” => “caption_en”).
A product that sets
MetaIds manually will migrate without modification (Quino will rename the property in the database).
A product that does _not_ set
MetaIds (this has been the default in Quino since version 2) will have a MetaID mismatch because the name has changed.
By default, Quino will migrate by attempting to drop, then re-create multi-language properties. In the case of value-list captions, this is harmless (since the data stored in these tables are generated wholly from the metadata). For actual multi-language properties with user data in them, this is _a problem_.
The simple solution is to call
UseLegacyLanguageMappingFinalizerBuilder() during application configuration to ensure a smooth migration (Quino will rename the property in the database).
A product that updates its languages should regenerate code to update any generated language-specific properties. Properties that had previously been generated as, e.g.
Caption_en_us will now be