Application configuration in code using RuntimeConfiguration
The generated code, both adapter and selfservicing, offer several application-wide configuration settings which can be set through the usage of the RuntimeConfiguration class. For .NET full, you can, besides using the RuntimeConfiguration class, also use Application configuration through a .config file. For .NET Standard you're required to use the RuntimeConfiguration class for configuring the LLBLGen Pro Runtime Framework as on .NET Standard/core there are no .config files.
RuntimeConfiguration and application startup
It's essential that your code calling RuntimeConfiguration methods and properties is run as soon as possible after your application starts. This way you avoid using the LLBLGen Pro runtime framework internals without configuring them properly. E.g. if you use ASP.NET Core, a good place to call RuntimeConfiguration methods is in the Configure method of ASP.NET Core Startup class.
Example of RuntimeConfiguration usage
Below is an example of how using the RuntimeConfiguration class to configure the LLBLGen Pro Runtime Framework at runtime looks like. The example below doesn't contain every option available.
// ... this code is placed in a method called at application startup
RuntimeConfiguration.AddConnectionString("ConnectionString.SQL Server (SqlClient)",
"data source=nerd;initial catalog=Northwind;integrated security=SSPI;persist security info=False;packet size=4096");
// Configure the DQE
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c => c.SetTraceLevel(TraceLevel.Verbose)
.AddDbProviderFactory(typeof(System.Data.SqlClient.SqlClientFactory))
.SetDefaultCompatibilityLevel(SqlServerCompatibilityLevel.SqlServer2012));
// Configure Dependency Injection
RuntimeConfiguration.SetDependencyInjectionInfo(
new List<Assembly>()
{
typeof(AddressEntity).Assembly,
this.GetType().Assembly
},
new List<string>()
{
"Northwind",
"Authorizers",
});
// Configure tracers
RuntimeConfiguration.Tracing
.SetTraceLevel("ORMPersistenceExecution", TraceLevel.Info);
.SetTraceLevel("ORMPlainSQLQueryExecution", TraceLevel.Info);
// Configure entity related settings
RuntimeConfiguration.Entity
.SetMarkSavedEntitiesAsFetched(true)
.SetMakeInvalidFieldReadsFatal(true);
The following sections will describe the methods used above as well as other, not illustated, options usable to configure the runtime framework.
Connection string configuration
To specify a connection string for your application, use the following method:
RuntimeConfiguration.AddConnectionString("Key", "ConnectionString");
Key is the name of the connection string as it's generated in the generated app.config
file in the generated code. This file
is generated for adapter in the DBSpecific project folder, and for SelfServicing it's generated in the generated code output folder.
ConnectionString is the connection string as generated in the app.config
file, with changes needed for your application at runtime.
Example:
RuntimeConfiguration.AddConnectionString("ConnectionString.SQL Server (SqlClient)",
"data source=.;initial catalog=Northwind;integrated security=SSPI;persist security info=False;packet size=4096");
To obtain a set connection string for a given key, use the method GetConnectionString(Key).
Dynamic Query Engine configuration
To make the LLBLGen Pro runtime framework be able to generate SQL at runtime, the Dynamic Query Engine (DQE) for the database you'll target has to be setup. All settings available to you for configuration are given below. Some are optional, and some are mandatory.
To be able to configure a DQE, the code calling RuntimeConfiguration needs a reference to the DQE package / dll specific for your database. E.g. if you are targeting SQL Server, you need a reference to the SD.LLBLGen.Pro.DQE.SQLServer.dll / nuget package, as the class used for configuration is present in that package.
The following classes are available for configuration of the different DQEs. Not all DQEs are supported on .NET Standard but as RuntimeConfiguration
is usable on .NET full as well, all DQEs offer a configuration class.
- SQLServerDQEConfiguration, for the SQL Server DQE
- OracleDQEConfiguration, for the Oracle DQEs.
- FirebirdDQEConfiguration, for the Firebird DQE
- MySqlDQEConfiguration, for the MySQL DQE
- PostgreSQLDQEConfiguration, for the PostgreSQL DQE
- DB2DQEConfiguration, for the IBM DB2 DQE
- AccessDQEConfiguration, for the MS Access DQE
- SpannerDQEConfiguration, for the Google Cloud Spanner DQE
All classes offer a fluent interface to configure all options in one go, using a Lambda:
RuntimeConfiguration.ConfigureDQE<DQEConfigurationClass>(c=>c.Method1(...)
.Method2(...));
Here c
is of type DQEConfigurationClass. See below for examples.
DbProviderFactory
The DbProviderFactory to use for your database is an essential class at runtime, as it is used to create connection objects and the like. On .NET full, the DbProviderFactory to use is in general obtained from the machine.config file of the .NET version you're using, or, in the case where the DbProviderFactory isn't installed in the machine.config file, it's obtained from the config file of your application. On .NET Standard however there's no machine.config file and it's essential that the DbProviderFactory is specified in code using RuntimeConfiguration.
No ADO.NET provider is available by default, you have to add the nuget package for the particular ADO.NET provider you want to use yourself, in all cases, including SqlClient.
To configure a DbProviderFactory, use the AddDbProviderFactory method on the DQE Configuration class. It's not required to specify an invariant name, it's by default derived from the namespace of the DbProviderFactory type specified.
Example, where the DbProviderFactory of SQLServer is specified:
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.AddDbProviderFactory(typeof(System.Data.SqlClient.SqlClientFactory)));
This requires your code to have a reference to the ADO.NET provider package of the database you're going to target, because there's no machine.config file nor GAC defined on .NET Standard, the type has to be specified explicitly.
On .NET full, setting a DbProviderFactory using AddDbProviderFactory will overwrite the factory obtained from machine.config/app.config. This means that if you're using ORM Profiler or other profiler which intercepts the ADO.NET provider factories from machine.config/app.config, setting the DbProviderFactory using AddDbProviderFactory will make these profilers to stop working.
For Google Cloud Spanner you always have to register the DbProviderFactory type with the DQE through the RuntimeConfiguration.ConfigureDQE method.
Tracing
To configure the trace switch specific for the DQE you'll use, (off by default), use the SetTraceLevel method on the DQE configuration class:
Example, where the "SqlServerDQE" trace switch is configured to emit output at the Verbose level.
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.SetTraceLevel(TraceLevel.Verbose));
See for more details about trace switches, the Troubleshooting and debugging - Conventions section.
Multi-tenancy support: Catalog name overwriting (SQL Server, MySQL)
Multi-tenancy support is implemented through catalog/schema name overwriting at runtime. This is configurable through code on e.g. the DataAccessAdapter and by using the DQE Configuration class by specifying catalog / schema name overwrites.
For generated code targeting SQL Server or MySQL, it can sometimes be necessary to use the generated code with a catalog with a different name than the catalog the project was created with. As LLBLGen Pro generates the catalog name into the code, it might be necessary to specify a way to rename the catalog name in the generated code at runtime with a given name.
LLBLGen Pro supports multiple catalogs per project, so you can specify more than one rename definition. This feature is similar to the feature discussed in Using the generated code / Adapter / DataAccessAdapter functionality (multi name setting) as it also offers a way to rename multiple catalogs in one go.
To specify a catalog name overwrite use the method AddCatalogNameOverwrite(fromName, toName). See the following example, which specifies a catalog overwrite for two catalogs for SQL Server. To configure it for MySql, you have to use the MySqlDQEConfiguration class instead.
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.AddCatalogNameOverwrite("oldCatalogName1", "newCatalogName1")
.AddCatalogNameOverwrite("oldCatalogName2", "newCatalogName2"));
This example makes sure that at runtime any reference in the persistence info of the elements in the generated code to the catalog 'OldCatalogName1' is renamed to 'NewCatalogName1' and any reference to 'OldCatalogName2' is renamed to 'NewCatalogName2'.
You can also specify an empty string for the toName. In that case, the DQE will not specify a catalog name in the generated SQL elements, which will make the SQL target the catalog specified in the connection string.
Wildcards
To overwrite all catalog names in the generated code with a single name,
you can use the wildcard *
to specify the new name of the catalog to use. All catalog names in the generated code will
in that case be replaced with the name specified as toName with *
as
fromName. Specifying *
as fromName overrides all other specified catalog name overwrites.
Multi-tenancy support: Schema name overwriting (SQL Server, Oracle, DB2, PostgreSql)
Besides the ability to rename catalog names, offers LLBLGen Pro also the functionality to rename schema names. This is supported for SQL Server, Oracle, PostgreSql and DB2 databases. To specify a schema name overwrite, use the method AddSchemaNameOverwrite(fromName, toName).
See the following example, which specifies a schema overwrite for two schemas for SQL Server. To specify a schema name overwrite for another database, use the DQE configuration class for that database instead of the SQLServerDQEConfiguration class.
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.AddSchemaNameOverwrite("oldSchemaName1", "newSchemaName1")
.AddSchemaNameOverwrite("oldSchemaName2", "newSchemaName2"));
This example makes sure that at runtime any reference in the persistence info of the elements in the generated code to the catalog 'OldSchemaName1' is renamed to 'NewSchemaName1' and any reference to 'OldSchemaName2' is renamed to 'NewSchemaName2'.
Keep in mind that schema renames are global. So you can't rename schema 'dbo' in catalog 'Foo' to 'schema1' and 'dbo' in catalog 'Bar' to 'schema2'.
You can also specify an empty string for the toName. In that case, the schema name isn't generated into the SQL. For some situations this can be handy, though keep in mind that if you use empty strings for schemas on SQL Server, you also have to specify an empty string for the catalog name using AddCatalogNameOverwrite.
Wildcards
To overwrite all schema names in the generated code with a single name,
you can use the wildcard *
for fromName to specify the new
name of the schema to use. All schema names in the generated code will
in that case be replaced with the name specified as toName with *
as
fromName. Specifying *
as fromName overrides all other specified schema name
overwrites.
SQL Server specific configuration options
The following options are specific for SQL Server.
Set default compatibility level
To use a different default compatibility level of the SQL Server DQE use the method SetDefaultCompatibilityLevel(level):
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.SetDefaultCompatibilityLevel(SqlServerCompatibilityLevel.SqlServer2012));
By default the compatibility level is set to: SQL Server 2005+.
Setting this compatibility level is setting the default value which is used in every SQL Server Dynamic Query Engine instance to produce a SQL query. When using Adapter, you can overrule this setting at runtime for a single DataAccessAdapter instance by setting the property CompatibilityLevel of the DataAccessAdapter instance.
See Database specific features, SQL Server Specific: compatibility mode, for details about the SQL specific effects each compatibility mode has.
Set ArithAbortOn flag
To set the global flag 'ArithAbortOn' you can use either the static field on the DQE's DynamicQueryEngine class, or use the method SetArithAbortOnFlag(value):
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(
c=>c.SetArithAbortOnFlag(true));
This sets the ArithAbortOn static field in the SQL Server DQE to the value specified. If true (Default is false) it will
signal the DQE to generate SET ARITHABORT ON
statements prior to INSERT, DELETE and UPDATE Queries.
PostgreSQL specific configuration options
To following options are specific for PostgreSQL
Use case-insensitive names
To make the DQE generate case insensitive names for tables/fields etc., use the method SetCaseInsensitiveNamesFlag(value):
RuntimeConfiguration.ConfigureDQE<PostgreSQLDQEConfiguration>(
c=>c.SetCaseInsensitiveNamesFlag(true));
Default is false
.
Oracle specific configuration options
The following options are specific for Oracle. The Oracle DQE for MS Oracle (System.Data.OracleClient) or ODP.NET use the same DQE configuration class OracleDQEConfiguration, so all options are available to both DQEs. Microsoft has deprecated System.Data.OracleClient and you should use ODP.NET instead. The options below are therefore described in relation to ODP.NET.
Trigger based sequence values
Some Oracle schemas use triggers to set the sequence value when a record is inserted. LLBLGen Pro's generated code
for Oracle uses a second query to first determine the next value of the sequence (using NEXTVAL
as sequence function)
then pass that value to the insert query as a normal value.
If that code is then used to insert a row in a table with a trigger for sequence values, the trigger will update the inserted value with a new value, making the value returned to the LLBLGen Pro runtime core not the value the trigger inserted into the sequenced field.
To overcome this, the Oracle DQE can be configured to execute a sequence retrieval query which
checks for the current value (using CURRVAL
as sequence function) after the insert took place, instead of
the sequence retrieval query which checks for the next value before
the insert takes place.
To do that, use the following method: SetTriggerSequencesFlag(value):
RuntimeConfiguration.ConfigureDQE<OracleDQEConfiguration>(c=>c.SetTriggerSequencesFlag(true));
Default is false
, which will result in a sequence call which asks for the next value before the insert.
Disable ansi-joins
All the DQE's of LLBLGen Pro use ansi-joins by default.
To disable ansi joins (and thus use the FROM A, B, C
style joins), use the following method: SetAnsiJoinsFlag(value):
RuntimeConfiguration.ConfigureDQE<OracleDQEConfiguration>(c=>c.SetAnsiJoinsFlag(false));
Default is true
, which will result in normal behavior: ansi joins.
Use case-insensitive names
To make the DQE generate case insensitive names for tables/fields etc., use the method SetCaseInsensitiveNamesFlag(value):
RuntimeConfiguration.ConfigureDQE<OracleDQEConfiguration>(
c=>c.SetCaseInsensitiveNamesFlag(true));
Default is false
.
Always use the unmanaged DbProviderFactory
If you use RuntimeConfiguration on .NET Full, you don't have to specify the DbProviderFactory, the runtime will pick the one associated with the DQE used. For ODP.NET, this is by default the managed provider for ODP.NET, and if it's not available it will switch to the unmanaged provider. In case you want to use the unmanaged provider in all cases (as the unmanaged provider has some limitations like no Xml support), use the following method:
RuntimeConfiguration.ConfigureDQE<OracleDQEConfiguration>(
c=>c.SetAlwaysChooseUnmanagedProvider(true));
Default is false
.
Set default compatibility level
To use a different default compatibility level of the Oracle DQE use the method SetDefaultCompatibilityLevel(level):
RuntimeConfiguration.ConfigureDQE<OracleDQEConfiguration>(
c=>c.SetDefaultCompatibilityLevel(OracleCompatibilityLevel.Oracle12c));
By default the compatibility level is set to: Oracle 9i/10g/11g
Setting this compatibility level is setting the default value which is used in every Oracle Dynamic Query Engine instance to produce a SQL query. When using Adapter, you can overrule this setting at runtime for a single DataAccessAdapter instance by setting the property CompatibilityLevel of the DataAccessAdapter instance.
See Database specific features, Oracle Specific: compatibility mode, for details about the SQL specific effects each compatibility mode has.
MySql specific configuration options
The following options are specific for MySql
Catalog name inclusion
By default, the catalog name isn't included in queries for MySQL, as versions of LLBLGen Pro before v3 didn't support multiple catalogs for MySQL and enabling it by default would be a breaking change. If you use multiple catalogs in your project for MySQL, you have to enable this setting by using the following method: SetIncludeCatalogNameInObjectNamesFlag(value).
RuntimeConfiguration.ConfigureDQE<MySqlDQEConfiguration>(
c=>c.SetIncludeCatalogNameInObjectNamesFlag(true));
Default is false
.
Firebird specific configuration options
The following options are specific for Firebird
Set default compatibility level
To use a different default compatibility level of the Firebird DQE use the method SetDefaultCompatibilityLevel(level):
RuntimeConfiguration.ConfigureDQE<FirebirdDQEConfiguration>(
c=>c.SetDefaultCompatibilityLevel(FirebirdCompatibilityLevel.Firebird15));
By default the compatibility level is set to: Firebird 2.x
Setting this compatibility level is setting the default value which is used in every Firebird Dynamic Query Engine instance to produce a SQL query. When using Adapter, you can overrule this setting at runtime for a single DataAccessAdapter instance by setting the property CompatibilityLevel of the DataAccessAdapter instance.
It's recommended to leave the compatibility level to the default. Only set it to Firebird15, if you use Firebird 1.5 and you run into problems with the aliases emitted on sub queries inside SELECT statements, which isn't supported on Firebird 1.5.
Trigger based sequence values
Some Firebird databases use triggers to set the sequence value when a record is inserted. LLBLGen Pro's generated code
for Firebird uses a second query to first determine the next value of the sequence (using 1 instead of 0 in GEN_ID
)
then pass that value to the insert query as a normal value.
If that code is then used to insert a row in a table with a trigger for sequence values, the trigger will update the inserted value with a new value, making the value returned to the LLBLGen Pro runtime core not the value the trigger inserted into the sequenced field.
To overcome this, the Firebird DQE can be configured to execute a sequence retrieval query which
checks for the current value (using 0 instead of 1 in GEN_ID
) after the insert took place, instead of
the sequence retrieval query which checks for the next value before
the insert takes place.
To do that, use the following method: SetTriggerSequencesFlag(value):
RuntimeConfiguration.ConfigureDQE<FirebirdDQEConfiguration>(c=>c.SetTriggerSequencesFlag(true));
Default is false
, which will result in a sequence call which asks for the next value before the insert.
Use case-insensitive names
To make the DQE generate case insensitive names for tables/fields etc., use the method SetCaseInsensitiveNamesFlag(value):
RuntimeConfiguration.ConfigureDQE<FirebirdDQEConfiguration>(
c=>c.SetCaseInsensitiveNamesFlag(true));
Default is false
.
DB2 specific configuration options
The following options are specific for DB2
Use case-insensitive names
To make the DQE generate case insensitive names for tables/fields etc., use the method SetCaseInsensitiveNamesFlag(value):
RuntimeConfiguration.ConfigureDQE<DB2DQEConfiguration>(
c=>c.SetCaseInsensitiveNamesFlag(true));
Default is false
.
Dependency Injection configuration
To specify which assemblies the LLBLGen Pro runtime framework should probe for instance types to use in the Dependency Injection system, use the method SetDependencyInjectionInfo(assemblies, instanceTypeFilters) on the RuntimeConfiguration class. It allows you to specify both the assemblies and also the optional instanceTypeFilters:
- assemblies, an
IEnumerable<Assembly>
which contains one or more assemblies to probe for DependencyInjectionInfoAttribute annotated types (Instance types) - instanceTypeFilters, optional
IEnumerable<string>
(specifynull
if you don't need filters). If no filters are specified, all instance types are enabled. If you specify one or more instance type filters, only the instance type filters matching the filter will be enabled. The specified namespace fragment means that you can specify the first part of a namespace to get a match. So if you specify "Northwind", both the namespaces "Northwind.DAL" and "Northwind.BL" will match but "GUI.Northwind" will not. You can use instance filters if you have multiple groups of instance types defined in the assembly or assemblies specified and you only want to use a subset of them.
In the following example, the assembly containing the type MyValidator
is specified as the assembly to probe for instance types.
Additionally, two type filters are specified, "Namespace1" and "Namespace2".
RuntimeConfiguration
.SetDependencyInjectionInfo(new List<Assembly>() { typeof(MyValidator).Assembly },
new List<string>() { "Namespace1", "Namespace2" });
There's no auto-discovery setting on RuntimeConfiguration as it's in general better to specify the assemblies to probe instead of letting the runtime probe all assemblies reachable at runtime, which could harm startup time.
Tracing configuration
The LLBLGen Pro Runtime Framework has a variety of Trace Switches which can be configured using the RuntimeConfiguration class. The DQE related trace switches are configured using the Dynamic Query Engine configuration classes, see above. The other trace switches are configured using the method SetTraceLevel( switchName, level) on the property Tracing.
Example, which defines the level of two trace switches:
RuntimeConfiguration.Tracing
.SetTraceLevel("ORMPersistenceExecution", TraceLevel.Info);
.SetTraceLevel("ORMPlainSQLQueryExecution", TraceLevel.Info);
On .NET Core, if you want to receive tracing info in the console, use this tracer setup:
// add at the start of your program
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
After that, info emitted by the enabled tracers inside the LLBLGen Pro Runtime Framework will be shown on the console. You can also use this to e.g. show the trace output of unit tests with a unit test.
Prefetch path configuration
To specify the default value for the UseRootMaxLimitAndSorterInPrefetchPathSubQueries
static property on Prefetch paths, use the
following method on the Prefetching property of the RuntimeConfiguration class:
RuntimeConfiguration.Prefetching
.SetUseRootMaxLimitAndSorterInPrefetchPathSubQueriesDefault(true);
Default is false
. Use this method with care, in general it's not recommended to set this flag to another value than its default.
Xml Serialization configuration
To specify the culture to use for serializing and deserializing System.Single, System.Double and System.Decimal typed values to and from XML, use the following method on the Xml property of the RuntimeConfiguration class: SetCultureNameForXmlValueConversion(name).
Example, which sets the culture to 'en-US':
RuntimeConfiguration.Xml.SetCultureNameForXmlValueConversion("en-US");
You can specify either the empty string, which will result in the Invariant culture, or the the name of the culture to use, e.g. nl-NL or en-US. For the full list of iso culture names please see the CultureInfo class documentation in the MSDN.
Entity Behavior configuration
To configure entity and entity handling behavior of the runtime, you can use various settings, they're described below. All these methods are available on the Entity property of the RuntimeConfiguration class. The methods on Entity form a fluent interface, so you can chain methods together.
Mark an entity as 'fetched' after save
To automatically mark a saved entity as 'Fetched', use the method SetMarkSavedEntitiesAsFetched(value):
RuntimeConfiguration.Entity.SetMarkSavedEntitiesAsFetched(true);
Default is false
.
See for more information Using the entity classes - Setting the EntityState to Fetched automatically after a save: for adapter and for selfservicing.
Bypass built-in validation
To bypass build-in validation logic in favor for your own validation logic, use the method SetBuildInValidationBypassMode(mode).
RuntimeConfiguration.Entity
.SetBuildInValidationBypassMode(BuildInValidationBypass.BypassWhenValidatorPresent);
Default is NoBypass
.
See for more information: Validation per field or per entity - Bypassing build-in validation logic
Define Scale overflow correction action
To define the correction action for a Scale overflow error, use the method SetScaleOverflowCorrectionActionToUse(value).
RuntimeConfiguration.Entity
.SetScaleOverflowCorrectionActionToUse(ScaleOverflowCorrectionAction.Round);
Default is Truncate
.
See for more information: Validation per field or per entity - Defining the Scale overflow correction action to use
Specify string hashcode behavior
To specify that the LLBLGen Pro runtime should use case insensitive hashcodes for strings (the hashcode for "Foo" is the same as for "FoO"), use the following method: SetCaseSensitiveStringHashCodes(value).
RuntimeConfiguration.Entity.SetCaseSensitiveStringHashCodes(true);
Default is false
, which means hashcodes are case sensitive.
Hashcodes for string values in entity fields are used in prefetch path
mergers and for example projection distinct filtering.
In general you should keep this setting set to the default (true). Only set this to false if you're working with a case-insensitive database which has FK values which differ only in casing from their PK counterparts (e.g. the FK field value is "Foo" and the PK field value is "foo").
Specify non-nullable field set to null behavior
By default, setting a nonnullable field to null / nothing is silently resulting in a no-op, without any exception.
However it can be beneficial to have this as a mandatory validation option. To make the runtime throw an ArgumentOutOfRangeException
,
use the method SetMakeSettingNonNullableFieldsToNullFatal(value).
RuntimeConfiguration.Entity.SetMakeSettingNonNullableFieldsToNullFatal(true);
Default is false
.
Allow reads from deleted entities
By default reading a value from an entity object which contains an entity which was deleted will result in an ORMEntityIsDeletedException
.
If you want to allow reads from deleted entities however, use the method: SetAllowReadsFromDeletedEntities(value)
RuntimeConfiguration.Entity.SetAllowReadsFromDeletedEntities(true);
Default is false
.
Usage example 1 with data from appsettings.json
The following example, supplied by Kai Wadsack, shows how to consume environment variables and the appsettings.json
file to read information which is then used to configure the LLBLGen Pro Runtime Framework using the RuntimeConfiguration system.
The appsettings.json file:
{
"LLBLGen":{
"ConnectionStrings": {
"ConnectionString.SQL Server (SqlClient)": "data source=YOURDATABOX;initial catalog=SomeUser;User ID=user;Password=secret;persist security info=False;packet size=4096;Connection Timeout=300"
},
"SqlServerCatalogNameOverwrites": [
{
"CatalogName": "NewName",
"Overwrite": ""
}
],
"Tracing": {
"Switches": {
"SqlServerDQE": 0,
"ORMGeneral": 0,
"ORMStateManagement": 0,
"ORMPersistenceExecution": 0,
"ORMPlainSQLQueryExecution": 0
},
"Listeners": {
"Debug": false,
"Console": true,
"File": "TraceLog.log"
}
}
}
}
The code to parse the above file and set the various RuntimeConfiguration settings:
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Configuration;
using SD.LLBLGen.Pro.DQE.SqlServer;
namespace MyCompany.MyApp {
public static class ConfigurationHelper {
public static void ConfigureFromAppSettings() {
var environmentName = Environment.GetEnvironmentVariable("ASPNET_ENV");
if (string.IsNullOrEmpty(environmentName)) {
environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
}
if (string.IsNullOrEmpty(environmentName)) {
environmentName = "Production";
}
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{environmentName}.json", true, true)
.AddJsonFile($"appsettings.{Environment.MachineName}.json", true, true)
.Build();
ConfigureFromConfiguration(config);
}
public static void ConfigureFromJson(string configFileName) {
var configDirectory = Directory.GetCurrentDirectory();
ConfigureFromJson(configFileName, configDirectory);
}
public static void ConfigureFromJson(string configFileName, string configDirectory) {
var configuration = new ConfigurationBuilder()
.SetBasePath(configDirectory)
.AddJsonFile(configFileName, true, true)
.Build();
ConfigureFromConfiguration(configuration);
}
private static void ConfigureFromConfiguration(IConfiguration configuration) {
var llblGenSettings = configuration.GetSection("LLBLGen");
var traceSettings =
llblGenSettings.GetSection("Tracing");
List<IConfigurationSection> traceSwitches =
traceSettings.GetSection("Switches").GetChildren().ToList();
var traceListeners =
traceSettings.GetSection("Listeners");
List<IConfigurationSection> connectionStrings =
configuration.GetSection("ConnectionStrings").GetChildren().ToList();
if (!connectionStrings.Any()) {
connectionStrings =
llblGenSettings.GetSection("ConnectionStrings").GetChildren().ToList();
}
List<IConfigurationSection> catalogNameOverwrites =
llblGenSettings.GetSection("SqlServerCatalogNameOverwrites").GetChildren().ToList();
var isTraceEnabled = false;
foreach (var traceSwitch in traceSwitches.Where(s => s.Key != "SqlServerDQE")) {
if (!int.TryParse(traceSwitch.Value, out var value)) {
continue;
}
if (value > 0) {
isTraceEnabled = true;
}
RuntimeConfiguration.Tracing
.SetTraceLevel(traceSwitch.Key, (TraceLevel)value);
}
foreach (var connection in connectionStrings) {
RuntimeConfiguration.AddConnectionString($"{connection.Key}", connection.Value);
}
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(config => {
config
.AddDbProviderFactory(typeof(SqlClientFactory))
.SetDefaultCompatibilityLevel(SqlServerCompatibilityLevel.SqlServer2012);
var dqeTraceSetting =
traceSwitches.FirstOrDefault(s => s.Key == "SqlServerDQE");
if (dqeTraceSetting != null) {
if (int.TryParse(dqeTraceSetting.Value, out var value)) {
if (value > 0) {
isTraceEnabled = true;
}
config.SetTraceLevel((TraceLevel)value);
}
}
foreach (var catalogNameOverwrite in catalogNameOverwrites) {
var catalogName =
catalogNameOverwrite.GetChildren()?.FirstOrDefault(s => s.Key == "CatalogName")?.Value;
if (catalogName == null) {
continue;
}
var overwrite =
catalogNameOverwrite.GetChildren()?.FirstOrDefault(s => s.Key == "Overwrite")?.Value;
config.AddCatalogNameOverwrite(catalogName, overwrite ?? string.Empty);
}
});
if (!isTraceEnabled || !traceListeners.GetChildren().Any()) {
return;
}
var logToConsole = traceListeners.GetValue<bool>("Console");
var logToDebug = traceListeners.GetValue<bool>("Debug");
var logFileName = traceListeners.GetValue<string>("File");
Trace.Listeners.Clear();
if (logToConsole) {
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out, "Console"));
}
if (logToDebug) {
Trace.Listeners.Add(new DefaultTraceListener {
Name = "Debug",
LogFileName = logFileName
});
return;
}
if (string.IsNullOrEmpty(logFileName)) {
Trace.Listeners.Add(new TextWriterTraceListener(logFileName, "File"));
}
}
}
}
Usage example 2 with data from appsettings.json
The following example, supplied by Wayne Brantley, shows how to consume configuration settings from the appsettings.json
file and fill a simple configuration class to configure the LLBLGen Pro Runtime Framework using the RuntimeConfiguration system. The example uses SQL Server but is easy to change to your database of choice.
The appsettings.json file:
"LLBLGen": {
"ConnectionStrings": {
"Main.ConnectionString.SQL Server (SqlClient)": "data source=.;;initial catalog=Xyz;",
"Other.SQL": "data source=.;initial catalog=Other;"
},
"SqlServerCatalogNameOverwrites": [
{
"CatalogName": "",
"Overwrite": ""
}
],
"Tracing": {
"Switches": {
"SqlServerDQE": "Off",
"ORMGeneral": "Error",
"ORMStateManagement": "Off",
"ORMPersistenceExecution": "Off",
"ORMPlainSQLQueryExecution": "Off"
},
"Listeners": {
"Debug": false,
"Console": true,
"File": "TraceLog.log"
}
}
}
This data is read into a configuration class called LlblgenOptions
public class LlblgenOptions
{
public Dictionary<string, string> ConnectionStrings { get; set; } = new Dictionary<string, string>();
public List<LlblgenSqlServerCatalogNameOverwritesOption> SqlServerCatalogNameOverwrites { get; set; } =
new List<LlblgenSqlServerCatalogNameOverwritesOption>();
public LlblgenTracingOptions Tracing { get; set; } = new LlblgenTracingOptions();
public class LlblgenTracingOptions
{
public LlblgenTracingSwitchOptions Switches { get; set; } = new LlblgenTracingSwitchOptions();
public LlblgenTracingListenerOptions Listeners { get; set; } = new LlblgenTracingListenerOptions();
public class LlblgenTracingSwitchOptions
{
public TraceLevel SqlServerDQE { get; set; }
public TraceLevel ORMGeneral { get; set; }
public TraceLevel ORMStateManagement { get; set; }
public TraceLevel ORMPersistenceExecution { get; set; }
public TraceLevel ORMPlainSQLQueryExecution { get; set; }
}
public class LlblgenTracingListenerOptions
{
public bool Debug { get; set; }
public bool Console { get; set; }
public string File { get; set; }
}
}
public class LlblgenSqlServerCatalogNameOverwritesOption
{
public string CatalogName { get; set; }
public string Overwrite { get; set; }
}
}
To read the data into an instance of LlblgenOptions, the following code is used: (Bind() is an extension method from the Microsoft.Extensions.Configuration package)
// e.g. in ConfigureServices
var options = new LlblgenOptions();
configuration.Bind(options);
An instance of LlblgenOptions is then passed to this method:
public void OrmConfig(LlblgenOptions options)
{
foreach (var connectionString in options.ConnectionStrings)
{
RuntimeConfiguration.AddConnectionString(connectionString.Key, connectionString.Value);
}
RuntimeConfiguration.Tracing.SetTraceLevel(nameof(options.Tracing.Switches.ORMGeneral),
options.Tracing.Switches.ORMGeneral);
RuntimeConfiguration.Tracing.SetTraceLevel(nameof(options.Tracing.Switches.ORMPersistenceExecution),
options.Tracing.Switches.ORMPersistenceExecution);
RuntimeConfiguration.Tracing.SetTraceLevel(nameof(options.Tracing.Switches.ORMPlainSQLQueryExecution),
options.Tracing.Switches.ORMPlainSQLQueryExecution);
RuntimeConfiguration.Tracing.SetTraceLevel(nameof(options.Tracing.Switches.ORMStateManagement),
options.Tracing.Switches.ORMStateManagement);
RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(config =>
{
config.AddDbProviderFactory(typeof(SqlClientFactory))
.SetDefaultCompatibilityLevel(SqlServerCompatibilityLevel.SqlServer2012)
.SetTraceLevel(options.Tracing.Switches.SqlServerDQE);
foreach (var catalogNameOverwrite in options.SqlServerCatalogNameOverwrites)
{
config.AddCatalogNameOverwrite(catalogNameOverwrite.CatalogName, catalogNameOverwrite.Overwrite);
}
});
bool traceEnabled = options.Tracing.Switches.ORMGeneral != TraceLevel.Off
|| options.Tracing.Switches.ORMPersistenceExecution != TraceLevel.Off
|| options.Tracing.Switches.ORMPlainSQLQueryExecution != TraceLevel.Off
|| options.Tracing.Switches.ORMStateManagement != TraceLevel.Off
|| options.Tracing.Switches.SqlServerDQE != TraceLevel.Off;
if (traceEnabled)
{
if (options.Tracing.Listeners.Console)
{
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out, "Console"));
}
if (options.Tracing.Listeners.Debug)
{
Trace.Listeners.Add(new DefaultTraceListener { Name = "Debug", LogFileName = options.Tracing.Listeners.File });
}
else
{
if (!string.IsNullOrEmpty(options.Tracing.Listeners.File))
{
Trace.Listeners.Add(new TextWriterTraceListener(options.Tracing.Listeners.File, "File"));
}
}
}
}