diff --git a/.editorconfig b/.editorconfig index 4dc5335..6c69d7a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,53 +1,238 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories root = true # All files [*] -indent_style = space +indent_style = space +tab_width = 4 trim_trailing_whitespace = true + +# Markdown files +[*.md] +trim_trailing_whitespace = false + + # Code files [*.{cs,csx,vb,vbx}] -indent_size = 4 +tab_width = 4 +indent_size = 4 +indent_style = space insert_final_newline = true -charset = utf-8-bom +charset = utf-8-bom -############################### -# .NET Coding Conventions # -############################### + +# .NET Coding Conventions [*.{cs,vb}] + +#### Core EditorConfig Options #### + +# New line preferences +end_of_line = crlf + +#### .NET Coding Conventions #### + # Organize usings -dotnet_sort_system_directives_first = false +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false -# this. preferences -dotnet_style_qualification_for_field = false : suggestion -dotnet_style_qualification_for_property = false : suggestion -dotnet_style_qualification_for_method = false : suggestion -dotnet_style_qualification_for_event = false : suggestion +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion -dotnet_style_predefined_type_for_member_access = true : suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent # Expression-level preferences -dotnet_style_object_initializer = true : suggestion -dotnet_style_collection_initializer = true : suggestion -dotnet_style_explicit_tuple_names = true : warning -dotnet_style_coalesce_expression = true : suggestion -dotnet_style_null_propagation = true : suggestion - -############################### -# C# Coding Conventions # -############################### -[*.cs] -# Expression-level preferences -csharp_prefer_braces = true : suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:warning + +#### C# Coding Conventions #### # var preferences -csharp_style_var_when_type_is_apparent = true : suggestion -csharp_style_var_elsewhere = false : suggestion +csharp_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion # Expression-bodied members -csharp_style_expression_bodied_properties = true : suggestion -csharp_style_expression_bodied_methods = true : suggestion -csharp_style_expression_bodied_indexers = true : suggestion -csharp_style_expression_bodied_accessors = true : suggestion +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true:suggestion +csharp_prefer_simple_using_statement = false:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:warning + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = flush_left +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming styles + +dotnet_naming_style.pascal_case_style.required_prefix = +dotnet_naming_style.pascal_case_style.required_suffix = +dotnet_naming_style.pascal_case_style.word_separator = +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +dotnet_naming_style.interface_style.required_prefix = I +dotnet_naming_style.interface_style.required_suffix = +dotnet_naming_style.interface_style.word_separator = +dotnet_naming_style.interface_style.capitalization = pascal_case + +dotnet_naming_style.field_style.required_prefix = _ +dotnet_naming_style.field_style.required_suffix = +dotnet_naming_style.field_style.word_separator = +dotnet_naming_style.field_style.capitalization = camel_case + +# Naming rules + +# Use "I" prefix for interfaces +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = interface_style + +# Use PascalCase for constants +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +# Use PascalCase for types +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case_style + +# Use PascalCase for properties, methods and events +dotnet_naming_symbols.non_field_members.applicable_kinds = property, method, event +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case_style + +# Use "_" prefix for private and internal fields (except constants) +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private +dotnet_naming_symbols.private_or_internal_field.required_modifiers = +dotnet_naming_rule.private_or_internal_field_should_be_field_name.severity = warning +dotnet_naming_rule.private_or_internal_field_should_be_field_name.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_field_name.style = field_style + +# Use "_" prefix for private and internal static fields +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static +dotnet_naming_rule.private_or_internal_static_field_should_be_field_name.severity = warning +dotnet_naming_rule.private_or_internal_static_field_should_be_field_name.symbols = private_or_internal_static_field +dotnet_naming_rule.private_or_internal_static_field_should_be_field_name.style = field_style diff --git a/.gitignore b/.gitignore index e020d30..cbb33f4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,6 @@ # CodeRush .cr -# XML documentation files -Kros.Utils.xml -Kros.KORM.xml - # User-specific files *.suo *.user diff --git a/Kros.KORM.Extensions.Asp.sln b/Kros.KORM.Extensions.Asp.sln index ade6d9d..6c74e89 100644 --- a/Kros.KORM.Extensions.Asp.sln +++ b/Kros.KORM.Extensions.Asp.sln @@ -9,6 +9,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kros.KORM.Extensions.Asp", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kros.KORM.Extensions.Api.UnitTests", "tests\Kros.KORM.Extensions.Api.UnitTests.csproj", "{C5837E0B-EAD8-4A82-88E7-FF00A8833447}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{73D684F8-E278-4E6E-8938-0B7D6489E5A6}" + ProjectSection(SolutionItems) = preProject + build-ci.yml = build-ci.yml + build.yml = build.yml + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/README.md b/README.md index b938bf9..b88223c 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,15 @@ To contribute with new topics/information or make changes, see [contributing](ht ## This topic contains following sections -* [ASP.NET Core extensions](#aspnet-core-extensions) -* [Database migrations](#database-migrations) +* [ASP.NET Core Extensions](#aspnet-core-extensions) +* [Database Migrations](#database-migrations) +* [Id Generators](#id-generators) -### ASP.NET Core extensions +### ASP.NET Core Extensions -You can use the `AddKorm` extension method to register `IDatabase` to the DI container. +You can use the `AddKorm` extension methods to register databases to the DI container. This registers `IDatabaseFactory` into DI container. This factory can be used to retrieve `IDatabase` instances by name. If no name is specified, default name `DefaultConnection` will be used. `IDatabase` instances has scoped lifetime. + +**The first** database registered by `AddKorm` method is also added to the DI container directly as `IDatabase` dependency. This is for simple use case, when only one database is used. So there is no need for using `IDatabaseFactory`. ``` csharp public void ConfigureServices(IServiceCollection services) @@ -30,53 +33,52 @@ public void ConfigureServices(IServiceCollection services) } ``` -The configuration file *(typically `appsettings.json`)* must contain a section `ConnectionString`. +The configuration file *(typically `appsettings.json`)* must contain a standard connection strings section (`ConnectionStrings`) and there must be connection string named `DefaultConnection`. Name of the connection string can be specified as second parameter. -``` jsonc -"ConnectionString": { - "ProviderName": "System.Data.SqlClient", - "ConnectionString": "Server=servername\\instancename;Initial Catalog=database;Persist Security Info=False;" +``` json +"ConnectionStrings": { + "DefaultConnection": "Server=ServerName\\InstanceName; Initial Catalog=database; Integrated Security=true", + "localConnection": "Server=Server2\\Instance; Integrated Security=true; KormAutoMigrate=true; KormProvider=System.Data.SqlClient;" } ``` -If you need to initialize the database for [IIdGenerator](https://kros-sk.github.io/Kros.Libs.Documentation/api/Kros.Utils/Kros.Data.IIdGenerator.html) then you can call `InitDatabaseForIdGenerator`. +Connection string can be passed directly to `AddKorm` method, together with its name. The name `DefaultConnection` will be used if no name is specified. ``` csharp public void ConfigureServices(IServiceCollection services) { - services.AddKorm(Configuration) - .InitDatabaseForIdGenerator(); + // Added from appsettings.json under "localConnection" name. + services.AddKorm(Configuration, "localConnection"); + + // Added directly with the name "db2". + services.AddKorm("Server=ServerName\\InstanceName; Initial Catalog=database; Integrated Security=true", "db2"); } ``` -### Database migrations +`AddKorm` extension methods supports additional keys in connection string, which will be used by KORM and **will be removed from the connection string**. These keys are: + +* `KormAutoMigrate`: The value is boolean `true`/`false`. If not set (or the value is invalid), the default value is `false`. If it is `true`, it allows automatic [database migrations](#database-migrations). +* `KormProvider`: This specifies database provider which will be used. If not set, the value `System.Data.SqlClient` will be used. KORM currently supports only Microsoft SQL Server, so there is no need to use this parameter. + +### Database Migrations + For simple database migration, you must call: + ``` csharp public void ConfigureServices(IServiceCollection services) { services.AddKorm(Configuration) - .AddKormMigrations(Configuration) + .AddKormMigrations() .Migrate(); } ``` -The previous code requires the `KormMigration` section in the configurations: - -``` jsonc -"KormMigrations": { - "ConnectionString": { - "ProviderName": "System.Data.SqlClient", - "ConnectionString": "Server=servername\\instancename;Initial Catalog=database;Persist Security Info=False;" - }, - "AutoMigrate": "true" -} -``` -Migrations are disabled by default, so you have to enable them by setting `AutoMigrate: true`. +Migrations are disabled by default, so the previous code requires that the automatic migrations are enabled in connection string: `KormAutoMigrate=true` -Korm performs migrations that default searches in the main assembly in the `SqlScripts` directory. The script file name must match pattern `{migrationId}_{MigrationName}.sql`. -`MigrationId` is increasing number over time. +Korm by default performs migrations by searching the main assembly for files in `SqlScripts` directory. The script file name must match pattern `{migrationId}_{MigrationName}.sql`. `MigrationId` is increasing number over time. For example: `20190301001_AddPeopleTable.sql` + ``` sql CREATE TABLE [dbo].People ( [Id] [int] NOT NULL, @@ -87,7 +89,7 @@ CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED ([Id] ASC) GO ``` -Migration can also be executed through an HTTP query. By calling the `/kormmigration` endpoint, the necessary migration will be executed. +Migration can also be executed through an HTTP request. By calling the `/kormmigration` endpoint, the necessary migrations will be executed. However, you need to add middleware: ``` csharp @@ -106,7 +108,7 @@ If you have scripts stored in a different way (for example, somewhere on a disk public void ConfigureServices(IServiceCollection services) { services.AddKorm(Configuration) - .AddKormMigrations(Configuration, o => + .AddKormMigrations(o => { var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.StartWith("Demo.DatabaseLayer")); o.AddAssemblyScriptsProvider(assembly, "Demo.DatabaseLayer.Resources"); @@ -118,3 +120,15 @@ public void ConfigureServices(IServiceCollection services) ``` KORM creates a `__KormMigrationsHistory` table in which it has a history of individual migrations. + +### Id Generators + +If you need to initialize the database for [IIdGenerator](https://kros-sk.github.io/Kros.Libs.Documentation/api/Kros.Utils/Kros.Data.IIdGenerator.html) then you can call `InitDatabaseForIdGenerator`. + +``` csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddKorm(Configuration) + .InitDatabaseForIdGenerator(); +} +``` diff --git a/src/DatabaseFactory.cs b/src/DatabaseFactory.cs new file mode 100644 index 0000000..0665dda --- /dev/null +++ b/src/DatabaseFactory.cs @@ -0,0 +1,76 @@ +using Kros.KORM.Extensions.Asp.Properties; +using Kros.Utils; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Kros.KORM.Extensions.Asp +{ + internal class DatabaseFactory : IDatabaseFactory + { + private static readonly ConcurrentDictionary> _builders = + new ConcurrentDictionary>(); + + private static Dictionary AddBuildersDictionary(IServiceCollection services) + => _builders.GetOrAdd(services, _ => new Dictionary()); + + /// + /// Adds with name into the list of builders for + /// . + /// + /// Service collection for which the builder is added. + /// The name of the database builder. + /// Database builder. + /// , if this was the first builder added, otherwise . + internal static bool AddBuilder(IServiceCollection services, string name, KormBuilder builder) + { + Dictionary builders = AddBuildersDictionary(services); + if (builders.ContainsKey(name)) + { + throw new ArgumentException(string.Format(Resources.DuplicateDatabaseName, name), nameof(name)); + } + builders.Add(name, builder); + + // We need to know if it was the first builder added. + // The first builder is added into the service container also as IDatabase dependency. + return builders.Count == 1; + } + + private readonly ConcurrentDictionary _databases = new ConcurrentDictionary(); + private readonly Dictionary _localBuilders; + private bool _disposed = false; + + internal DatabaseFactory(IServiceCollection services) + { + _localBuilders = AddBuildersDictionary(services); + } + + IDatabase IDatabaseFactory.GetDatabase(string name) + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(DatabaseFactory)); + } + Check.NotNullOrWhiteSpace(name, nameof(name)); + + if (!_localBuilders.TryGetValue(name, out KormBuilder builder)) + { + throw new ArgumentException( + string.Format(Resources.InvalidDatabaseName, name, nameof(ServiceCollectionExtensions.AddKorm)), + nameof(name)); + } + return _databases.GetOrAdd(name, _ => builder.Build()); + } + + public void Dispose() + { + _disposed = true; + foreach (IDatabase database in _databases.Values) + { + database?.Dispose(); + } + _databases.Clear(); + } + } +} diff --git a/src/DatabaseFactoryExtensions.cs b/src/DatabaseFactoryExtensions.cs new file mode 100644 index 0000000..20560db --- /dev/null +++ b/src/DatabaseFactoryExtensions.cs @@ -0,0 +1,17 @@ +namespace Kros.KORM.Extensions.Asp +{ + /// + /// Extension methods for . + /// + public static class DatabaseFactoryExtensions + { + /// + /// Returns database registered with default connection string name + /// (). + /// + /// Database factory. + /// Database instance. + public static IDatabase GetDatabase(this IDatabaseFactory factory) + => factory.GetDatabase(KormBuilder.DefaultConnectionStringName); + } +} diff --git a/src/IDatabaseFactory.cs b/src/IDatabaseFactory.cs new file mode 100644 index 0000000..45dde2d --- /dev/null +++ b/src/IDatabaseFactory.cs @@ -0,0 +1,26 @@ +using System; + +namespace Kros.KORM.Extensions.Asp +{ + /// + /// Factory for using multiple named databases. + /// + public interface IDatabaseFactory : IDisposable + { + /// + /// Returns database with specified . + /// + /// Name of the database to get. + /// The database. + /// The value of is . + /// + /// The value of is: + /// + /// Empty string. + /// String containing whitespace characters. + /// Ivalid name. The database with that name is not registered. + /// + /// + IDatabase GetDatabase(string name); + } +} diff --git a/src/KormBuilder.cs b/src/KormBuilder.cs index eec34b6..6611eab 100644 --- a/src/KormBuilder.cs +++ b/src/KormBuilder.cs @@ -1,14 +1,10 @@ using Kros.Data; -using Kros.KORM.Extensions.Asp.Properties; using Kros.KORM.Migrations; using Kros.KORM.Migrations.Middleware; using Kros.KORM.Migrations.Providers; using Kros.Utils; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; -using System.Configuration; -using System.Threading.Tasks; namespace Kros.KORM.Extensions.Asp { @@ -17,21 +13,58 @@ namespace Kros.KORM.Extensions.Asp /// public class KormBuilder { - private ConnectionStringSettings _connectionString; - private IDatabaseBuilder _builder; + /// + /// Default connection string name in configuration if no name is provided. + /// + public const string DefaultConnectionStringName = "DefaultConnection"; + + internal const string DefaultProviderName = Kros.Data.SqlServer.SqlServerDataHelper.ClientId; + internal const bool DefaultAutoMigrate = false; + + private readonly IDatabaseBuilder _builder; + + /// + /// Initializes a new instance of the class. Automatic migrations are off and + /// Microsoft SQL Server KORM provider is used. + /// + /// The service collection. + /// The database connection string. + public KormBuilder(IServiceCollection services, string connectionString) + : this(services, connectionString, DefaultAutoMigrate, DefaultProviderName) + { + } + + /// + /// Initializes a new instance of the class. Microsoft SQL Server KORM provider is used. + /// + /// The service collection. + /// The database connection string. + /// + /// Value for setting if automatic migrations () are allowed or not. + /// + public KormBuilder(IServiceCollection services, string connectionString, bool autoMigrate) + : this(services, connectionString, autoMigrate, DefaultProviderName) + { + } /// /// Initializes a new instance of the class. /// /// The service collection. - /// The connection string settings. - public KormBuilder(IServiceCollection services, ConnectionStringSettings connectionString) + /// The database connection string. + /// + /// Value for setting if automatic migrations () are allowed or not. + /// + /// KORM provider value. + public KormBuilder(IServiceCollection services, string connectionString, bool autoMigrate, string kormProvider) { Services = Check.NotNull(services, nameof(services)); - _connectionString = Check.NotNull(connectionString, nameof(connectionString)); + ConnectionString = Check.NotNullOrWhiteSpace(connectionString, nameof(connectionString)); + KormProvider = Check.NotNullOrWhiteSpace(kormProvider, nameof(kormProvider)); + AutoMigrate = autoMigrate; _builder = Database.Builder; - _builder.UseConnection(connectionString); + _builder.UseConnection(connectionString, kormProvider); } /// @@ -39,6 +72,10 @@ public KormBuilder(IServiceCollection services, ConnectionStringSettings connect /// public IServiceCollection Services { get; } + internal string ConnectionString { get; } + internal string KormProvider { get; } + internal bool AutoMigrate { get; } + /// /// Use database configuration. /// @@ -47,7 +84,6 @@ public KormBuilder(IServiceCollection services, ConnectionStringSettings connect public KormBuilder UseDatabaseConfiguration() where TConfiguration : DatabaseConfigurationBase, new() { _builder.UseDatabaseConfiguration(); - return this; } @@ -59,7 +95,6 @@ public KormBuilder(IServiceCollection services, ConnectionStringSettings connect public KormBuilder UseDatabaseConfiguration(DatabaseConfigurationBase databaseConfiguration) { _builder.UseDatabaseConfiguration(databaseConfiguration); - return this; } @@ -69,44 +104,27 @@ public KormBuilder UseDatabaseConfiguration(DatabaseConfigurationBase databaseCo /// This instance. public KormBuilder InitDatabaseForIdGenerator() { - var factory = IdGeneratorFactories.GetFactory(_connectionString.ConnectionString, _connectionString.ProviderName); - - using (var idGenerator = factory.GetGenerator(string.Empty)) + IIdGeneratorFactory factory = IdGeneratorFactories.GetFactory(ConnectionString, KormProvider); + using (IIdGenerator idGenerator = factory.GetGenerator(string.Empty)) { idGenerator.InitDatabaseForIdGenerator(); } - return this; } - private const string MigrationSectionName = "KormMigrations"; - private const string ConnectionStringSectionName = "ConnectionString"; - private const string AutoMigrateSectionName = "AutoMigrate"; - private bool _autoMigrate = false; - /// /// Adds configuration for into . /// - /// The configuration. /// Setup migration options. /// This instance of . - public KormBuilder AddKormMigrations( - IConfiguration configuration, - Action setupAction = null) + public KormBuilder AddKormMigrations(Action setupAction = null) { - IConfigurationSection migrationsConfig = GetMigrationsSection(configuration); - _autoMigrate = migrationsConfig.GetValue(AutoMigrateSectionName, false); - var connectionString = migrationsConfig - .GetSection(ConnectionStringSectionName).Get(); - Services .AddMemoryCache() - .AddTransient((Func)((s) => + .AddTransient((Func)(s => { - var database = new Database(connectionString); - + var database = new Database(ConnectionString, KormProvider); MigrationOptions options = SetupMigrationOptions(setupAction); - return new MigrationsRunner(database, options); })); @@ -115,7 +133,7 @@ public KormBuilder AddKormMigrations( private static MigrationOptions SetupMigrationOptions(Action setupAction) { - MigrationOptions options = new MigrationOptions(); + var options = new MigrationOptions(); if (setupAction != null) { @@ -129,24 +147,12 @@ private static MigrationOptions SetupMigrationOptions(Action s return options; } - private static IConfigurationSection GetMigrationsSection(IConfiguration configuration) - { - var migrationsConfig = configuration.GetSection(MigrationSectionName); - if (!migrationsConfig.Exists()) - { - throw new InvalidOperationException( - string.Format(Resources.ConfigurationSectionIsMissing, MigrationSectionName)); - } - - return migrationsConfig; - } - /// - /// Execute database migration. + /// Execute database migration. Migrations are executed only if they were enabled in constructor. /// public void Migrate() { - if (_autoMigrate) + if (AutoMigrate) { Services.BuildServiceProvider() .GetService() diff --git a/src/Kros.KORM.Extensions.Asp.csproj b/src/Kros.KORM.Extensions.Asp.csproj index 253ed4c..2a8bd4b 100644 --- a/src/Kros.KORM.Extensions.Asp.csproj +++ b/src/Kros.KORM.Extensions.Asp.csproj @@ -1,25 +1,24 @@  - netcoreapp2.1 - KROS a.s. + netcoreapp2.2 + KROS a. s. 1.1.0-alfa - KROS a.s. - Kros.KORM - This package contains extensios for project Kros.KORM for AspNet Core projects. - Copyright © KROS a.s. + KROS a. s. + Kros.KORM.Extensions.Asp + Extensions for Kros.KORM library for ASP.NET Core projects. + Copyright © KROS a. s. true - Kros.KORM.Extensions.Asp Kros.KORM.Extensions.Asp + Kros.KORM.Extensions.Asp + Kros.KORM.Extensions.Asp Kros.KORM.Extensions.Asp - bin\Release\Kros.KORM.xml + true Kros.KORM.Extensions.Asp - https://github.com/Kros-sk/Kros.Libs/blob/master/Kros.KORM.Extensions.Asp/README.md - - Kros;KORM;Asp.Net Core;Kros.KORM - + https://github.com/Kros-sk/Kros.KORM.Extensions.Asp + Kros;KORM;ASP.NET Core;Kros.KORM MIT https://en.gravatar.com/userimage/137934964/524e95fbd8c2e8779e02819ab6902bef.png https://github.com/Kros-sk/Kros.KORM.Extensions.Asp @@ -27,20 +26,17 @@ - bin\Debug\ - - - bin\Release\ + DEBUG;TRACE - - - - - - - + + + + + + + diff --git a/src/Migrations/Middleware/MigrationsMiddleware.cs b/src/Migrations/Middleware/MigrationsMiddleware.cs index 45ce3c1..809c158 100644 --- a/src/Migrations/Middleware/MigrationsMiddleware.cs +++ b/src/Migrations/Middleware/MigrationsMiddleware.cs @@ -12,7 +12,9 @@ public class MigrationsMiddleware { private const string WasMigrationExecutedKey = "WasMigrationExecuted"; +#pragma warning disable IDE0052 // Remove unread private members private readonly RequestDelegate _next; +#pragma warning restore IDE0052 // Remove unread private members private readonly MigrationMiddlewareOptions _options; private readonly IMemoryCache _cache; @@ -32,30 +34,29 @@ public MigrationsMiddleware( _cache = Check.NotNull(cache, nameof(cache)); } +#pragma warning disable IDE0060 // Remove unused parameter /// /// Invokes the specified context. /// /// The context. /// Migrations runner. - public async Task Invoke( - HttpContext context, - IMigrationsRunner migrationsRunner) + public async Task Invoke(HttpContext context, IMigrationsRunner migrationsRunner) { if (CanMigrate()) { SetupCache(); - await migrationsRunner.MigrateAsync(); } } +#pragma warning restore IDE0060 // Remove unused parameter private bool CanMigrate() - => !_cache.TryGetValue(WasMigrationExecutedKey, out var migrated) || !migrated; + => !_cache.TryGetValue(WasMigrationExecutedKey, out bool migrated) || !migrated; private void SetupCache() { - var options = new MemoryCacheEntryOptions() - .SetSlidingExpiration(_options.SlidingExpirationBetweenMigrations); + var options = new MemoryCacheEntryOptions(); + options.SetSlidingExpiration(_options.SlidingExpirationBetweenMigrations); _cache.Set(WasMigrationExecutedKey, true, options); } } diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 877f6d8..0e3b792 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -61,29 +61,38 @@ internal Resources() { } /// - /// Looks up a localized string similar to Configuration section '{0}' is missing.. + /// Looks up a localized string similar to Connection string contains only KORM keys. These are removed and so connection string is empty.. /// - internal static string ConfigurationSectionIsMissing { + internal static string ConnectionStringContainsOnlyKormKeys { get { - return ResourceManager.GetString("ConfigurationSectionIsMissing", resourceCulture); + return ResourceManager.GetString("ConnectionStringContainsOnlyKormKeys", resourceCulture); } } /// - /// Looks up a localized string similar to 'ConnectionString' is required.. + /// Looks up a localized string similar to Database with name "{0}" was already added.. /// - internal static string ConnectionStringIsRequired { + internal static string DuplicateDatabaseName { get { - return ResourceManager.GetString("ConnectionStringIsRequired", resourceCulture); + return ResourceManager.GetString("DuplicateDatabaseName", resourceCulture); } } /// - /// Looks up a localized string similar to 'ProviderName' is required.. + /// Looks up a localized string similar to Connection strings section does not contain a connection string with name '{0}'.. /// - internal static string ProviderNameIsRequired { + internal static string InvalidConnectionStringName { get { - return ResourceManager.GetString("ProviderNameIsRequired", resourceCulture); + return ResourceManager.GetString("InvalidConnectionStringName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Database with name "{0}" does not exist. Use "{1}" extension method to add it.. + /// + internal static string InvalidDatabaseName { + get { + return ResourceManager.GetString("InvalidDatabaseName", resourceCulture); } } } diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index 6c93ecc..eb57381 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -1,4 +1,4 @@ - +