diff --git a/README.md b/README.md
index c7a743d..f1fb335 100644
--- a/README.md
+++ b/README.md
@@ -7,40 +7,38 @@ The service was developed primary for syncing on premise SQL server data to Azur
## Prerequisites
-- .NET 7 SDK - https://dotnet.microsoft.com/en-us/download
-- Azure Functions Core Tools version 4.0.4785, or a later version. - https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#v2
-- Azure CLI version 2.20, or a later version. - https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
+- .NET 10 SDK - https://dotnet.microsoft.com/en-us/download
+- Azure Functions Core Tools version 4.0, or a later version - https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#v2
+- Azure CLI version 2.50, or a later version - https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
- IDE
- - Visual Studio - 17.4.2, or a later version
- - VS Code - 1.73.1, or a later version
+ - Visual Studio 2022 17.10, or a later version
+ - VS Code 1.90, or a later version
## Configuration
The function is configured through Azure App Settings / Environment variables, you can have multiple sync source/targets configures, and multiple tables per sync job.
-| Key | Description | Example |
-|-----------------------------------------------------|-------------------------------------------|------------------------------------------------------------------------------|
-| `ProcessGlobalChangeTrackingSchedule` | Custom schedule cron expression | `0 */5 * * * *` |
-| `SyncJobsConfig:Jobs:[key]:Source:ConnectionString` | Source database connection string | `Server=my.dbserver.net;Initial Catalog=MySourceDb;Integrated Security=True` |
-| `SyncJobsConfig:Jobs:[key]:Source:ManagedIdentity` | Flag for if managed identity used | `false` |
-| `SyncJobsConfig:Jobs:[key]:Source:TenantId` | Azure tenant ID used for managed identity | `46b41530-1e0d-4403-b815-24815944aa6a` |
-| `SyncJobsConfig:Jobs:[key]:Target:ConnectionString` | Target database connection string | `Server=my.dbserver.net;Initial Catalog=MyTargetDb;Integrated Security=True` |
-| `SyncJobsConfig:Jobs:[key]:Target:ManagedIdentity` | Flag for if managed identity used | `true` |
-| `SyncJobsConfig:Jobs:[key]:Target:TenantId` | Azure tenant ID used for managed identity | `46b41530-1e0d-4403-b815-24815944aa6a` |
-| `SyncJobsConfig:Jobs:[key]:BatchSize` | Bulk sync batch size | `1000` |
-| `SyncJobsConfig:Jobs:[key]:Area` | Area name, used to manually trigger sync | `Development` |
-| `SyncJobsConfig:Jobs:[key]:Manual` | Flag is sync excluded from schedules | `true` |
-| `SyncJobsConfig:Jobs:[key]:Schedules:[key]` | Optional opt-in/out schedules | `true` |
-| `SyncJobsConfig:Jobs:[key]:Tables:[key]` | Fully qualified name of table to sync | `dbo.MyTable` |
+| Key | Description | Example |
+|---------------------------------------------------------------------|-------------------------------------------|------------------------------------------------------------------------------|
+| `ProcessGlobalChangeTrackingSchedule` | Custom schedule cron expression | `0 */5 * * * *` |
+| `SyncJobsConfig__Jobs__[key]__Source__ConnectionString` | Source database connection string | `Server=my.dbserver.net;Initial Catalog=MySourceDb;Integrated Security=True` |
+| `SyncJobsConfig__Jobs__[key]__Source__ManagedIdentity` | Flag for if managed identity used | `false` |
+| `SyncJobsConfig__Jobs__[key]__Source__TenantId` | Azure tenant ID used for managed identity | `46b41530-1e0d-4403-b815-24815944aa6a` |
+| `SyncJobsConfig__Jobs__[key]__Target__ConnectionString` | Target database connection string | `Server=my.dbserver.net;Initial Catalog=MyTargetDb;Integrated Security=True` |
+| `SyncJobsConfig__Jobs__[key]__Target__ManagedIdentity` | Flag for if managed identity used | `true` |
+| `SyncJobsConfig__Jobs__[key]__Target__TenantId` | Azure tenant ID used for managed identity | `46b41530-1e0d-4403-b815-24815944aa6a` |
+| `SyncJobsConfig__Jobs__[key]__BatchSize` | Bulk sync batch size | `1000` |
+| `SyncJobsConfig__Jobs__[key]__Area` | Area name, used to manually trigger sync | `Development` |
+| `SyncJobsConfig__Jobs__[key]__Manual` | Flag is sync excluded from schedules | `true` |
+| `SyncJobsConfig__Jobs__[key]__Schedules__[key]` | Optional opt-in/out schedules | `true` |
+| `SyncJobsConfig__Jobs__[key]__Tables__[key]` | Fully qualified name of table to sync | `dbo.MyTable` |
> Note:
>
-> Replace `[key]` with unique name of sync job / table config i.e. `MySync` / `MyTable` would result in `SyncJobsConfig:Jobs:MySync:Tables:MyTable`=`dbo.MyTable`
+> Replace `[key]` with unique name of sync job / table config i.e. `MySync` / `MyTable` would result in `SyncJobsConfig__Jobs__MySync__Tables__MyTable`=`dbo.MyTable`
>
-> Non-Windows operating systems you'll need to replace `:` with `__`, i.e. `SyncJobsConfig__Jobs__MySync__Tables__MyTable`
->
-> Configuration from KeyVault replace `:` with `--` i.e. `SyncJobsConfig--Jobs--MySync--Tables--MyTable`
+> Configuration from KeyVault replace `__` with `--` i.e. `SyncJobsConfig--Jobs--MySync--Tables--MyTable`
## Schedules
@@ -71,24 +69,50 @@ To quicker get started testing the function locally example configuration and da
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"ProcessGlobalChangeTrackingSchedule": "0 23 11 * * *",
- "SyncJobsConfig:Jobs:SyncTest:Area": "SyncTest",
- "SyncJobsConfig:Jobs:SyncTest:Source:ConnectionString": "Server=localhost;Initial Catalog=SyncTest;Integrated Security=True",
- "SyncJobsConfig:Jobs:SyncTest:Source:ManagedIdentity": false,
- "SyncJobsConfig:Jobs:SyncTest:Target:ConnectionString": "Server=localhost;Initial Catalog=SyncTest;Integrated Security=True",
- "SyncJobsConfig:Jobs:SyncTest:Target:ManagedIdentity": false,
- "SyncJobsConfig:Jobs:SyncTest:BatchSize": 1000,
- "SyncJobsConfig:Jobs:SyncTest:Manual": false,
- "SyncJobsConfig:Jobs:SyncTest:Tables:Test": "source.[Test]",
- "SyncJobsConfig:Jobs:SyncTest:TargetTables:Test": "target.[Test]",
- "SyncJobsConfig:Jobs:SyncTest:Schedules:Custom": true,
- "SyncJobsConfig:Jobs:SyncTest:Schedules:Noon": true,
- "SyncJobsConfig:Jobs:SyncTest:Schedules:Midnight": true,
- "SyncJobsConfig:Jobs:SyncTest:Schedules:EveryFiveMinutes": true,
- "SyncJobsConfig:Jobs:SyncTest:Schedules:EveryHour": true
+ "SyncJobsConfig__Jobs__SyncTest__Area": "SyncTest",
+ "SyncJobsConfig__Jobs__SyncTest__Source__ConnectionString": "Server=localhost;Initial Catalog=SyncTest;Integrated Security=True",
+ "SyncJobsConfig__Jobs__SyncTest__Source__ManagedIdentity": false,
+ "SyncJobsConfig__Jobs__SyncTest__Target__ConnectionString": "Server=localhost;Initial Catalog=SyncTest;Integrated Security=True",
+ "SyncJobsConfig__Jobs__SyncTest__Target__ManagedIdentity": false,
+ "SyncJobsConfig__Jobs__SyncTest__BatchSize": 1000,
+ "SyncJobsConfig__Jobs__SyncTest__Manual": false,
+ "SyncJobsConfig__Jobs__SyncTest__Tables__Test": "source.[Test]",
+ "SyncJobsConfig__Jobs__SyncTest__TargetTables__Test": "target.[Test]",
+ "SyncJobsConfig__Jobs__SyncTest__Schedules__Custom": true,
+ "SyncJobsConfig__Jobs__SyncTest__Schedules__Noon": true,
+ "SyncJobsConfig__Jobs__SyncTest__Schedules__Midnight": true,
+ "SyncJobsConfig__Jobs__SyncTest__Schedules__EveryFiveMinutes": true,
+ "SyncJobsConfig__Jobs__SyncTest__Schedules__EveryHour": true
}
}
```
+### Example .env file for Docker
+
+```
+# Environment variables for running SqlBulkSyncFunction in Docker
+
+AzureWebJobsStorage=UseDevelopmentStorage=true
+FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
+ProcessGlobalChangeTrackingSchedule=0 23 11 * * *
+SyncJobsConfig__Jobs__SyncTest__Area=SyncTest
+SyncJobsConfig__Jobs__SyncTest__Source__ConnectionString=Server=localhost;Initial Catalog=SyncTest;Integrated Security=True
+SyncJobsConfig__Jobs__SyncTest__Source__ManagedIdentity=false
+SyncJobsConfig__Jobs__SyncTest__Target__ConnectionString=Server=localhost;Initial Catalog=SyncTest;Integrated Security=True
+SyncJobsConfig__Jobs__SyncTest__Target__ManagedIdentity=false
+SyncJobsConfig__Jobs__SyncTest__BatchSize=1000
+SyncJobsConfig__Jobs__SyncTest__Manual=false
+SyncJobsConfig__Jobs__SyncTest__Tables__Test=source.[Test]
+SyncJobsConfig__Jobs__SyncTest__TargetTables__Test=target.[Test]
+SyncJobsConfig__Jobs__SyncTest__Schedules__Custom=true
+SyncJobsConfig__Jobs__SyncTest__Schedules__Noon=true
+SyncJobsConfig__Jobs__SyncTest__Schedules__Midnight=true
+SyncJobsConfig__Jobs__SyncTest__Schedules__EveryFiveMinutes=true
+SyncJobsConfig__Jobs__SyncTest__Schedules__EveryHour=true
+```
+
+
+
### Example database seed script
```sql
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 37b7367..db073b5 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,46 +1,51 @@
name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)
+
trigger:
- main
-pool:
- vmImage: 'windows-latest'
-
+pool: WCOM
+
variables:
- solution: '**/*.sln'
- buildPlatform: 'Any CPU'
- buildConfiguration: 'Release'
- artifactName: 'SqlBulkSyncFunction'
-
-steps:
-- task: UseDotNet@2.263.0
- displayName: 'Use .NET SDK global.json'
- inputs:
- packageType: sdk
- useGlobalJson: true
-
-- script: |
- dotnet publish -p:Version=$(Build.BuildNumber) --configuration $(BuildConfiguration) -p:GenerateFullPaths=true --output "$(Build.ArtifactStagingDirectory)/Output" src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj
- displayName: .NET Publish $(ArtifactName)
-
-- script: |
- dotnet pack -p:Version=$(Build.BuildNumber) --configuration $(BuildConfiguration) -p:GenerateFullPaths=true --output "$(Build.ArtifactStagingDirectory)/NuGet" src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj
- displayName: .NET Pack $(ArtifactName)
-
-- task: ArchiveFiles@2.265.0
- displayName: Archive Output $(ArtifactName)
- inputs:
- rootFolderOrFile: $(Build.ArtifactStagingDirectory)/Output
- includeRootFolder: false
- archiveFile: $(Build.ArtifactStagingDirectory)/$(ArtifactName).zip
-
-- task: PublishPipelineArtifact@1.242.0
- displayName: Publish Pipeline Artifact $(ArtifactName) Zip
- inputs:
- artifactName: $(ArtifactName)
- path: $(Build.ArtifactStagingDirectory)/$(ArtifactName).zip
-
-- task: PublishPipelineArtifact@1.242.0
- displayName: Publish Pipeline Artifact $(ArtifactName) NuGet
- inputs:
- artifactName: "$(ArtifactName)NuGet"
- path: $(Build.ArtifactStagingDirectory)/NuGet
+ - group: common-build-variables
+
+resources:
+ repositories:
+ - repository: templates
+ type: github
+ endpoint: GitHubPublic
+ name: WCOMAB/WCOM.AzurePipelines.YamlTemplates
+ ref: refs/heads/main
+
+stages:
+- template: dotnetweb/stages.yml@templates
+ parameters:
+ devopsOrg: 'wcom'
+ system: 'sqlbulksync'
+ suffix: 'sqlbulksync'
+ webAppName: 'sqlbulksync'
+ build: Development
+ useDotNetSDK:
+ skipTask: true
+ skipTests: true
+ projectSrc: ./src/SqlBulkSyncFunction
+ buildParameters:
+ - '-c Release'
+ shouldDeploy: eq(variables['Build.SourceBranch'], 'refs/heads/main')
+ dpi:
+ report: eq(variables['Build.SourceBranch'], 'refs/heads/main')
+ WorkspaceId: $(API.DEV.LogId)
+ SharedKey: $(API.DEV.LogKey)
+ environments:
+ - env: dev
+ name: Development
+ deploy: false
+ container:
+ csproj: './src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj'
+ artifact: 'wcom-sqlbulksync-image'
+ repository: 'wcom.sqlbulksync'
+ baseimage: 'mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated10.0'
+ latest: true
+ servers:
+ - name: 'wcompublicacr.azurecr.io'
+ username: 'wcompublicacr'
+ password: '$(wcompublicacr.azurecr.io)'
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..805dfb2
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.editorconfig b/src/.editorconfig
new file mode 100644
index 0000000..a855bb9
--- /dev/null
+++ b/src/.editorconfig
@@ -0,0 +1,429 @@
+# Version: 1.3.2 (Using https://semver.org/)
+# Updated: 2019-08-04
+# See https://github.com/RehanSaeed/EditorConfig/releases for release notes.
+# See https://github.com/RehanSaeed/EditorConfig for updates to this file.
+# See http://EditorConfig.org for more information about .editorconfig files.
+# Modified by marbjo@wcom.se
+
+##########################################
+# Common Settings
+##########################################
+
+# This file is the top-most EditorConfig file
+root = true
+
+# All Files
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+##########################################
+# File Extension Settings
+##########################################
+
+# Visual Studio Solution Files
+[*.{sln,slnx}]
+indent_style = tab
+
+# Visual Studio XML Project Files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,sqlproj}]
+indent_size = 2
+
+# Various XML Configuration Files
+[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON Files
+[*.{json,json5}]
+indent_size = 2
+
+# YAML Files
+[*.{yml,yaml}]
+indent_size = 2
+
+# Markdown Files
+[*.md]
+trim_trailing_whitespace = false
+
+# Web Files
+[*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}]
+indent_size = 2
+
+# Batch Files
+[*.{cmd,bat}]
+end_of_line = crlf
+
+# Bash Files
+[*.sh]
+end_of_line = lf
+
+##########################################
+# .NET Language Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions
+##########################################
+
+# .NET Code Style Settings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings
+[*.{cs,csx,cake,vb}]
+# "this." and "Me." qualifiers
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me
+dotnet_style_qualification_for_field = false:warning
+dotnet_style_qualification_for_property = false:warning
+dotnet_style_qualification_for_method = false:warning
+dotnet_style_qualification_for_event = false:warning
+# Language keywords instead of framework type names for type references
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords
+dotnet_style_predefined_type_for_locals_parameters_members = true:warning
+dotnet_style_predefined_type_for_member_access = true:warning
+# Modifier preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers
+dotnet_style_require_accessibility_modifiers = always:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
+visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async
+dotnet_style_readonly_field = true:warning
+# Parentheses preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
+# Expression-level preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
+dotnet_style_object_initializer = true:warning
+dotnet_style_collection_initializer = true:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_prefer_inferred_tuple_names = true:warning
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion
+dotnet_style_prefer_conditional_expression_over_return = false:suggestion
+dotnet_style_prefer_compound_assignment = true:warning
+# Null-checking preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_null_propagation = true:warning
+# Parameter preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences
+dotnet_code_quality_unused_parameters = all:warning
+# More style options (Undocumented)
+# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641
+dotnet_style_operator_placement_when_wrapping = end_of_line
+
+# C# Code Style Settings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings
+[*.{cs,csx,cake}]
+# Implicit and explicit types
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = true:warning
+# Expression-bodied members
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members
+csharp_style_expression_bodied_methods = true:warning
+csharp_style_expression_bodied_constructors = true:warning
+csharp_style_expression_bodied_operators = true:warning
+csharp_style_expression_bodied_properties = true:warning
+csharp_style_expression_bodied_indexers = true:warning
+csharp_style_expression_bodied_accessors = true:warning
+csharp_style_expression_bodied_lambdas = true:warning
+csharp_style_expression_bodied_local_functions = true:warning
+# Pattern matching
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+# Inlined variable declarations
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations
+csharp_style_inlined_variable_declaration = true:warning
+# Expression-level preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
+csharp_prefer_simple_default_expression = true:warning
+# "Null" checking preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences
+csharp_style_throw_expression = true:warning
+csharp_style_conditional_delegate_call = true:warning
+# Code block preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences
+csharp_prefer_braces = true:warning
+# Unused value preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences
+csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+# Index and range preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences
+csharp_style_prefer_index_operator = true:warning
+csharp_style_prefer_range_operator = true:warning
+# Miscellaneous preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences
+csharp_style_deconstructed_variable_declaration = true:warning
+csharp_style_pattern_local_over_anonymous_function = true:warning
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_static_local_function = true:warning
+csharp_prefer_simple_using_statement = true:warning
+
+##########################################
+# .NET Formatting Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
+##########################################
+
+# Organize usings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives
+dotnet_sort_system_directives_first = true
+# Newline options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+# Indentation options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = no_change
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents_when_block = false
+# Spacing options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_parentheses = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_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_call_parameter_list_parentheses = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_after_comma = true
+csharp_space_before_comma = false
+csharp_space_after_dot = false
+csharp_space_before_dot = false
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_around_declaration_statements = false
+csharp_space_before_open_square_brackets = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_square_brackets = false
+# Wrapping options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options
+csharp_preserve_single_line_statements = false
+csharp_preserve_single_line_blocks = true
+
+##########################################
+# .NET Naming Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
+##########################################
+
+[*.{cs,csx,cake,vb}]
+
+##########################################
+# Styles
+##########################################
+
+# camel_case_style - Define the camelCase style
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+# pascal_case_style - Define the PascalCase style
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+# first_upper_style - The first character must start with an upper-case character
+dotnet_naming_style.first_upper_style.capitalization = first_word_upper
+# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I'
+dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case
+dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I
+# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T'
+dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case
+dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T
+# disallowed_style - Anything that has this style applied is marked as disallowed
+dotnet_naming_style.disallowed_style.capitalization = pascal_case
+dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____
+dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____
+# internal_error_style - This style should never occur... if it does, it's indicates a bug in file or in the parser using the file
+dotnet_naming_style.internal_error_style.capitalization = pascal_case
+dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____
+dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____
+
+##########################################
+# .NET Design Guideline Field Naming Rules
+# Naming rules for fields follow the .NET Framework design guidelines
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/index
+##########################################
+
+# All public/protected/protected_internal constant fields must be PascalCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
+dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning
+
+# All public/protected/protected_internal static readonly fields must be PascalCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning
+
+# No other public/protected/protected_internal fields are allowed
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
+
+##########################################
+# StyleCop Field Naming Rules
+# Naming rules for fields follow the StyleCop analyzers
+# This does not override any rules using disallowed_style above
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
+##########################################
+
+# All constant fields must be PascalCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
+dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
+dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
+dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
+
+# All static readonly fields must be PascalCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
+
+# No non-private instance fields are allowed
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
+dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
+dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
+
+# Private fields must have underscore
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
+dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
+dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
+dotnet_naming_rule.private_members_with_underscore.severity = suggestion
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private
+dotnet_naming_style.prefix_underscore.capitalization = camel_case
+dotnet_naming_style.prefix_underscore.required_prefix = _
+
+# Local variables must be camelCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
+dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
+dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent
+
+# This rule should never fire. However, it's included for at least two purposes:
+# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
+# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
+dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = *
+dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
+
+
+##########################################
+# Other Naming Rules
+##########################################
+
+# All of the following must be PascalCase:
+# - Namespaces
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
+# - Classes and Enumerations
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
+# - Delegates
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types
+# - Constructors, Properties, Events, Methods
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
+dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property
+dotnet_naming_rule.element_rule.symbols = element_group
+dotnet_naming_rule.element_rule.style = pascal_case_style
+dotnet_naming_rule.element_rule.severity = warning
+
+# Interfaces use PascalCase and are prefixed with uppercase 'I'
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+dotnet_naming_symbols.interface_group.applicable_kinds = interface
+dotnet_naming_rule.interface_rule.symbols = interface_group
+dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style
+dotnet_naming_rule.interface_rule.severity = warning
+
+# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter
+dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group
+dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style
+dotnet_naming_rule.type_parameter_rule.severity = warning
+
+# Function parameters use camelCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
+dotnet_naming_symbols.parameters_group.applicable_kinds = parameter
+dotnet_naming_rule.parameters_rule.symbols = parameters_group
+dotnet_naming_rule.parameters_rule.style = camel_case_style
+dotnet_naming_rule.parameters_rule.severity = warning
+
+##########################################
+# License
+##########################################
+# The following applies as to the .editorconfig file ONLY, and is
+# included below for reference, per the requirements of the license
+# corresponding to this .editorconfig file.
+# See: https://github.com/RehanSaeed/EditorConfig
+#
+# MIT License
+#
+# Copyright (c) 2017-2019 Muhammad Rehan Saeed
+# Copyright (c) 2019 Henry Gabryjelski
+#
+# Permission is hereby granted, free of charge, to any
+# person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the
+# Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute,
+# sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject
+# to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+##########################################
+
+# CA1040: Avoid empty interfaces
+dotnet_diagnostic.CA1040.severity = warning
+
+# CA1014: Mark assemblies with CLSCompliant
+dotnet_diagnostic.CA1014.severity = none
+
+# IDE0005: Using directive is unnecessary
+dotnet_diagnostic.IDE0005.severity = warning
\ No newline at end of file
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
new file mode 100644
index 0000000..232f613
--- /dev/null
+++ b/src/Directory.Packages.props
@@ -0,0 +1,17 @@
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SqlBulkSyncFunction.sln b/src/SqlBulkSyncFunction.sln
deleted file mode 100644
index 035762f..0000000
--- a/src/SqlBulkSyncFunction.sln
+++ /dev/null
@@ -1,34 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.6.30114.105
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlBulkSyncFunction", "SqlBulkSyncFunction\SqlBulkSyncFunction.csproj", "{39549960-540B-4118-82A7-1E8DEE9D6362}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|x64.ActiveCfg = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|x64.Build.0 = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|x86.ActiveCfg = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Debug|x86.Build.0 = Debug|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|Any CPU.Build.0 = Release|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|x64.ActiveCfg = Release|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|x64.Build.0 = Release|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|x86.ActiveCfg = Release|Any CPU
- {39549960-540B-4118-82A7-1E8DEE9D6362}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/src/SqlBulkSyncFunction.slnx b/src/SqlBulkSyncFunction.slnx
new file mode 100644
index 0000000..6502b97
--- /dev/null
+++ b/src/SqlBulkSyncFunction.slnx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/SqlBulkSyncFunction/Constants.cs b/src/SqlBulkSyncFunction/Constants.cs
new file mode 100644
index 0000000..b47a4d5
--- /dev/null
+++ b/src/SqlBulkSyncFunction/Constants.cs
@@ -0,0 +1,12 @@
+namespace SqlBulkSyncFunction;
+
+///
+/// Constants used throughout the application.
+///
+public static class Constants
+{
+ ///
+ /// Queue name for processing global change tracking jobs.
+ ///
+ public const string ProcessGlobalChangeTrackingQueue = "processglobalchangetrackingqueue";
+}
diff --git a/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingQueue.cs b/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingQueue.cs
index 8e259ac..486dd64 100644
--- a/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingQueue.cs
+++ b/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingQueue.cs
@@ -15,10 +15,10 @@ IProcessSyncJobService ProcessSyncJobService
{
[Function(nameof(ProcessGlobalChangeTrackingQueue))]
-
- public async Task Run([QueueTrigger(nameof(ProcessGlobalChangeTrackingQueue))] SyncJob syncJob)
+
+ public async Task Run([QueueTrigger(SqlBulkSyncFunction.Constants.ProcessGlobalChangeTrackingQueue)] SyncJob syncJob)
{
- if(syncJob == null)
+ if (syncJob == null)
{
return;
}
diff --git a/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingSchedule.cs b/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingSchedule.cs
index 043b1d6..9802094 100644
--- a/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingSchedule.cs
+++ b/src/SqlBulkSyncFunction/Functions/ProcessGlobalChangeTrackingSchedule.cs
@@ -5,11 +5,10 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using SqlBulkSyncFunction.Helpers;
using SqlBulkSyncFunction.Models;
using SqlBulkSyncFunction.Models.Job;
using SqlBulkSyncFunction.Services;
-using SqlBulkSyncFunction.Helpers;
-using System.Collections.Generic;
// ReSharper disable UnusedMember.Global
namespace SqlBulkSyncFunction.Functions
diff --git a/src/SqlBulkSyncFunction/Functions/QueueGlobalChangeTracking.cs b/src/SqlBulkSyncFunction/Functions/QueueGlobalChangeTracking.cs
index 35ea960..6d7fd76 100644
--- a/src/SqlBulkSyncFunction/Functions/QueueGlobalChangeTracking.cs
+++ b/src/SqlBulkSyncFunction/Functions/QueueGlobalChangeTracking.cs
@@ -1,6 +1,5 @@
using System;
using System.Net;
-using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
@@ -11,7 +10,7 @@
using SqlBulkSyncFunction.Models.Job;
using SqlBulkSyncFunction.Services;
-namespace SqlBulkSyncFunction.Functions
+namespace SqlBulkSyncFunction.Functions
{
public record QueueGlobalChangeTracking(
ILogger Logger,
@@ -28,10 +27,7 @@ public async Task Queue(
)] HttpRequestData req,
string area,
string id
- )
- {
- return await GetQueueGlobalChangeTrackingResult(req, area, id);
- }
+ ) => await GetQueueGlobalChangeTrackingResult(req, area, id);
[Function(nameof(QueueGlobalChangeTracking) + nameof(Seed))]
public async Task Seed(
@@ -43,10 +39,7 @@ public async Task Seed(
string area,
string id,
bool seed
- )
- {
- return await GetQueueGlobalChangeTrackingResult(req, area, id, seed);
- }
+ ) => await GetQueueGlobalChangeTrackingResult(req, area, id, seed);
private async Task GetQueueGlobalChangeTrackingResult(
HttpRequestData req,
@@ -72,7 +65,7 @@ private async Task GetQueueGlobalChangeTracking
expires: DateTimeOffset.UtcNow.AddMinutes(4),
id: id,
schedule: nameof(jobConfig.Manual),
- seed:seed
+ seed: seed
),
req.CreateResponse(HttpStatusCode.Accepted)
);
diff --git a/src/SqlBulkSyncFunction/Helpers/SchemaExtensions.cs b/src/SqlBulkSyncFunction/Helpers/SchemaExtensions.cs
index 6ada8ac..f361c76 100644
--- a/src/SqlBulkSyncFunction/Helpers/SchemaExtensions.cs
+++ b/src/SqlBulkSyncFunction/Helpers/SchemaExtensions.cs
@@ -74,7 +74,8 @@ string tableName
{
var tableVersion = conn.Query(
commandTimeout: 180,
- param: new {
+ param: new
+ {
TableName = tableName
},
sql: @"SELECT TableName,
@@ -86,11 +87,11 @@ FROM sync.TableVersion
)
.SingleOrDefault()
?? new TableVersion
- {
- TableName = tableName,
- CurrentVersion = -1,
- MinValidVersion = -1
- };
+ {
+ TableName = tableName,
+ CurrentVersion = -1,
+ MinValidVersion = -1
+ };
return tableVersion;
}
@@ -116,9 +117,7 @@ Column[] columns
return result;
}
- public static Column[] GetColumns(this IDbConnection sourceConn, string tableName)
- {
- return sourceConn
+ public static Column[] GetColumns(this IDbConnection sourceConn, string tableName) => sourceConn
.Query(
commandTimeout: 5000,
param: new { TableName = tableName },
@@ -175,6 +174,5 @@ END AS IsPrimary
tp.name <> 'timestamp'"
)
.ToArray();
- }
}
}
diff --git a/src/SqlBulkSyncFunction/Helpers/SqlCommandExtensions.cs b/src/SqlBulkSyncFunction/Helpers/SqlCommandExtensions.cs
index 63f1612..6a86b47 100644
--- a/src/SqlBulkSyncFunction/Helpers/SqlCommandExtensions.cs
+++ b/src/SqlBulkSyncFunction/Helpers/SqlCommandExtensions.cs
@@ -15,9 +15,7 @@ public static void DropSyncTables(
TableSchema tableSchema,
object scope,
ILogger logger
- )
- {
- Array.ForEach(
+ ) => Array.ForEach(
new[]
{
new
@@ -34,7 +32,10 @@ ILogger logger
table =>
{
if (string.IsNullOrEmpty(tableSchema?.SyncNewOrUpdatedTableName))
+ {
return;
+ }
+
try
{
targetConn.Execute(
@@ -56,7 +57,6 @@ ILogger logger
}
}
);
- }
public static void MergeData(
this SqlConnection targetConn,
@@ -92,9 +92,7 @@ public static void BulkCopyData(
TableSchema tableSchema,
object scope,
ILogger logger
- )
- {
- Array.ForEach(
+ ) => Array.ForEach(
new[]
{
new
@@ -128,7 +126,7 @@ ILogger logger
BatchSize = tableSchema.BatchSize,
NotifyAfter = tableSchema.BatchSize,
BulkCopyTimeout = 300,
- EnableStreaming= true
+ EnableStreaming = true
};
bcp.SqlRowsCopied += (s, e) => logger.LogInformation("{Scope} {TableName} {RowsCopied} {Description} rows copied", scope, table.Name, e.RowsCopied, table.Description);
@@ -136,28 +134,22 @@ ILogger logger
logger.LogInformation("{Scope} Bulk copy complete for {Description}.", scope, table.Description);
}
);
- }
public static bool SyncTablesExist(
this SqlConnection targetConn,
TableSchema tableSchema
- )
- {
- return targetConn.Query(
+ ) => targetConn.Query(
commandType: CommandType.Text,
commandTimeout: 500,
sql: tableSchema.SyncTableExistStatement
).First();
- }
public static void CreateSyncTables(
this SqlConnection targetConn,
TableSchema tableSchema,
object scope,
ILogger logger
- )
- {
- Array.ForEach(
+ ) => Array.ForEach(
new[]
{
new
@@ -181,7 +173,6 @@ ILogger logger
logger.LogInformation("{Scope} Sync table {Name} created.", scope, table.Name);
}
);
- }
public static void EnsureSyncSchemaAndTableExists(
this SqlConnection targetConn,
diff --git a/src/SqlBulkSyncFunction/Helpers/SqlStatementExtensions.cs b/src/SqlBulkSyncFunction/Helpers/SqlStatementExtensions.cs
index a494411..79b35f7 100644
--- a/src/SqlBulkSyncFunction/Helpers/SqlStatementExtensions.cs
+++ b/src/SqlBulkSyncFunction/Helpers/SqlStatementExtensions.cs
@@ -45,16 +45,13 @@ FROM CHANGETABLE(VERSION {0}, ({1}), ({1})) as t",
);
}
- public static string GetDropStatement(this string tableName)
- {
- return string.Format(
+ public static string GetDropStatement(this string tableName) => string.Format(
@"IF OBJECT_ID('{0}') IS NOT NULL
BEGIN
DROP TABLE {0}
END",
tableName
);
- }
public static string GetNewOrUpdatedMergeStatement(this TableSchema tableSchema)
{
diff --git a/src/SqlBulkSyncFunction/Helpers/SyncJobConfigExtensions.cs b/src/SqlBulkSyncFunction/Helpers/SyncJobConfigExtensions.cs
index fe2132a..e47dc3a 100644
--- a/src/SqlBulkSyncFunction/Helpers/SyncJobConfigExtensions.cs
+++ b/src/SqlBulkSyncFunction/Helpers/SyncJobConfigExtensions.cs
@@ -14,7 +14,7 @@ public static SyncJob ToSyncJob(
ConcurrentDictionary tokenCache,
DateTimeOffset expires,
bool seed
- ) => new (
+ ) => new(
id,
schedule,
job.Area,
@@ -41,18 +41,15 @@ private static SyncJobTable[] ToSyncJobTables(this SyncJobConfig job)
sourceTable.Value,
targetTableLookup?[sourceTable.Key].FirstOrDefault() switch
{
- { Length:>0 } overrideTargetTable => overrideTargetTable,
- _=> sourceTable.Value
+ { Length: > 0 } overrideTargetTable => overrideTargetTable,
+ _ => sourceTable.Value
}
)
).ToArray();
}
- private static string TryGetToken(SyncJobConfigDataSource dataSource, ConcurrentDictionary tokenCache)
- {
- return dataSource.ManagedIdentity && tokenCache.TryGetValue(dataSource.TenantId ?? string.Empty, out var sourceToken)
+ private static string TryGetToken(SyncJobConfigDataSource dataSource, ConcurrentDictionary tokenCache) => dataSource.ManagedIdentity && tokenCache.TryGetValue(dataSource.TenantId ?? string.Empty, out var sourceToken)
? sourceToken
: null;
- }
}
}
diff --git a/src/SqlBulkSyncFunction/Models/Job/SyncJobConfig.cs b/src/SqlBulkSyncFunction/Models/Job/SyncJobConfig.cs
index ebab20d..16e6c46 100644
--- a/src/SqlBulkSyncFunction/Models/Job/SyncJobConfig.cs
+++ b/src/SqlBulkSyncFunction/Models/Job/SyncJobConfig.cs
@@ -13,4 +13,4 @@ public record SyncJobConfig
public bool? Manual { get; init; }
public Dictionary Schedules { get; init; }
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/Job/SyncJobConfigDataSource.cs b/src/SqlBulkSyncFunction/Models/Job/SyncJobConfigDataSource.cs
index 55cfbb7..81bfbe8 100644
--- a/src/SqlBulkSyncFunction/Models/Job/SyncJobConfigDataSource.cs
+++ b/src/SqlBulkSyncFunction/Models/Job/SyncJobConfigDataSource.cs
@@ -6,4 +6,4 @@ public record SyncJobConfigDataSource
public bool ManagedIdentity { get; set; }
public string TenantId { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/Job/SyncJobTable.cs b/src/SqlBulkSyncFunction/Models/Job/SyncJobTable.cs
index 327b30e..90c8d51 100644
--- a/src/SqlBulkSyncFunction/Models/Job/SyncJobTable.cs
+++ b/src/SqlBulkSyncFunction/Models/Job/SyncJobTable.cs
@@ -1,4 +1,4 @@
namespace SqlBulkSyncFunction.Models.Job
{
public record SyncJobTable(string Source, string Target);
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/Job/SyncJobsConfig.cs b/src/SqlBulkSyncFunction/Models/Job/SyncJobsConfig.cs
index 6f6b9c8..81a2a36 100644
--- a/src/SqlBulkSyncFunction/Models/Job/SyncJobsConfig.cs
+++ b/src/SqlBulkSyncFunction/Models/Job/SyncJobsConfig.cs
@@ -6,9 +6,9 @@ namespace SqlBulkSyncFunction.Models.Job
{
public record SyncJobsConfig
{
- private static readonly string[] DefaultJobSchedules = {"Custom"};
+ private static readonly string[] DefaultJobSchedules = { "Custom" };
- public Dictionary Jobs { get; init; }
+ public Dictionary Jobs { get; init; }
public Lazy> ScheduledJobs { get; }
private static Dictionary Empty { get; } = new Dictionary(0);
@@ -33,12 +33,9 @@ private static IEnumerable GetJobSchedules(KeyValuePairvalue.Value)
- .Select(key=>key.Key);
+ .Where(value => value.Value)
+ .Select(key => key.Key);
- public SyncJobsConfig()
- {
- ScheduledJobs = new Lazy>(GetScheduledJobs);
- }
+ public SyncJobsConfig() => ScheduledJobs = new Lazy>(GetScheduledJobs);
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/ProcessGlobalChangeTrackingResult.cs b/src/SqlBulkSyncFunction/Models/ProcessGlobalChangeTrackingResult.cs
index 9820b1c..591c442 100644
--- a/src/SqlBulkSyncFunction/Models/ProcessGlobalChangeTrackingResult.cs
+++ b/src/SqlBulkSyncFunction/Models/ProcessGlobalChangeTrackingResult.cs
@@ -1,13 +1,12 @@
using Microsoft.Azure.Functions.Worker;
-using SqlBulkSyncFunction.Functions;
using SqlBulkSyncFunction.Models.Job;
namespace SqlBulkSyncFunction.Models
{
public record ProcessGlobalChangeTrackingResult(
- [property:QueueOutput(nameof(ProcessGlobalChangeTrackingQueue))]params SyncJob[] SyncJobs
+ [property: QueueOutput(SqlBulkSyncFunction.Constants.ProcessGlobalChangeTrackingQueue)] params SyncJob[] SyncJobs
)
{
public static ProcessGlobalChangeTrackingResult Empty { get; } = new();
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/QueueGlobalChangeTrackingResult.cs b/src/SqlBulkSyncFunction/Models/QueueGlobalChangeTrackingResult.cs
index cc91890..5afb913 100644
--- a/src/SqlBulkSyncFunction/Models/QueueGlobalChangeTrackingResult.cs
+++ b/src/SqlBulkSyncFunction/Models/QueueGlobalChangeTrackingResult.cs
@@ -1,13 +1,12 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
-using SqlBulkSyncFunction.Functions;
using SqlBulkSyncFunction.Models.Job;
namespace SqlBulkSyncFunction.Models
{
public record QueueGlobalChangeTrackingResult(
- [property:QueueOutput(nameof(ProcessGlobalChangeTrackingQueue))]
+ [property:QueueOutput(SqlBulkSyncFunction.Constants.ProcessGlobalChangeTrackingQueue)]
SyncJob SyncJob,
HttpResponseData HttpResponseData
);
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Models/Schema/Column.cs b/src/SqlBulkSyncFunction/Models/Schema/Column.cs
index 4f481ae..b8781d5 100644
--- a/src/SqlBulkSyncFunction/Models/Schema/Column.cs
+++ b/src/SqlBulkSyncFunction/Models/Schema/Column.cs
@@ -1,6 +1,7 @@
namespace SqlBulkSyncFunction.Models.Schema
{
- public record Column {
+ public record Column
+ {
public string Name { get; set; }
public string QuoteName { get; set; }
public string Type { get; set; }
diff --git a/src/SqlBulkSyncFunction/Models/Schema/TableVersion.cs b/src/SqlBulkSyncFunction/Models/Schema/TableVersion.cs
index dc5fa6c..46b5c88 100644
--- a/src/SqlBulkSyncFunction/Models/Schema/TableVersion.cs
+++ b/src/SqlBulkSyncFunction/Models/Schema/TableVersion.cs
@@ -9,4 +9,4 @@ public record TableVersion
public long MinValidVersion { get; set; }
public DateTimeOffset Queried { get; set; }
};
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Program.cs b/src/SqlBulkSyncFunction/Program.cs
index 064f2b9..a526d68 100644
--- a/src/SqlBulkSyncFunction/Program.cs
+++ b/src/SqlBulkSyncFunction/Program.cs
@@ -1,13 +1,14 @@
using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using SqlBulkSyncFunction.Models.Job;
using SqlBulkSyncFunction.Services;
await new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(
- configure => {
+ configure =>
+ {
configure.AddOptions()
.Configure(
(settings, configuration) => configuration.GetSection(nameof(SyncJobsConfig)).Bind(settings));
@@ -19,4 +20,4 @@
.AddSingleton();
})
.Build()
- .RunAsync();
\ No newline at end of file
+ .RunAsync();
diff --git a/src/SqlBulkSyncFunction/Services/AzureSqlTokenService.cs b/src/SqlBulkSyncFunction/Services/AzureSqlTokenService.cs
index 0bdd46e..bef8372 100644
--- a/src/SqlBulkSyncFunction/Services/AzureSqlTokenService.cs
+++ b/src/SqlBulkSyncFunction/Services/AzureSqlTokenService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
diff --git a/src/SqlBulkSyncFunction/Services/IAzureSqlTokenService.cs b/src/SqlBulkSyncFunction/Services/IAzureSqlTokenService.cs
index 0a0dd0d..f1b7995 100644
--- a/src/SqlBulkSyncFunction/Services/IAzureSqlTokenService.cs
+++ b/src/SqlBulkSyncFunction/Services/IAzureSqlTokenService.cs
@@ -1,9 +1,9 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
namespace SqlBulkSyncFunction.Services
{
public interface IAzureSqlTokenService
{
- Task GetAccessToken(string tenantId);
+ public Task GetAccessToken(string tenantId);
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Services/IProcessSyncJobService.cs b/src/SqlBulkSyncFunction/Services/IProcessSyncJobService.cs
index 9fac908..24c0d58 100644
--- a/src/SqlBulkSyncFunction/Services/IProcessSyncJobService.cs
+++ b/src/SqlBulkSyncFunction/Services/IProcessSyncJobService.cs
@@ -1,10 +1,10 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using SqlBulkSyncFunction.Models.Job;
namespace SqlBulkSyncFunction.Services
{
public interface IProcessSyncJobService
{
- Task ProcessSyncJob(SyncJob syncJob, bool globalChangeTracking);
+ public Task ProcessSyncJob(SyncJob syncJob, bool globalChangeTracking);
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Services/ITokenCacheService.cs b/src/SqlBulkSyncFunction/Services/ITokenCacheService.cs
index 883ec19..a595931 100644
--- a/src/SqlBulkSyncFunction/Services/ITokenCacheService.cs
+++ b/src/SqlBulkSyncFunction/Services/ITokenCacheService.cs
@@ -1,4 +1,4 @@
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using SqlBulkSyncFunction.Models.Job;
@@ -7,7 +7,7 @@ namespace SqlBulkSyncFunction.Services
{
public interface ITokenCacheService
{
- Task> GetTokenCache(IEnumerable jobs);
- Task> GetTokenCache(SyncJobConfig job);
+ public Task> GetTokenCache(IEnumerable jobs);
+ public Task> GetTokenCache(SyncJobConfig job);
}
-}
\ No newline at end of file
+}
diff --git a/src/SqlBulkSyncFunction/Services/ProcessSyncJobService.cs b/src/SqlBulkSyncFunction/Services/ProcessSyncJobService.cs
index 6a3bff7..5877e69 100644
--- a/src/SqlBulkSyncFunction/Services/ProcessSyncJobService.cs
+++ b/src/SqlBulkSyncFunction/Services/ProcessSyncJobService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -20,8 +20,8 @@ public async Task ProcessSyncJob(SyncJob syncJob, bool globalChangeTracking)
using (Logger.BeginScope(scope))
{
await using SqlConnection
- sourceConn = new(syncJob.SourceDbConnection) {AccessToken = syncJob.SourceDbAccessToken},
- targetConn = new(syncJob.TargetDbConnection) {AccessToken = syncJob.TargetDbAccessToken};
+ sourceConn = new(syncJob.SourceDbConnection) { AccessToken = syncJob.SourceDbAccessToken },
+ targetConn = new(syncJob.TargetDbConnection) { AccessToken = syncJob.TargetDbAccessToken };
using IDisposable from = Logger.BeginScope($"{sourceConn.DataSource}.{sourceConn.Database}"),
to = Logger.BeginScope($"{targetConn.DataSource}.{targetConn.Database}");
@@ -76,7 +76,7 @@ public async Task ProcessSyncJob(SyncJob syncJob, bool globalChangeTracking)
using (Logger.BeginScope(tableSchema.Scope))
{
Logger.LogInformation("{Scope} Begin {TableSchemaScope}", scope, tableSchema.Scope);
-
+
if (syncJob.Seed)
{
SeedTable(targetConn, tableSchema, sourceConn, scope);
diff --git a/src/SqlBulkSyncFunction/Services/TokenCacheService.cs b/src/SqlBulkSyncFunction/Services/TokenCacheService.cs
index a51ac70..2a781cb 100644
--- a/src/SqlBulkSyncFunction/Services/TokenCacheService.cs
+++ b/src/SqlBulkSyncFunction/Services/TokenCacheService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -42,14 +42,10 @@ await dataSources
.Distinct(StringComparer.OrdinalIgnoreCase)
.ParallelForEachAsync(
maxDegreeOfParallelism: 4,
- asyncItemAction: async tenant =>
- {
- tokenCache.TryAdd(
+ asyncItemAction: async tenant => tokenCache.TryAdd(
tenant,
await AzureSqlTokenService.GetAccessToken(tenant)
- );
- }
- );
+ ));
return tokenCache;
}
diff --git a/src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj b/src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj
index 437e340..7f07ff5 100644
--- a/src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj
+++ b/src/SqlBulkSyncFunction/SqlBulkSyncFunction.csproj
@@ -1,21 +1,21 @@
- net9.0
+ net10.0
latest
v4
Exe
<_FunctionsSkipCleanOutput>true
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -29,6 +29,7 @@
true
false
+ false
contentFiles
Apache-2.0
WCOM128x128_squares.png
@@ -43,11 +44,7 @@
A lightweight, performant non-intrusive SQL Server data sync service.
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
+