v8.0.0: ASP.NET Core, Web Client 2, Culture/Language improvements

2/19/2020 - Marco von Ballmoos (updated on 9/18/2020)

The summary below describes major new features, items of note and breaking changes.

The links above require a login.


Breaking Changes

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

See the Quino-Web/Sandbox.Web project for a working example. This integrates the standard SandboxApplication into a web site using the standard GenericController and MetadataController to provide data and UI to the generic Quino Client.

Namespace Changes

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 Encodo.Quino.Core to Encodo.Quino.Culture:

  • LanguageTextAttribute
  • IValueParser
  • CaptionAttribute
  • LanguageDescriptionAttribute

The following types were moved from Encodo.Quino.Core to Encodo.Quino.TextFormatting:

  • IFileSizeFormatter

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.

Default Languages

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. Applications should use the IDynamicStringFactory in most cases, instead.

An application can control fallback by registering custom IDynamicStringFallbackCalculator and 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 AddAmericanEnglishAndSwissGerman() instead.


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.

Database Fields

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").

Manual MetaIds

A product that sets MetaIds manually will migrate without modification (Quino will rename the property in the database).

Automatic MetaIds

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).

Regenerating Code

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 Caption_en.

Sign up for our Newsletter