Introduction
Let's say you have following message description table.
public class DatabaseMessages : MessageDescriptions
{
/// <summary>Singleton</summary>
static readonly Lazy<DatabaseMessages> instance = new Lazy<DatabaseMessages>(() => new DatabaseMessages().SetAllReadOnly().SetReadOnly());
/// <summary>Singleton</summary>
public static DatabaseMessages Instance => instance.Value;
/// <summary>Exception factory for http errors</summary>
static readonly Func<IMessage, Exception> httpExceptionFactory = (IMessage m) => new HttpRequestException(m.Print(), m.Error, (System.Net.HttpStatusCode)m.HttpStatusCode());
/// <summary>Initialize fields</summary>
public DatabaseMessages() : base() { this.AddRange(MessageDescriptionsExtensions.FieldReader(this)); }
/// <summary></summary>
public readonly IMessageDescription ChannelNotFound = Http(nameof(ChannelNotFound), "Channel '{channel}' is not found-", HttpStatusCode.NotFound);
/// <summary>User is not authenticated (logged in)</summary>
public readonly IMessageDescription UserNotAuthenticated = Http(nameof(UserNotAuthenticated), "User is not authenticated.", HttpStatusCode.Forbidden);
/// <summary>User is not (email) verified</summary>
public readonly IMessageDescription UserNotConfirmedEmail = Http(nameof(UserNotConfirmedEmail), "User's '{user}' e-mail is not verified.", HttpStatusCode.Forbidden);
/// <summary>User not found in database</summary>
public readonly IMessageDescription UserNotFound = Http(nameof(UserNotFound), "User '{user}' is not found.", HttpStatusCode.NotFound);
/// <summary>User is not subscribed on channel</summary>
public readonly IMessageDescription UserNotSubscribed = Http(nameof(UserNotSubscribed), "User '{user}' is not subscribed on channel '{channel}'.", HttpStatusCode.Forbidden);
/// <summary>Create http message</summary>
public static IMessageDescription Http(string key, string template, HttpStatusCode? httpCode)
{
// Create description
MessageDescription messageDescription = new MessageDescription(key, null, TemplateFormat.BraceAlphaNumeric.Text[template])
.SetCode(new FNVHash32().HashIn(key))
.SetHttpStatusCode(httpCode)
.SetException(httpExceptionFactory);
// Error
if (httpCode.HasValue && (int)httpCode.Value >= 300) messageDescription.SetHResult(0x80000000).SetSeverity(MessageLevel.Error);
// Ok
else messageDescription.SetHResult(0x00000000).SetSeverity(MessageLevel.Information);
// Return
return messageDescription;
}
}
Table can be added to dependency injection as is.
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<DatabaseMessages>(DatabaseMessages.Instance);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create message
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print message
WriteLine(status.Print()); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
If service collection has a localization, then initializer delegate is used to apply localization on the table.
// Create localization
ILocalization localization = new Localization()
.AddLine("fi", "ChannelNotFound", "Detect", "Kanavaa {channelId} ei löytyny")
.AddLine("fi", "UserNotAuthenticated", "Detect", "Käyttäjä ei ole kirjautunut")
.AddLine("fi", "UserNotConfirmedEmail", "Detect", "Käyttäjän \"{user}\" sähköposti ei ole varmistettu.")
.AddLine("fi", "UserNotFound", "Detect", "Käyttäjää {channelId} ei löytyny")
.AddLine("fi", "UserNotSubscribed", "Detect", "Käyttäjä {user} ei ole tilannut kanavaa {channel}.");
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<ILocalization>(localization)
.AddSingleton<DatabaseMessages>(
sp => new DatabaseMessages()
.LocalizeMessages(sp.GetRequiredService<ILocalization>())
.SetAllReadOnly()
.SetReadOnly()
);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create status object
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print status in "fi"
WriteLine(status.Print(CultureInfo.GetCultureInfo("fi"))); // "Kanavaa '00000000-0000-0000-1000-000000000000' ei löytyny"
If error message is forwarded to user interface, the display text need to be localized from exception.
try
{
// User was not found
messages.UserNotFound.Throw("00000000-0000-0000-1000-000000000000");
} catch (HttpRequestException e)
{
// Choose culture
CultureInfo fi = CultureInfo.GetCultureInfo("fi");
// Print unlocalized and localized messages
string unlocalized = e.Message;
string localized = e.AttachedMessage()?.Print(fi) ?? e.Message;
// Print
WriteLine(unlocalized); // "User '00000000-0000-0000-1000-000000000000' is not found"
WriteLine(localized ); // "Käyttäjää '00000000-0000-0000-1000-000000000000' ei löytynyt"
}
Messages can be localized to use active culture from ICultureProvider.
// Create localization
ILocalization localization = new Localization()
.AddLine("fi", "ChannelNotFound", "Detect", "Kanavaa '{channel}' ei löytyny")
.AddLine("fi", "UserNotAuthenticated", "Detect", "Käyttäjä ei ole kirjautunut")
.AddLine("fi", "UserNotConfirmedEmail", "Detect", "Käyttäjän '{user}' sähköpostia ei ole varmistettu.")
.AddLine("fi", "UserNotFound", "Detect", "Käyttäjää '{channel}' ei löytyny")
.AddLine("fi", "UserNotSubscribed", "Detect", "Käyttäjä '{user}' ei ole tilannut kanavaa '{channel}'.");
// Choose culture provider (Asp.Net uses Thread.CurrentThread.CurrentCulture)
ICultureProvider cultureProvider = CultureProvider.CurrentThread.Instance;
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<ILocalization>(localization)
.AddSingleton<DatabaseMessages>(
sp => new DatabaseMessages()
.LocalizeMessages(
localization: sp.GetRequiredService<ILocalization>(),
cultureProvider: cultureProvider)
.SetAllReadOnly()
.SetReadOnly()
);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Assign language
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fi-FI");
// Assign number and date formats
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("fi-FI");
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create status object
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print in CurrentUICulture (language) + CurrentCulture (units)
WriteLine(status); // "Kanavaa '00000000-0000-0000-1000-000000000000 'ei löytyny"
// Print with fallback culture
WriteLine(status.Print(CultureInfo.InvariantCulture)); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
try
{
// User was not found
messages.UserNotFound.Throw("00000000-0000-0000-1000-000000000000");
} catch (HttpRequestException e)
{
WriteLine(e.Message); // "Käyttäjää '00000000-0000-0000-1000-000000000000' ei löytynyt"
WriteLine(e.AttachedMessage()!.Print(CultureInfo.InvariantCulture)); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
}
Full Example
Full example
using System.Globalization;
using System.Net;
using Avalanche.Localization;
using Avalanche.Message;
using Avalanche.Template;
using Avalanche.Utilities;
using Microsoft.Extensions.DependencyInjection;
using static System.Console;
public class di
{
public static void Run()
{
{
// <01>
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<DatabaseMessages>(DatabaseMessages.Instance);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create message
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print message
WriteLine(status.Print()); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
// </01>
}
{
// <02>
// Create localization
ILocalization localization = new Localization()
.AddLine("fi", "ChannelNotFound", "Detect", "Kanavaa {channelId} ei löytyny")
.AddLine("fi", "UserNotAuthenticated", "Detect", "Käyttäjä ei ole kirjautunut")
.AddLine("fi", "UserNotConfirmedEmail", "Detect", "Käyttäjän \"{user}\" sähköposti ei ole varmistettu.")
.AddLine("fi", "UserNotFound", "Detect", "Käyttäjää {channelId} ei löytyny")
.AddLine("fi", "UserNotSubscribed", "Detect", "Käyttäjä {user} ei ole tilannut kanavaa {channel}.");
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<ILocalization>(localization)
.AddSingleton<DatabaseMessages>(
sp => new DatabaseMessages()
.LocalizeMessages(sp.GetRequiredService<ILocalization>())
.SetAllReadOnly()
.SetReadOnly()
);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create status object
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print status in "fi"
WriteLine(status.Print(CultureInfo.GetCultureInfo("fi"))); // "Kanavaa '00000000-0000-0000-1000-000000000000' ei löytyny"
// </02>
// <03>
try
{
// User was not found
messages.UserNotFound.Throw("00000000-0000-0000-1000-000000000000");
} catch (HttpRequestException e)
{
// Choose culture
CultureInfo fi = CultureInfo.GetCultureInfo("fi");
// Print unlocalized and localized messages
string unlocalized = e.Message;
string localized = e.AttachedMessage()?.Print(fi) ?? e.Message;
// Print
WriteLine(unlocalized); // "User '00000000-0000-0000-1000-000000000000' is not found"
WriteLine(localized ); // "Käyttäjää '00000000-0000-0000-1000-000000000000' ei löytynyt"
}
// </03>
}
{
// <04>
// Create localization
ILocalization localization = new Localization()
.AddLine("fi", "ChannelNotFound", "Detect", "Kanavaa '{channel}' ei löytyny")
.AddLine("fi", "UserNotAuthenticated", "Detect", "Käyttäjä ei ole kirjautunut")
.AddLine("fi", "UserNotConfirmedEmail", "Detect", "Käyttäjän '{user}' sähköpostia ei ole varmistettu.")
.AddLine("fi", "UserNotFound", "Detect", "Käyttäjää '{channel}' ei löytyny")
.AddLine("fi", "UserNotSubscribed", "Detect", "Käyttäjä '{user}' ei ole tilannut kanavaa '{channel}'.");
// Choose culture provider (Asp.Net uses Thread.CurrentThread.CurrentCulture)
ICultureProvider cultureProvider = CultureProvider.CurrentThread.Instance;
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddSingleton<ILocalization>(localization)
.AddSingleton<DatabaseMessages>(
sp => new DatabaseMessages()
.LocalizeMessages(
localization: sp.GetRequiredService<ILocalization>(),
cultureProvider: cultureProvider)
.SetAllReadOnly()
.SetReadOnly()
);
// Service provider
using var service = serviceCollection.BuildServiceProvider();
// Assign language
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fi-FI");
// Assign number and date formats
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("fi-FI");
// Get messages
DatabaseMessages messages = service.GetRequiredService<DatabaseMessages>();
// Create status object
IMessage status = messages.ChannelNotFound.New("00000000-0000-0000-1000-000000000000");
// Print in CurrentUICulture (language) + CurrentCulture (units)
WriteLine(status); // "Kanavaa '00000000-0000-0000-1000-000000000000 'ei löytyny"
// Print with fallback culture
WriteLine(status.Print(CultureInfo.InvariantCulture)); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
try
{
// User was not found
messages.UserNotFound.Throw("00000000-0000-0000-1000-000000000000");
} catch (HttpRequestException e)
{
WriteLine(e.Message); // "Käyttäjää '00000000-0000-0000-1000-000000000000' ei löytynyt"
WriteLine(e.AttachedMessage()!.Print(CultureInfo.InvariantCulture)); // "Channel '00000000-0000-0000-1000-000000000000' is not found"
}
// </04>
}
}
// <100>
public class DatabaseMessages : MessageDescriptions
{
/// <summary>Singleton</summary>
static readonly Lazy<DatabaseMessages> instance = new Lazy<DatabaseMessages>(() => new DatabaseMessages().SetAllReadOnly().SetReadOnly());
/// <summary>Singleton</summary>
public static DatabaseMessages Instance => instance.Value;
/// <summary>Exception factory for http errors</summary>
static readonly Func<IMessage, Exception> httpExceptionFactory = (IMessage m) => new HttpRequestException(m.Print(), m.Error, (System.Net.HttpStatusCode)m.HttpStatusCode());
/// <summary>Initialize fields</summary>
public DatabaseMessages() : base() { this.AddRange(MessageDescriptionsExtensions.FieldReader(this)); }
/// <summary></summary>
public readonly IMessageDescription ChannelNotFound = Http(nameof(ChannelNotFound), "Channel '{channel}' is not found-", HttpStatusCode.NotFound);
/// <summary>User is not authenticated (logged in)</summary>
public readonly IMessageDescription UserNotAuthenticated = Http(nameof(UserNotAuthenticated), "User is not authenticated.", HttpStatusCode.Forbidden);
/// <summary>User is not (email) verified</summary>
public readonly IMessageDescription UserNotConfirmedEmail = Http(nameof(UserNotConfirmedEmail), "User's '{user}' e-mail is not verified.", HttpStatusCode.Forbidden);
/// <summary>User not found in database</summary>
public readonly IMessageDescription UserNotFound = Http(nameof(UserNotFound), "User '{user}' is not found.", HttpStatusCode.NotFound);
/// <summary>User is not subscribed on channel</summary>
public readonly IMessageDescription UserNotSubscribed = Http(nameof(UserNotSubscribed), "User '{user}' is not subscribed on channel '{channel}'.", HttpStatusCode.Forbidden);
/// <summary>Create http message</summary>
public static IMessageDescription Http(string key, string template, HttpStatusCode? httpCode)
{
// Create description
MessageDescription messageDescription = new MessageDescription(key, null, TemplateFormat.BraceAlphaNumeric.Text[template])
.SetCode(new FNVHash32().HashIn(key))
.SetHttpStatusCode(httpCode)
.SetException(httpExceptionFactory);
// Error
if (httpCode.HasValue && (int)httpCode.Value >= 300) messageDescription.SetHResult(0x80000000).SetSeverity(MessageLevel.Error);
// Ok
else messageDescription.SetHResult(0x00000000).SetSeverity(MessageLevel.Information);
// Return
return messageDescription;
}
}
// </100>
}