Error & Exception Handling¶
Most of our own exceptions will be business exceptions.
- Do derive a new class from BusinessException for each business exception in the domain shared package.
[Serializable]
public class ModelExistException : BusinessException
{
public ModelExistException(string name, string serverName)
{
Code = OdmsErrorCodes.ModelExistException;
WithData("name", name);
WithData("serverName", serverName);
}
public ModelExistException(Exception innerException = null, LogLevel logLevel = LogLevel.Error)
: base(innerException: innerException, logLevel: logLevel, message: innerException?.Message)
{
Code = OdmsErrorCodes.ModelExistException;
}
}
- Do define error code for each business exception with <code-namespace>:<error-code> format at <ModuleName>ErrorCodes.cs static class in the domain shared package.
public static class OdmsErrorCodes
{
// Validation
public const string ValidationError = "Odms:00001";
...
// Model
public const string ModelExistException = "Odms:00251";
...
}
- Do define the code-namespace to localization resource mapping at the module configuration in the domain shared package.
One problem with throwing exceptions is how to localize error messages while sending it to the client.
In this example code namespace is Odms
.
[DependsOn(
typeof(AbpValidationModule)
)]
public class OdmsDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
...
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Odms", typeof(OdmsResource));
});
}
}
Localization resource is OdmsResource.cs
[LocalizationResourceName("Odms")]
public class OdmsResource
{
}
- Do generate a json file for each supported language according to the code specified in the standards in the domain shared package.
The name is a combination of an ISO 639 two-letter or three-letter lowercase culture code associated with a language and an ISO 3166 two-letter uppercase subculture code associated with a country or region.
Then any of the exceptions with Odms
namespace will be localized using their given localization resource. The localization resource should always have an entry with the error code key
{
"culture": "en",
"texts": {
// Error Codes
"Odms:00251": "A model named {name} already exists on the {serverName} server.",
}
}
- Do embed localization json files into assemblies and use them like physical files at runtime.
A file should be first marked as embedded resource to embed the file into the assembly.
You can directly edit your .csproj
file:
<ItemGroup>
<EmbeddedResource Include="Localization\Odms\*.json" />
<Content Remove="Localization\Odms\*.json" />
</ItemGroup>
This configuration recursively adds all files under the Localization\Odms
folder of the project (including the files you will add in the future).
Embedding a file in the project/assembly may cause problems if a file name contains some special chars. To overcome this limitation;
- Add
Microsoft.Extensions.FileProviders.Embedded
NuGet package to the project that contains the embedded resource(s).
<Project Sdk="Microsoft.NET.Sdk">
...
<PropertyGroup>
...
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Validation" Version="8.2.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.8" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Odms\*.json" />
<Content Remove="Localization\Odms\*.json" />
</ItemGroup>
</Project>
Use AbpVirtualFileSystemOptions
options class to register the embedded files to the virtual file system in the ConfigureServices method of your module.
[DependsOn(
typeof(AbpValidationModule)
)]
public class OdmsDomainSharedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<OdmsDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<OdmsResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/Odms");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Odms", typeof(OdmsResource));
});
}
}