diff --git a/.editorconfig b/.editorconfig index 326085a..e25eb42 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,270 +7,270 @@ root = true #### Core EditorConfig Options #### # Indentation and spacing -indent_size = 4 -indent_style = space -tab_width = 4 +indent_size = 4 +indent_style = space +tab_width = 4 # New line preferences -end_of_line = crlf -insert_final_newline = false +end_of_line = crlf +insert_final_newline = false #### .NET Coding Conventions #### # Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true -file_header_template = unset +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset # this. and Me. preferences -dotnet_style_qualification_for_event = false:error -dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false:error -dotnet_style_qualification_for_property = false:error +dotnet_style_qualification_for_event = false:error +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false:error +dotnet_style_qualification_for_property = false:error # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_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 -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_arithmetic_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 +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_require_accessibility_modifiers = for_non_interface_members # Expression-level preferences -dotnet_style_coalesce_expression = true:warning -dotnet_style_collection_initializer = true:error -dotnet_style_explicit_tuple_names = true:error -dotnet_style_namespace_match_folder = true -dotnet_style_null_propagation = true:warning -dotnet_style_object_initializer = true:error -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_prefer_auto_properties = true:warning -dotnet_style_prefer_collection_expression = when_types_loosely_match -dotnet_style_prefer_compound_assignment = true:error -dotnet_style_prefer_conditional_expression_over_assignment = true:error -dotnet_style_prefer_conditional_expression_over_return = true:error -dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed -dotnet_style_prefer_inferred_anonymous_type_member_names = true:error -dotnet_style_prefer_inferred_tuple_names = true:error -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -dotnet_style_prefer_simplified_boolean_expressions = true:error -dotnet_style_prefer_simplified_interpolation = true +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true:error +dotnet_style_explicit_tuple_names = true:error +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true:warning +dotnet_style_object_initializer = true:error +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true:error +dotnet_style_prefer_conditional_expression_over_assignment = true:error +dotnet_style_prefer_conditional_expression_over_return = true:error +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true:error +dotnet_style_prefer_inferred_tuple_names = true:error +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:error +dotnet_style_prefer_simplified_interpolation = true # Field preferences -dotnet_style_readonly_field = true:warning +dotnet_style_readonly_field = true:warning # Parameter preferences -dotnet_code_quality_unused_parameters = all:error +dotnet_code_quality_unused_parameters = all:error # Suppression preferences -dotnet_remove_unnecessary_suppression_exclusions = none +dotnet_remove_unnecessary_suppression_exclusions = none # New line preferences -dotnet_style_allow_multiple_blank_lines_experimental = false:error -dotnet_style_allow_statement_immediately_after_block_experimental = false:warning +dotnet_style_allow_multiple_blank_lines_experimental = false:error +dotnet_style_allow_statement_immediately_after_block_experimental = false:warning #### C# Coding Conventions #### # var preferences -csharp_style_var_elsewhere = true:silent -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent # Expression-bodied members -csharp_style_expression_bodied_accessors = true:warning -csharp_style_expression_bodied_constructors = true:warning -csharp_style_expression_bodied_indexers = true:warning -csharp_style_expression_bodied_lambdas = true:warning -csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = true:warning -csharp_style_expression_bodied_operators = true:warning -csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_accessors = true:warning +csharp_style_expression_bodied_constructors = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = true:warning +csharp_style_expression_bodied_operators = true:warning +csharp_style_expression_bodied_properties = true:warning # Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true:error -csharp_style_pattern_matching_over_is_with_cast_check = true:error -csharp_style_prefer_extended_property_pattern = true:suggestion -csharp_style_prefer_not_pattern = true:error -csharp_style_prefer_pattern_matching = true:error -csharp_style_prefer_switch_expression = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:error +csharp_style_prefer_pattern_matching = true:error +csharp_style_prefer_switch_expression = true:error # Null-checking preferences -csharp_style_conditional_delegate_call = true:warning +csharp_style_conditional_delegate_call = true:warning # Modifier preferences -csharp_prefer_static_local_function = true:warning -csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async -csharp_style_prefer_readonly_struct = true:warning -csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async +csharp_style_prefer_readonly_struct = true:warning +csharp_style_prefer_readonly_struct_member = true:suggestion # Code-block preferences -csharp_prefer_braces = true:error -csharp_prefer_simple_using_statement = true:warning -csharp_style_namespace_declarations = file_scoped:warning -csharp_style_prefer_method_group_conversion = true:warning -csharp_style_prefer_primary_constructors = true:suggestion -csharp_style_prefer_top_level_statements = false:silent +csharp_prefer_braces = true:error +csharp_prefer_simple_using_statement = true:warning +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_prefer_method_group_conversion = true:warning +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = false:silent # Expression-level preferences -csharp_prefer_simple_default_expression = true:error -csharp_style_deconstructed_variable_declaration = true:warning -csharp_style_implicit_object_creation_when_type_is_apparent = true:error -csharp_style_inlined_variable_declaration = true:warning -csharp_style_prefer_index_operator = true:error -csharp_style_prefer_local_over_anonymous_function = true:error -csharp_style_prefer_null_check_over_type_check = true:warning -csharp_style_prefer_range_operator = true:error -csharp_style_prefer_tuple_swap = true:error -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_throw_expression = true:warning -csharp_style_unused_value_assignment_preference = discard_variable:warning -csharp_style_unused_value_expression_statement_preference = discard_variable:warning +csharp_prefer_simple_default_expression = true:error +csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_implicit_object_creation_when_type_is_apparent = true:error +csharp_style_inlined_variable_declaration = true:warning +csharp_style_prefer_index_operator = true:error +csharp_style_prefer_local_over_anonymous_function = true:error +csharp_style_prefer_null_check_over_type_check = true:warning +csharp_style_prefer_range_operator = true:error +csharp_style_prefer_tuple_swap = true:error +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_throw_expression = true:warning +csharp_style_unused_value_assignment_preference = discard_variable:warning +csharp_style_unused_value_expression_statement_preference = discard_variable:warning # 'using' directive preferences -csharp_using_directive_placement = outside_namespace:warning +csharp_using_directive_placement = outside_namespace:warning # New line preferences csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:warning csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false:warning -csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false:warning -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error -csharp_style_allow_embedded_statements_on_same_line_experimental = true:warning +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false:warning +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error +csharp_style_allow_embedded_statements_on_same_line_experimental = true: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 +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 = one_less_than_current -csharp_indent_switch_labels = true +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +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 = false -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = ignore -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 +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 = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = ignore +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 = false +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false #### Naming styles #### # Naming rules -dotnet_naming_rule.interface_should_be_begins_with_i.severity = error -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +dotnet_naming_rule.interface_should_be_begins_with_i.severity = error +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i -dotnet_naming_rule.types_should_be_pascal_case.severity = error -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.types_should_be_pascal_case.severity = error +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case -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 +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 -dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning -dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field -dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning +dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field +dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case # Symbol specifications -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = -dotnet_naming_symbols.static_field.applicable_kinds = field -dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.static_field.required_modifiers = static +dotnet_naming_symbols.static_field.applicable_kinds = field +dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.static_field.required_modifiers = static -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case [*.{cs,vb}] -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf -dotnet_style_coalesce_expression = true:warning -dotnet_style_null_propagation = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -dotnet_style_prefer_auto_properties = true:warning -dotnet_style_object_initializer = true:error -dotnet_style_collection_initializer = true:error -dotnet_style_prefer_simplified_boolean_expressions = true:error -dotnet_style_prefer_conditional_expression_over_assignment = true:error -dotnet_style_explicit_tuple_names = true:error -dotnet_style_prefer_conditional_expression_over_return = true:error -dotnet_style_prefer_inferred_tuple_names = true:error -dotnet_style_prefer_inferred_anonymous_type_member_names = true:error -dotnet_style_prefer_compound_assignment = true:error -dotnet_style_prefer_simplified_interpolation = true:suggestion -dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion -dotnet_style_namespace_match_folder = true:suggestion -dotnet_style_readonly_field = true:warning -dotnet_style_predefined_type_for_member_access = true:silent -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_allow_multiple_blank_lines_experimental = false:error +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_object_initializer = true:error +dotnet_style_collection_initializer = true:error +dotnet_style_prefer_simplified_boolean_expressions = true:error +dotnet_style_prefer_conditional_expression_over_assignment = true:error +dotnet_style_explicit_tuple_names = true:error +dotnet_style_prefer_conditional_expression_over_return = true:error +dotnet_style_prefer_inferred_tuple_names = true:error +dotnet_style_prefer_inferred_anonymous_type_member_names = true:error +dotnet_style_prefer_compound_assignment = true:error +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:warning +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = false:error dotnet_style_allow_statement_immediately_after_block_experimental = false:warning -dotnet_code_quality_unused_parameters = all:error -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:error -dotnet_style_qualification_for_method = false:error -dotnet_style_qualification_for_event = false:error \ No newline at end of file +dotnet_code_quality_unused_parameters = all:error +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:error +dotnet_style_qualification_for_method = false:error +dotnet_style_qualification_for_event = false:error diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fd126ee..446b951 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,4 +4,3 @@ updates: directory: "/" schedule: interval: "weekly" - diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 0473985..125d797 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,9 +1,7 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - name: .NET on: + workflow_dispatch: push: branches: [ "main" ] pull_request: @@ -11,25 +9,51 @@ on: jobs: build: + name: Build and analyze + runs-on: windows-latest + + steps: + - name: Set up JDK + uses: actions/setup-java@v4.4.0 + with: + java-version: 17 + distribution: 'zulu' - runs-on: ubuntu-latest + - name: Checkout + uses: actions/checkout@v4.2.1 + with: + fetch-depth: 0 - steps: - - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.0.x - - - name: Delete nuget*.config files - run: rm -f nuget*.config - - - name: Restore dependencies - run: dotnet restore - - - name: Build - run: dotnet build --no-restore - - - name: Test - run: dotnet test --no-build --verbosity normal + - name: 🛠 Cache SonarQube Cloud packages + uses: actions/cache@v4.2.3 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: 🛠 Cache SonarQube Cloud scanner + id: cache-sonar-scanner + uses: actions/cache@v4.2.3 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + + - name: 🛠 Install SonarQube Cloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + + - name: 🔍 Restore, 🛠 Build and 🧪 Test with ☁️ SonarCloud / Qube project - ${{ vars.SONAR_PROJECT_NAME }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + dotnet tool install --global dotnet-coverage + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ github.event.repository.name }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.scanner.scanAll=false /d:sonar.scanner.skipJreProvisioning=true + dotnet build --configuration Release + dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Tests.EndToEnd"' -f xml -o 'coverage.xml' + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1f8c366 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,32 @@ +name: 🚀 Publish NuGet Package + +on: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: 🧾 Checkout code + uses: actions/checkout@v4 + + - name: 🛠 Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.x' + + - name: 🔍 Restore dependencies + run: dotnet restore + + - name: 🛠 Build solution + run: dotnet build --configuration Release + + - name: 📦 Pack NuGet package for ${{ github.event.repository.name }} + run: dotnet pack ./src/**/*.csproj --configuration Release --output ./nupkg + + - name: 🚀 Publish to NuGet.org + run: dotnet nuget push ./nupkg/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NuGet_API_Key }} # + diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.gitignore b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.gitignore new file mode 100644 index 0000000..361073b --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/modules.xml +/projectSettingsUpdater.xml +/.idea.AStar.Dev.Infrastructure.FilesDb.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.name b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.name new file mode 100644 index 0000000..a22f43b --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/.name @@ -0,0 +1 @@ +AStar.Dev.Infrastructure.FilesDb \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/encodings.xml b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/indexLayout.xml b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/inspectionProfiles/Project_Default.xml b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..8d66637 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/misc.xml b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/misc.xml new file mode 100644 index 0000000..90dee70 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/misc.xml @@ -0,0 +1,4 @@ + + + {} + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/vcs.xml b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/vcs.xml new file mode 100644 index 0000000..1364320 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Infrastructure.FilesDb/.idea/vcs.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/AStar.Dev.Example.sln.sln b/AStar.Dev.Example.sln.sln deleted file mode 100644 index be68fef..0000000 --- a/AStar.Dev.Example.sln.sln +++ /dev/null @@ -1,57 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ADD5430F-CD80-42C7-80DA-90048E210EE7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{73794993-9898-4968-AF19-C3E7450C94E4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Example.ClassLib", "src\AStar.Dev.Example.ClassLib\AStar.Dev.Example.ClassLib.csproj", "{A9C19332-40FE-4E24-A890-405D46CD72A5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "end-to-end", "end-to-end", "{F1C7FB9E-2F0F-41C9-822A-7320339193CA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration", "integration", "{D724595D-C6BC-4F31-9D2A-4F4707436F10}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit", "unit", "{10DD984D-6788-4E04-A89C-3270006F5C56}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Example.ClassLib.Tests.Unit", "test\unit\AStar.Dev.Example.ClassLib.Tests.Unit\AStar.Dev.Example.ClassLib.Tests.Unit.csproj", "{1D7D41F6-3866-4C00-A1BA-1675227FA9FA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1CEEE40-22D0-4F7B-AB2B-A308F8DE6A54}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - build-and-test.ps1 = build-and-test.ps1 - CodeMaid.config = CodeMaid.config - LICENSE = LICENSE - nuget.ci.config = nuget.ci.config - nuget.config = nuget.config - README.md = README.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9C19332-40FE-4E24-A890-405D46CD72A5}.Release|Any CPU.Build.0 = Release|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {A9C19332-40FE-4E24-A890-405D46CD72A5} = {ADD5430F-CD80-42C7-80DA-90048E210EE7} - {F1C7FB9E-2F0F-41C9-822A-7320339193CA} = {73794993-9898-4968-AF19-C3E7450C94E4} - {D724595D-C6BC-4F31-9D2A-4F4707436F10} = {73794993-9898-4968-AF19-C3E7450C94E4} - {10DD984D-6788-4E04-A89C-3270006F5C56} = {73794993-9898-4968-AF19-C3E7450C94E4} - {1D7D41F6-3866-4C00-A1BA-1675227FA9FA} = {10DD984D-6788-4E04-A89C-3270006F5C56} - EndGlobalSection -EndGlobal diff --git a/AStar.Dev.Infrastructure.FilesDb.slnx b/AStar.Dev.Infrastructure.FilesDb.slnx new file mode 100644 index 0000000..57c7220 --- /dev/null +++ b/AStar.Dev.Infrastructure.FilesDb.slnx @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/CodeMaid.config b/CodeMaid.config deleted file mode 100644 index 54f2ebe..0000000 --- a/CodeMaid.config +++ /dev/null @@ -1,82 +0,0 @@ - - - - -
- - - - - - <?xml version="1.0" encoding="utf-16"?> - <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <string>ReSharper disable </string> - <string>ReSharper enable </string> - </ArrayOfString> - - - - True - - - 1 - - - False - - - True - - - True - - - 1 - - - 2 - - - True - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - False - - - True - - - 1 - - - - \ No newline at end of file diff --git a/astar.ico b/astar.ico new file mode 100644 index 0000000..38b6709 Binary files /dev/null and b/astar.ico differ diff --git a/astar.png b/astar.png new file mode 100644 index 0000000..74b197e Binary files /dev/null and b/astar.png differ diff --git a/nuget.ci.config b/nuget.ci.config deleted file mode 100644 index aa5beec..0000000 --- a/nuget.ci.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/nuget.config b/nuget.config deleted file mode 100644 index 782724b..0000000 --- a/nuget.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj b/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj deleted file mode 100644 index 125f4c9..0000000 --- a/src/AStar.Dev.Example.ClassLib/AStar.Dev.Example.ClassLib.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net9.0 - enable - enable - - - diff --git a/src/AStar.Dev.Example.ClassLib/Class1.cs b/src/AStar.Dev.Example.ClassLib/Class1.cs deleted file mode 100644 index 27e9361..0000000 --- a/src/AStar.Dev.Example.ClassLib/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace AStar.Dev.Example.ClassLib; - -public class Class1 -{ - -} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.csproj b/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.csproj new file mode 100644 index 0000000..d6dbf74 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.csproj @@ -0,0 +1,73 @@ + + + + net9.0 + enable + enable + latest-recommended + True + + + + AStar Developement, Jason Barden + AStar Development + AStar Developement, 2025 + Defines the context and models for the FilesDb. + $(AssemblyName).xml + true + true + true + true + astar.png + $(AssemblyName) + MIT + https://github.com/astar-development/astar-dev-infrastructure-filesdb + Readme.md + true + Initial creation + true + git + https://github.com/astar-development/astar-dev-infrastructure-filesdb.git + snupkg + AStar Dev Infrastructure FilesDb + 0.1.0 + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + True + 1701;1702; + + + + True + 1701;1702; + + + diff --git a/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.xml b/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.xml new file mode 100644 index 0000000..b4cac2c --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/AStar.Dev.Infrastructure.FilesDb.xml @@ -0,0 +1,794 @@ + + + + AStar.Dev.Infrastructure.FilesDb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A lot of variations of this method have been tried, but none of them worked as expected. + This one does not work as we're using SQLite for testing, and it does not support DateTimeOffset. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The class + + + The list of files in the dB + + + + + + + + + + + + + + The list of files in the dB + + + + + + + + + The list of Events + + + + + Gets or sets the File Classifications + + + + + Gets or sets the Duplicate Details + + + + + The overridden OnModelCreating method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Defines dates/times for soft and hard deletion + + + + + Gets or sets when the file was 'soft deleted'. I know, shocking... + + + + + Gets or sets when the file was marked as 'soft delete pending'. I know, shocking... + + + + + Gets or sets when the file was marked as 'hard delete pending'. I know, shocking... + + + + + + + + + + + + + + + + + + The class defines the fields that will be mapped from the vw_DuplicateDetails in the database + + + + + Gets or sets the File Name + + + + + Gets or sets the Directory Name + + + + + Gets or sets the File Height + + + + + Gets or sets the File Width + + + + + Gets or sets the File Size + + + + + Gets or sets the File Handle + + + + + Gets or sets whether File is an image + + + + + Gets or sets the Instance count for the duplicate group + + + + + Gets or sets the Details Last Updated + + + + + Gets or sets the Last Viewed date + + + + + Gets or sets the Soft Deleted flag + + + + + Gets or sets the SoftDeletePending flag + + + + + Gets or sets the Move Required flag + + + + + Gets or sets the Hard Delete Pending flag + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gets or sets the Updated By property to track who made the change + + + + + Represents a sealed class for defining distinct event types such as Add, Update, and Delete. + This class ensures that only predefined instances of event types can be used, + providing type safety and preventing the creation of arbitrary states. + + + + + Private constructor to prevent external instantiation. + This ensures that only the static readonly instances above can be created. + + The integer value representing the event type. + The string name of the event type. + + + + Represents an 'Add' event type, typically used for new record creation. + + + + + Represents an 'Update' event type, typically used for modifying existing records. + + + + + Represents a 'SoftDelete' event type, typically used for 'soft' removing records. + + + + + Represents a 'HardDelete' event type, typically used for permanently removing records. + + + + + Gets the integer value associated with the event type. + + + + + Gets the string name of the event type. + + + + + + + + + + + Returns the string name of the event type, useful for debugging and display. + + The name of the event type. + + + + + + + Overloads the equality operator to compare two objects. + + The first to compare. + The second to compare. + true if the two objects are equal; otherwise, false. + + + + Overloads the inequality operator to compare two objects. + + The first to compare. + The second to compare. + true if the two objects are not equal; otherwise, false. + + + + + + + + + + + + + + + + + + + + + + + + + + + + The FileDetail class containing the current properties + + + + + The default constructor required by EF Core etc + + + + + The copy constructor that allows for passing an instance of FileInfo to this class, simplifying consumer code + + + The instance of FileInfo to map + + + + + + + + + Gets or sets the ID of the . I know, shocking... + + + + + + + + + Gets or sets the file name. I know, shocking... + + + + + Gets or sets the name of the directory containing the file detail. I know, shocking... + + + + + Gets the full name of the file with the path combined + + + + + Gets or sets the file size. I know, shocking... + + + + + Gets or sets whether the file is of a supported image type + + + + + Gets or sets the file handle. I know, shocking... + + + + + + + + + + + + + + + + + Gets or sets whether the file has been marked as 'needs to move'. I know, shocking... + + + + + Gets or sets the file deletion status. I know, shocking... + + + + + Returns this object in JSON format + + + This object serialized as a JSON object. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Defines the FileId + + The value of the File Id + + + + Defines the FileId + + The value of the File Id + + + The value of the File Id + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gets the file length property + + + + + Gets the file height property + + + + + Gets the file width property + + + + + The Create method will return a populated instance of the class + + + The length of the file + + + The height of the file if an image + + + The width of the file if an image + + + A populated instance of . + + + + + Returns this object in JSON format + + + This object serialized as a JSON object + + + + + The class that defines how the file sizes are deemed to be equal + + + + + The Equals method has been overridden to perform the equality check currently required. The equality check is for + Height, Width and Length - making this more of an ImageComparer... + + + An instance of the class to compare + + + The other instance of the class to compare + + + true if the files are deemed to be the same size, false otherwise + + + + + The GetHashCode has been overridden to return the hash-codes as per the fields compared in the overridden Equals + method + + + The to calculate the appropriate hash-code for + + + The hash-code, combined from the relevant properties own hash-codes + + + + + + + + + + + + + The enumeration defining the available search types + + + + + Search for images only + + + + + Search for all file types + + + + + Search for duplicates - file type is ignored + + + + + Search for duplicate images + + + + + The currently supported SortOrders + + + + + Order by the size descending + + + + + Order by the size ascending + + + + + Order by the name descending + + + + + Order by the name ascending + + + + diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/DeletionStatusConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/DeletionStatusConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/EventConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/EventConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/EventTypeConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/EventTypeConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileClassificationConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileClassificationConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileDetailConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileDetailConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileNamePartConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/FileNamePartConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Configurations/ImageDetailConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Configurations/ImageDetailConfiguration.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Constants.cs b/src/AStar.Dev.Infrastructure.FilesDb/Constants.cs new file mode 100644 index 0000000..5b3063e --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Constants.cs @@ -0,0 +1,10 @@ +namespace AStar.Dev.Infrastructure.FilesDb; + +/// +/// +public static class Constants +{ + /// + /// + public const string SchemaName = "files"; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DeletionStatusConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DeletionStatusConfiguration.cs new file mode 100644 index 0000000..6c0fe09 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DeletionStatusConfiguration.cs @@ -0,0 +1,27 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +internal sealed class DeletionStatusConfiguration : IComplexPropertyConfiguration +{ + public void Configure(ComplexPropertyBuilder builder) + { + _ = builder + .Property(deletionStatus => deletionStatus.HardDeletePending) + .HasColumnName("HardDeletePending") + .HasColumnType("datetimeoffset"); + + _ = builder + .Property(deletionStatus => deletionStatus.SoftDeletePending) + .HasColumnName("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + _ = builder + .Property(deletionStatus => deletionStatus.SoftDeleted) + .HasColumnName("SoftDeleted") + .HasColumnType("datetimeoffset"); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DirectoryNameConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DirectoryNameConfiguration.cs new file mode 100644 index 0000000..8e7d470 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/DirectoryNameConfiguration.cs @@ -0,0 +1,14 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +internal sealed class DirectoryNameConfiguration : IComplexPropertyConfiguration +{ + public void Configure(ComplexPropertyBuilder builder) + => builder.Property(directoryName => directoryName.Value) + .HasColumnName("DirectoryName") + .HasColumnType("nvarchar(256)"); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventConfiguration.cs new file mode 100644 index 0000000..d5f8839 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventConfiguration.cs @@ -0,0 +1,26 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +/// +/// +public class EventConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + _ = builder + .ToTable(nameof(Event), Constants.SchemaName) + .HasKey(fileDetail => fileDetail.Id); + + _ = builder.Property(fileDetail => fileDetail.FileName).HasMaxLength(256); + _ = builder.Property(fileDetail => fileDetail.DirectoryName).HasMaxLength(256); + _ = builder.Property(fileDetail => fileDetail.Handle).HasMaxLength(256); + _ = builder.Property(fileDetail => fileDetail.UpdatedBy).HasMaxLength(30); + + _ = builder.ComplexProperty(fileDetail => fileDetail.Type).Configure(new EventTypeConfiguration()); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventTypeConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventTypeConfiguration.cs new file mode 100644 index 0000000..5f7defd --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/EventTypeConfiguration.cs @@ -0,0 +1,15 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +internal sealed class EventTypeConfiguration : IComplexPropertyConfiguration +{ + public void Configure(ComplexPropertyBuilder builder) + { + _ = builder.Property(eventType => eventType.Value).HasColumnName("EventType").IsRequired(); + _ = builder.Property(eventType => eventType.Name).HasColumnName("EventName").IsRequired(); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileClassificationConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileClassificationConfiguration.cs new file mode 100644 index 0000000..7cc7943 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileClassificationConfiguration.cs @@ -0,0 +1,21 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +/// +/// +public class FileClassificationConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + _ = builder + .ToTable(nameof(FileClassification), Constants.SchemaName) + .HasKey(fileClassification => fileClassification.Id); + + _ = builder.HasMany(); + _ = builder.Property(fileClassification => fileClassification.Name).HasMaxLength(150); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileDetailConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileDetailConfiguration.cs new file mode 100644 index 0000000..8817e16 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileDetailConfiguration.cs @@ -0,0 +1,50 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +/// +/// +public class FileDetailConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + _ = builder.ToTable("FileDetail"); + + _ = builder.HasKey(file => file.Id); + + _ = builder.Property(file => file.Id) + .HasConversion(fileId => fileId.Value, fileId => new(fileId)); + + _ = builder.Ignore(fileDetail => fileDetail.FileName); + _ = builder.Ignore(fileDetail => fileDetail.DirectoryName); + _ = builder.Ignore(fileDetail => fileDetail.FullNameWithPath); + + _ = builder.Property(file => file.FileHandle) + .HasColumnType("nvarchar(256)") + .HasConversion(fileHandle => fileHandle.Value, fileHandle => new(fileHandle)); + + _ = builder.ComplexProperty(fileDetail => fileDetail.ImageDetail) + .Configure(new ImageDetailConfiguration()); + + _ = builder.ComplexProperty(fileDetail => fileDetail.DirectoryName) + .Configure(new DirectoryNameConfiguration()); + + _ = builder.ComplexProperty(fileDetail => fileDetail.FileName) + .Configure(new FileNameConfiguration()); + + _ = builder.ComplexProperty(fileDetail => fileDetail.DeletionStatus) + .Configure(new DeletionStatusConfiguration()); + + _ = builder.HasIndex(fileDetail => fileDetail.FileHandle).IsUnique(); + _ = builder.HasIndex(fileDetail => fileDetail.FileSize); + + // Composite index to optimize duplicate images search (partial optimization) + // Note: ImageHeight and ImageWidth can't be indexed directly as they're complex properties + _ = builder.HasIndex(fileDetail => new { fileDetail.IsImage, fileDetail.FileSize }) + .HasDatabaseName("IX_FileDetail_DuplicateImages"); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNameConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNameConfiguration.cs new file mode 100644 index 0000000..a8afc7e --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNameConfiguration.cs @@ -0,0 +1,14 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +internal sealed class FileNameConfiguration : IComplexPropertyConfiguration +{ + public void Configure(ComplexPropertyBuilder builder) + => builder.Property(fileName => fileName.Value) + .HasColumnName("FileName") + .HasColumnType("nvarchar(256)"); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNamePartConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNamePartConfiguration.cs new file mode 100644 index 0000000..f6016fb --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/FileNamePartConfiguration.cs @@ -0,0 +1,20 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +/// +/// +public class FileNamePartConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + _ = builder + .ToTable(nameof(FileNamePart), Constants.SchemaName) + .HasKey(fileNamePart => fileNamePart.Id); + + _ = builder.Property(fileNamePart => fileNamePart.Text).HasMaxLength(150); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/ImageDetailConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/ImageDetailConfiguration.cs new file mode 100644 index 0000000..5e8d738 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/Configurations/ImageDetailConfiguration.cs @@ -0,0 +1,15 @@ +using AStar.Dev.Infrastructure.Data.Configurations; +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AStar.Dev.Infrastructure.FilesDb.Data.Configurations; + +internal sealed class ImageDetailConfiguration : IComplexPropertyConfiguration +{ + public void Configure(ComplexPropertyBuilder builder) + { + _ = builder.Property(image => image.Width).HasColumnName("ImageWidth"); + _ = builder.Property(image => image.Height).HasColumnName("ImageHeight"); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileContextExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileContextExtensions.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDeletionStatusExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDeletionStatusExtensions.cs new file mode 100644 index 0000000..c08bd28 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDeletionStatusExtensions.cs @@ -0,0 +1,20 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailDeletionStatusExtensions +{ + /// + /// + /// + /// + /// + public static IQueryable IncludeDeletedOrDeletePending(this IQueryable files, bool includeDeleted) + => includeDeleted + ? files + : files.Where(f => f.DeletionStatus.HardDeletePending == null + && f.DeletionStatus.SoftDeletePending == null + && f.DeletionStatus.SoftDeleted == null); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDirectoryNameExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDirectoryNameExtensions.cs new file mode 100644 index 0000000..c22d542 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailDirectoryNameExtensions.cs @@ -0,0 +1,20 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; +using AStar.Dev.Utilities; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailDirectoryNameExtensions +{ + /// + /// + /// + /// + /// + /// + public static IQueryable WhereDirectoryNameMatches(this IQueryable files, string directoryName, bool includeSubDirectories) + => includeSubDirectories + ? files.Where(file => file.DirectoryName.Value.Contains(directoryName.RemoveTrailing(@"\"))) + : files.Where(file => file.DirectoryName.Value == directoryName.RemoveTrailing(@"\")); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailLastViewedExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailLastViewedExtensions.cs new file mode 100644 index 0000000..e93f614 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailLastViewedExtensions.cs @@ -0,0 +1,21 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailLastViewedExtensions +{ + /// + /// A lot of variations of this method have been tried, but none of them worked as expected. + /// This one does not work as we're using SQLite for testing, and it does not support DateTimeOffset. + /// + /// + /// + /// + /// + public static IQueryable WhereLastViewedIsOlderThan(this IQueryable files, int days, TimeProvider time) + => days == 0 + ? files + : files.Where(file => !file.FileLastViewed.HasValue || file.FileLastViewed.Value <= time.GetUtcNow().AddDays(-days)); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailOrderingExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailOrderingExtensions.cs new file mode 100644 index 0000000..8fa956b --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailOrderingExtensions.cs @@ -0,0 +1,24 @@ +using System.Diagnostics; +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailOrderingExtensions +{ + /// + /// + /// + /// + /// + public static IQueryable OrderResultsBy(this IQueryable files, SortOrder sortOrder) + => sortOrder switch + { + SortOrder.NameAscending => files.OrderBy(f => f.FileName.Value), + SortOrder.NameDescending => files.OrderByDescending(f => f.FileName.Value), + SortOrder.SizeAscending => files.OrderBy(f => f.FileSize), + SortOrder.SizeDescending => files.OrderByDescending(f => f.FileSize), + _ => throw new UnreachableException("If we reach here, a new SortOrder has been added but not included...") + }; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailPagingExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailPagingExtensions.cs new file mode 100644 index 0000000..07f92d3 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailPagingExtensions.cs @@ -0,0 +1,34 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailPagingExtensions +{ + /// + /// + /// + /// + /// + /// + public static IQueryable GetPage(this IQueryable files, int pageNumber, int pageSize) + { + pageSize = RestrictPageSize(pageSize); + + if(pageNumber < 1) + { + pageNumber = 1; + } + + return files.Skip(pageNumber * pageSize).Take(pageSize); + } + + private static int RestrictPageSize(int pageSize) + => pageSize switch + { + < 1 => 1, + > 50 => 50, + _ => pageSize + }; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailSearchTypeExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailSearchTypeExtensions.cs new file mode 100644 index 0000000..637ec7b --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailSearchTypeExtensions.cs @@ -0,0 +1,28 @@ +using System.Diagnostics; +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailSearchTypeExtensions +{ + /// + /// + /// + /// + /// + public static IQueryable OfSearchType(this IQueryable files, SearchType searchType) + => searchType switch + { + SearchType.All => files, + SearchType.Images => files.Where(f => f.IsImage), + SearchType.Duplicates => files.Where(f => files.Count(x => x.FileSize == f.FileSize) > 1), + SearchType.DuplicateImages => files.Where(f => f.IsImage && + files.Count(x => x.IsImage && + x.FileSize == f.FileSize && + x.ImageDetail.Height == f.ImageDetail.Height && + x.ImageDetail.Width == f.ImageDetail.Width) > 1), + _ => throw new UnreachableException("If we reach here, a new SearchType has been added but not included...") + }; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailTextContainsExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailTextContainsExtensions.cs new file mode 100644 index 0000000..3720692 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FileDetailTextContainsExtensions.cs @@ -0,0 +1,18 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// +public static class FileDetailTextContainsExtensions +{ + /// + /// + /// + /// + /// + public static IQueryable SelectFilesMatching(this IQueryable files, string? searchText) + => string.IsNullOrEmpty(searchText) + ? files + : files.Where(file => file.DirectoryName.Value.Contains(searchText) || file.FileName.Value.Contains(searchText)); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Data/FilesContext.cs b/src/AStar.Dev.Infrastructure.FilesDb/Data/FilesContext.cs new file mode 100644 index 0000000..c4dbfe1 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Data/FilesContext.cs @@ -0,0 +1,71 @@ +using AStar.Dev.Infrastructure.FilesDb.Models; +using Microsoft.EntityFrameworkCore; + +namespace AStar.Dev.Infrastructure.FilesDb.Data; + +/// +/// The class +/// +/// +/// The list of files in the dB +/// +public class FilesContext : DbContext +{ + /// + /// + /// + public FilesContext(DbContextOptions options) + : base(options) + { + } + + /// + /// + public FilesContext() + : base(new DbContextOptions()) + { + } + + /// + /// The list of files in the dB + /// + public virtual DbSet FileDetails { get; set; } = null!; + + /// + /// + public DbSet FileNameParts { get; set; } = null!; + + /// + /// The list of Events + /// + public virtual DbSet Events { get; set; } = null!; + + /// + /// Gets or sets the File Classifications + /// + public DbSet FileClassifications { get; set; } = null!; + + /// + /// Gets or sets the Duplicate Details + /// + public DbSet DuplicateDetails { get; set; } + + /// + /// The overridden OnModelCreating method + /// + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + _ = modelBuilder.UseCollation("SQL_Latin1_General_CP1_CI_AS"); + _ = modelBuilder.HasDefaultSchema(Constants.SchemaName); + _ = modelBuilder.ApplyConfigurationsFromAssembly(typeof(FilesContext).Assembly); + + _ = modelBuilder + .Entity(eb => + { + _ = eb.HasNoKey(); + _ = eb.ToView("vw_DuplicateDetails"); + }); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/EnumerableExtensions.cs b/src/AStar.Dev.Infrastructure.FilesDb/EnumerableExtensions.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/LICENSE b/src/AStar.Dev.Infrastructure.FilesDb/LICENSE new file mode 100644 index 0000000..0b1b024 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 AStar Development, Jason Barden + +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. diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250715194331_InitialCreation.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250715194331_InitialCreation.Designer.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250715194331_InitialCreation.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250715194331_InitialCreation.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.Designer.cs new file mode 100644 index 0000000..1d12a66 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.Designer.cs @@ -0,0 +1,343 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + [Migration("20250725184934_RecreateAgain")] + partial class RecreateAgain + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle"); + + b.HasIndex("FileName"); + + b.HasIndex("FileSize"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.cs new file mode 100644 index 0000000..01badff --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725184934_RecreateAgain.cs @@ -0,0 +1,218 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations; + +/// +public partial class RecreateAgain : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.EnsureSchema( + name: "files"); + + _ = migrationBuilder.CreateTable( + name: "Event", + schema: "files", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + EventOccurredAt = table.Column(type: "datetimeoffset", nullable: false), + FileName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + DirectoryName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Handle = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Width = table.Column(type: "int", nullable: true), + Height = table.Column(type: "int", nullable: true), + FileSize = table.Column(type: "bigint", nullable: false), + FileCreated = table.Column(type: "datetimeoffset", nullable: false), + FileLastModified = table.Column(type: "datetimeoffset", nullable: false), + UpdatedBy = table.Column(type: "nvarchar(30)", maxLength: 30, nullable: false), + EventName = table.Column(type: "nvarchar(max)", nullable: false), + EventType = table.Column(type: "int", nullable: false) + }, + constraints: table => _ = table.PrimaryKey("PK_Event", x => x.Id)); + + _ = migrationBuilder.CreateTable( + name: "FileClassification", + schema: "files", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(150)", maxLength: 150, nullable: false), + Celebrity = table.Column(type: "bit", nullable: false) + }, + constraints: table => _ = table.PrimaryKey("PK_FileClassification", x => x.Id)); + + _ = migrationBuilder.CreateTable( + name: "FileDetail", + schema: "files", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + FileName = table.Column(type: "nvarchar(256)", nullable: false), + FileSize = table.Column(type: "bigint", nullable: false), + IsImage = table.Column(type: "bit", nullable: false), + FileHandle = table.Column(type: "nvarchar(256)", nullable: false), + FileCreated = table.Column(type: "datetimeoffset", nullable: false), + FileLastModified = table.Column(type: "datetimeoffset", nullable: false), + FileLastViewed = table.Column(type: "datetimeoffset", nullable: true), + MoveRequired = table.Column(type: "bit", nullable: false), + HardDeletePending = table.Column(type: "datetimeoffset", nullable: true), + SoftDeletePending = table.Column(type: "datetimeoffset", nullable: true), + SoftDeleted = table.Column(type: "datetimeoffset", nullable: true), + ImageHeight = table.Column(type: "int", nullable: true), + ImageWidth = table.Column(type: "int", nullable: true), + UpdatedBy = table.Column(type: "nvarchar(max)", nullable: false), + UpdatedOn = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => _ = table.PrimaryKey("PK_FileDetail", x => x.Id)); + + _ = migrationBuilder.CreateTable( + name: "FileNamePart", + schema: "files", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Text = table.Column(type: "nvarchar(150)", maxLength: 150, nullable: false), + FileClassificationId = table.Column(type: "int", nullable: true) + }, + constraints: table => + { + _ = table.PrimaryKey("PK_FileNamePart", x => x.Id); + + _ = table.ForeignKey( + name: "FK_FileNamePart_FileClassification_FileClassificationId", + column: x => x.FileClassificationId, + principalSchema: "files", + principalTable: "FileClassification", + principalColumn: "Id"); + }); + + _ = migrationBuilder.CreateTable( + name: "FileClassificationFileDetail", + schema: "files", + columns: table => new + { + FileClassificationsId = table.Column(type: "int", nullable: false), + FileDetailsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + _ = table.PrimaryKey("PK_FileClassificationFileDetail", x => new { x.FileClassificationsId, x.FileDetailsId }); + + _ = table.ForeignKey( + name: "FK_FileClassificationFileDetail_FileClassification_FileClassificationsId", + column: x => x.FileClassificationsId, + principalSchema: "files", + principalTable: "FileClassification", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + _ = table.ForeignKey( + name: "FK_FileClassificationFileDetail_FileDetail_FileDetailsId", + column: x => x.FileDetailsId, + principalSchema: "files", + principalTable: "FileDetail", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + _ = migrationBuilder.CreateTable( + name: "FileClassificationFileNamePart", + schema: "files", + columns: table => new + { + FileClassificationsId = table.Column(type: "int", nullable: false), + FileNamePartsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + _ = table.PrimaryKey("PK_FileClassificationFileNamePart", x => new { x.FileClassificationsId, x.FileNamePartsId }); + + _ = table.ForeignKey( + name: "FK_FileClassificationFileNamePart_FileClassification_FileClassificationsId", + column: x => x.FileClassificationsId, + principalSchema: "files", + principalTable: "FileClassification", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + _ = table.ForeignKey( + name: "FK_FileClassificationFileNamePart_FileNamePart_FileNamePartsId", + column: x => x.FileNamePartsId, + principalSchema: "files", + principalTable: "FileNamePart", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileClassificationFileDetail_FileDetailsId", + schema: "files", + table: "FileClassificationFileDetail", + column: "FileDetailsId"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileClassificationFileNamePart_FileNamePartsId", + schema: "files", + table: "FileClassificationFileNamePart", + column: "FileNamePartsId"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileHandle", + schema: "files", + table: "FileDetail", + column: "FileHandle"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileName", + schema: "files", + table: "FileDetail", + column: "FileName"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileSize", + schema: "files", + table: "FileDetail", + column: "FileSize"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileNamePart_FileClassificationId", + schema: "files", + table: "FileNamePart", + column: "FileClassificationId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.DropTable( + name: "Event", + schema: "files"); + + _ = migrationBuilder.DropTable( + name: "FileClassificationFileDetail", + schema: "files"); + + _ = migrationBuilder.DropTable( + name: "FileClassificationFileNamePart", + schema: "files"); + + _ = migrationBuilder.DropTable( + name: "FileDetail", + schema: "files"); + + _ = migrationBuilder.DropTable( + name: "FileNamePart", + schema: "files"); + + _ = migrationBuilder.DropTable( + name: "FileClassification", + schema: "files"); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.Designer.cs new file mode 100644 index 0000000..25147bd --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.Designer.cs @@ -0,0 +1,351 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + [Migration("20250725190252_RecreateAgain2")] + partial class RecreateAgain2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("DirectoryName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DirectoryName#DirectoryName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("DirectoryName"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle"); + + b.HasIndex("FileName"); + + b.HasIndex("FileSize"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.cs new file mode 100644 index 0000000..821b1f2 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250725190252_RecreateAgain2.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations; + +/// +public partial class RecreateAgain2 : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + => migrationBuilder.AddColumn( + name: "DirectoryName", + schema: "files", + table: "FileDetail", + type: "nvarchar(256)", + nullable: false, + defaultValue: ""); + + /// + protected override void Down(MigrationBuilder migrationBuilder) + => migrationBuilder.DropColumn( + name: "DirectoryName", + schema: "files", + table: "FileDetail"); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.Designer.cs new file mode 100644 index 0000000..a83c27b --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.Designer.cs @@ -0,0 +1,353 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + [Migration("20250729201535_UpdateFileNameConfiguration")] + partial class UpdateFileNameConfiguration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("DirectoryName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DirectoryName#DirectoryName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("DirectoryName"); + }); + + b.ComplexProperty>("FileName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.FileName#FileName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle"); + + b.HasIndex("FileSize"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.cs new file mode 100644 index 0000000..96028ca --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729201535_UpdateFileNameConfiguration.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations; + +/// +public partial class UpdateFileNameConfiguration : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + => migrationBuilder.DropIndex( + name: "IX_FileDetail_FileName", + schema: "files", + table: "FileDetail"); + + /// + protected override void Down(MigrationBuilder migrationBuilder) + => migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileName", + schema: "files", + table: "FileDetail", + column: "FileName"); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.Designer.cs new file mode 100644 index 0000000..1f1b5f3 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.Designer.cs @@ -0,0 +1,353 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + [Migration("20250729202510_AddFileDetailIndexes")] + partial class AddFileDetailIndexes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("DirectoryName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DirectoryName#DirectoryName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("DirectoryName"); + }); + + b.ComplexProperty>("FileName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.FileName#FileName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle"); + + b.HasIndex("FileSize"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.cs new file mode 100644 index 0000000..3990a25 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250729202510_AddFileDetailIndexes.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations; + +/// +public partial class AddFileDetailIndexes : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // NAR - the code changes did not produce a dB change + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // NAR - the code changes did not produce a dB change + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.Designer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.Designer.cs new file mode 100644 index 0000000..72fb318 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.Designer.cs @@ -0,0 +1,359 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + [Migration("20250808163238_AddDuplicateImagesIndex")] + partial class AddDuplicateImagesIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("DirectoryName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DirectoryName#DirectoryName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("DirectoryName"); + }); + + b.ComplexProperty>("FileName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.FileName#FileName", b1 => + { + b1.IsRequired(); + + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle") + .IsUnique(); + + b.HasIndex("FileSize"); + + b.HasIndex("IsImage", "FileSize") + .HasDatabaseName("IX_FileDetail_DuplicateImages"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.cs new file mode 100644 index 0000000..c3d91b0 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/20250808163238_AddDuplicateImagesIndex.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations; + +/// +public partial class AddDuplicateImagesIndex : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.DropIndex( + name: "IX_FileDetail_FileHandle", + schema: "files", + table: "FileDetail"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_DuplicateImages", + schema: "files", + table: "FileDetail", + columns: ["IsImage", "FileSize"]); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileHandle", + schema: "files", + table: "FileDetail", + column: "FileHandle", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.DropIndex( + name: "IX_FileDetail_DuplicateImages", + schema: "files", + table: "FileDetail"); + + _ = migrationBuilder.DropIndex( + name: "IX_FileDetail_FileHandle", + schema: "files", + table: "FileDetail"); + + _ = migrationBuilder.CreateIndex( + name: "IX_FileDetail_FileHandle", + schema: "files", + table: "FileDetail", + column: "FileHandle"); + } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Migrations/FilesContextModelSnapshot.cs b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/FilesContextModelSnapshot.cs new file mode 100644 index 0000000..5a4bfd3 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Migrations/FilesContextModelSnapshot.cs @@ -0,0 +1,356 @@ +// + +using System.Collections.Generic; +using AStar.Dev.Infrastructure.FilesDb.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AStar.Dev.Infrastructure.FilesDb.Migrations +{ + [DbContext(typeof(FilesContext))] + partial class FilesContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("files") + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.DuplicateDetail", b => + { + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileHandle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("HardDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("Instances") + .HasColumnType("int"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("SoftDeletePending") + .HasColumnType("datetimeoffset"); + + b.Property("SoftDeleted") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ToTable((string)null); + + b.ToView("vw_DuplicateDetails", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.Event", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DirectoryName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EventOccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Width") + .HasColumnType("int"); + + b.ComplexProperty>("Type", "AStar.Dev.Infrastructure.FilesDb.Models.Event.Type#EventType", b1 => + { + b1.IsRequired(); + + b1.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("EventName"); + + b1.Property("Value") + .HasColumnType("int") + .HasColumnName("EventType"); + }); + + b.HasKey("Id"); + + b.ToTable("Event", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Celebrity") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.ToTable("FileClassification", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("FileCreated") + .HasColumnType("datetimeoffset"); + + b.Property("FileHandle") + .IsRequired() + .HasColumnType("nvarchar(256)"); + + b.Property("FileLastModified") + .HasColumnType("datetimeoffset"); + + b.Property("FileLastViewed") + .HasColumnType("datetimeoffset"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("IsImage") + .HasColumnType("bit"); + + b.Property("MoveRequired") + .HasColumnType("bit"); + + b.Property("UpdatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedOn") + .HasColumnType("datetimeoffset"); + + b.ComplexProperty>("DeletionStatus", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DeletionStatus#DeletionStatus", b1 => + { + b1.IsRequired(); + + b1.Property("HardDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("HardDeletePending"); + + b1.Property("SoftDeletePending") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeletePending"); + + b1.Property("SoftDeleted") + .HasColumnType("datetimeoffset") + .HasColumnName("SoftDeleted"); + }); + + b.ComplexProperty>("DirectoryName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.DirectoryName#DirectoryName", b1 => + { + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("DirectoryName"); + }); + + b.ComplexProperty>("FileName", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.FileName#FileName", b1 => + { + b1.IsRequired(); + + b1.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasColumnName("FileName"); + }); + + b.ComplexProperty>("ImageDetail", "AStar.Dev.Infrastructure.FilesDb.Models.FileDetail.ImageDetail#ImageDetail", b1 => + { + b1.IsRequired(); + + b1.Property("Height") + .HasColumnType("int") + .HasColumnName("ImageHeight"); + + b1.Property("Width") + .HasColumnType("int") + .HasColumnName("ImageWidth"); + }); + + b.HasKey("Id"); + + b.HasIndex("FileHandle") + .IsUnique(); + + b.HasIndex("FileSize"); + + b.HasIndex("IsImage", "FileSize") + .HasDatabaseName("IX_FileDetail_DuplicateImages"); + + b.ToTable("FileDetail", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FileClassificationId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("nvarchar(150)"); + + b.HasKey("Id"); + + b.HasIndex("FileClassificationId"); + + b.ToTable("FileNamePart", "files"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileDetailsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileDetailsId"); + + b.HasIndex("FileDetailsId"); + + b.ToTable("FileClassificationFileDetail", "files"); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.Property("FileClassificationsId") + .HasColumnType("int"); + + b.Property("FileNamePartsId") + .HasColumnType("int"); + + b.HasKey("FileClassificationsId", "FileNamePartsId"); + + b.HasIndex("FileNamePartsId"); + + b.ToTable("FileClassificationFileNamePart", "files"); + }); + + modelBuilder.Entity("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationId"); + }); + + modelBuilder.Entity("FileClassificationFileDetail", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileDetail", null) + .WithMany() + .HasForeignKey("FileDetailsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("FileClassificationFileNamePart", b => + { + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileClassification", null) + .WithMany() + .HasForeignKey("FileClassificationsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AStar.Dev.Infrastructure.FilesDb.Models.FileNamePart", null) + .WithMany() + .HasForeignKey("FileNamePartsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/DeletionStatus.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/DeletionStatus.cs new file mode 100644 index 0000000..cbcf9fb --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/DeletionStatus.cs @@ -0,0 +1,22 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// Defines dates/times for soft and hard deletion +/// +public class DeletionStatus +{ + /// + /// Gets or sets when the file was 'soft deleted'. I know, shocking... + /// + public DateTimeOffset? SoftDeleted { get; set; } + + /// + /// Gets or sets when the file was marked as 'soft delete pending'. I know, shocking... + /// + public DateTimeOffset? SoftDeletePending { get; set; } + + /// + /// Gets or sets when the file was marked as 'hard delete pending'. I know, shocking... + /// + public DateTimeOffset? HardDeletePending { get; set; } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/DirectoryName.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/DirectoryName.cs new file mode 100644 index 0000000..3d5af52 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/DirectoryName.cs @@ -0,0 +1,6 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +/// +public readonly record struct DirectoryName(string Value); \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/DuplicateDetail.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/DuplicateDetail.cs new file mode 100644 index 0000000..050fa83 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/DuplicateDetail.cs @@ -0,0 +1,82 @@ +using System.ComponentModel.DataAnnotations; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// The class defines the fields that will be mapped from the vw_DuplicateDetails in the database +/// +public class DuplicateDetail +{ + /// + /// Gets or sets the File Name + /// + [MaxLength(256)] + public required string FileName { get; set; } + + /// + /// Gets or sets the Directory Name + /// + [MaxLength(256)] + public required string DirectoryName { get; set; } + + /// + /// Gets or sets the File Height + /// + public int ImageHeight { get; set; } + + /// + /// Gets or sets the File Width + /// + public int ImageWidth { get; set; } + + /// + /// Gets or sets the File Size + /// + public long FileSize { get; set; } + + /// + /// Gets or sets the File Handle + /// + [MaxLength(256)] + public required string FileHandle { get; set; } + + /// + /// Gets or sets whether File is an image + /// + public bool IsImage { get; set; } + + /// + /// Gets or sets the Instance count for the duplicate group + /// + public int Instances { get; set; } + + /// + /// Gets or sets the Details Last Updated + /// + public DateTimeOffset UpdatedOn { get; set; } + + /// + /// Gets or sets the Last Viewed date + /// + public DateTimeOffset? FileLastViewed { get; set; } + + /// + /// Gets or sets the Soft Deleted flag + /// + public DateTimeOffset? SoftDeleted { get; set; } + + /// + /// Gets or sets the SoftDeletePending flag + /// + public DateTimeOffset? SoftDeletePending { get; set; } + + /// + /// Gets or sets the Move Required flag + /// + public bool MoveRequired { get; set; } + + /// + /// Gets or sets the Hard Delete Pending flag + /// + public DateTimeOffset? HardDeletePending { get; set; } +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/Event.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/Event.cs new file mode 100644 index 0000000..fea1e1e --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/Event.cs @@ -0,0 +1,57 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +#pragma warning disable CA1716 +public class Event +#pragma warning restore CA1716 +{ + /// + /// + public int Id { get; set; } + + /// + /// + public EventType Type { get; set; } = EventType.Update; + + /// + /// + public DateTimeOffset EventOccurredAt { get; set; } + + /// + /// + public string FileName { get; set; } = string.Empty; + + /// + /// + public string DirectoryName { get; set; } = string.Empty; + + /// + /// + public string Handle { get; set; } = string.Empty; + + /// + /// + public int? Width { get; set; } + + /// + /// + public int? Height { get; set; } + + /// + /// + public long FileSize { get; set; } + + /// + /// + public DateTimeOffset FileCreated { get; set; } + + /// + /// + public DateTimeOffset FileLastModified { get; set; } + + /// + /// Gets or sets the Updated By property to track who made the change + /// + public string UpdatedBy { get; set; } = "Jay Barden"; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/EventType.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/EventType.cs new file mode 100644 index 0000000..a64c947 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/EventType.cs @@ -0,0 +1,100 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// Represents a sealed class for defining distinct event types such as Add, Update, and Delete. +/// This class ensures that only predefined instances of event types can be used, +/// providing type safety and preventing the creation of arbitrary states. +/// +public sealed class EventType : IEquatable +{ + /// + /// Private constructor to prevent external instantiation. + /// This ensures that only the static readonly instances above can be created. + /// + /// The integer value representing the event type. + /// The string name of the event type. + private EventType(int value, string name) + { + Value = value; + Name = name; + } + + /// + /// Represents an 'Add' event type, typically used for new record creation. + /// + public static EventType Add => new(1, "Add"); + + /// + /// Represents an 'Update' event type, typically used for modifying existing records. + /// + public static EventType Update => new(2, "Update"); + + /// + /// Represents a 'SoftDelete' event type, typically used for 'soft' removing records. + /// + public static EventType SoftDelete => new(3, "SoftDelete"); + + /// + /// Represents a 'HardDelete' event type, typically used for permanently removing records. + /// + public static EventType HardDelete => new(4, "HardDelete"); + + /// + /// Gets the integer value associated with the event type. + /// + public int Value { get; } + + /// + /// Gets the string name of the event type. + /// + public string Name { get; } + + /// + public bool Equals(EventType? other) + { + if(other is null) + { + return false; + } + +#pragma warning disable IDE0046 + if(ReferenceEquals(this, other)) +#pragma warning restore IDE0046 + { + return true; + } + + return Value == other.Value && Name == other.Name; + } + + /// + public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is EventType other && Equals(other)); + + /// + /// Returns the string name of the event type, useful for debugging and display. + /// + /// The name of the event type. + public override string ToString() => Name; + + /// + public override int GetHashCode() => HashCode.Combine(Value, Name); + + /// + /// Overloads the equality operator to compare two objects. + /// + /// The first to compare. + /// The second to compare. + /// true if the two objects are equal; otherwise, false. + public static bool operator ==(EventType left, EventType right) +#pragma warning disable IDE0041 + => left?.Equals(right) ?? ReferenceEquals(right, null); +#pragma warning restore IDE0041 + + /// + /// Overloads the inequality operator to compare two objects. + /// + /// The first to compare. + /// The second to compare. + /// true if the two objects are not equal; otherwise, false. + public static bool operator !=(EventType left, EventType right) => !(left == right); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileClassification.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileClassification.cs new file mode 100644 index 0000000..3ea40ee --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileClassification.cs @@ -0,0 +1,26 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +public class FileClassification +{ + /// + /// + public int Id { get; set; } + + /// + /// + public string Name { get; set; } = string.Empty; + + /// + /// + public ICollection FileDetails { get; set; } = []; + + /// + /// + public bool Celebrity { get; set; } + + /// + /// + public ICollection FileNameParts { get; set; } = []; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileDetail.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileDetail.cs new file mode 100644 index 0000000..39a1d90 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileDetail.cs @@ -0,0 +1,104 @@ +using System.IO.Abstractions; +using AStar.Dev.Infrastructure.Data; +using AStar.Dev.Utilities; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// The FileDetail class containing the current properties +/// +public sealed class FileDetail : AuditableEntity +{ + /// + /// The default constructor required by EF Core etc + /// + public FileDetail() + { + } + + /// + /// The copy constructor that allows for passing an instance of FileInfo to this class, simplifying consumer code + /// + /// + /// The instance of FileInfo to map + /// + public FileDetail(IFileInfo fileInfo) + { + FileName = new(fileInfo.Name); + DirectoryName = new(fileInfo.DirectoryName!); + FileSize = fileInfo.Length; + } + + /// + /// + public ICollection FileClassifications { get; set; } = []; + + /// + /// Gets or sets the ID of the . I know, shocking... + /// + public FileId Id { get; set; } + + /// + /// + public ImageDetail ImageDetail { get; set; } = new(null, null); + + /// + /// Gets or sets the file name. I know, shocking... + /// + public required FileName FileName { get; set; } + + /// + /// Gets or sets the name of the directory containing the file detail. I know, shocking... + /// + public required DirectoryName DirectoryName { get; set; } + + /// + /// Gets the full name of the file with the path combined + /// + public string FullNameWithPath => Path.Combine(DirectoryName.Value, FileName.Value ?? ""); + + /// + /// Gets or sets the file size. I know, shocking... + /// + public long FileSize { get; set; } + + /// + /// Gets or sets whether the file is of a supported image type + /// + public bool IsImage { get; set; } + + /// + /// Gets or sets the file handle. I know, shocking... + /// + public FileHandle FileHandle { get; set; } + + /// + /// + public DateTimeOffset FileCreated { get; set; } + + /// + /// + public DateTimeOffset FileLastModified { get; set; } + + /// + /// + public DateTimeOffset? FileLastViewed { get; set; } + + /// + /// Gets or sets whether the file has been marked as 'needs to move'. I know, shocking... + /// + public bool MoveRequired { get; set; } + + /// + /// Gets or sets the file deletion status. I know, shocking... + /// + public DeletionStatus DeletionStatus { get; set; } = new(); + + /// + /// Returns this object in JSON format + /// + /// + /// This object serialized as a JSON object. + /// + public override string ToString() => this.ToJson(); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileHandle.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileHandle.cs new file mode 100644 index 0000000..e800552 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileHandle.cs @@ -0,0 +1,19 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +/// +public readonly record struct FileHandle(string Value) +{ + /// + /// + /// + /// + public static FileHandle Create(string value) => string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value cannot be null or whitespace.", nameof(value)) : new FileHandle(value); + + /// + /// + /// + /// + public static implicit operator string(FileHandle d) => d.Value; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileId.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileId.cs new file mode 100644 index 0000000..752c33f --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileId.cs @@ -0,0 +1,7 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// Defines the FileId +/// +/// The value of the File Id +public readonly record struct FileId(int Value); \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileName.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileName.cs new file mode 100644 index 0000000..b6947c7 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileName.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +/// +[Index(nameof(Value))] +public record FileName(string Value) +{ + /// + /// + /// + public override string ToString() => Value; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileNamePart.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileNamePart.cs new file mode 100644 index 0000000..e95a32a --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileNamePart.cs @@ -0,0 +1,18 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +public class FileNamePart +{ + /// + /// + public int Id { get; set; } + + /// + /// + public string Text { get; set; } = string.Empty; + + /// + /// + public virtual ICollection FileClassifications { get; set; } = []; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSize.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSize.cs new file mode 100644 index 0000000..62cdd0f --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSize.cs @@ -0,0 +1,55 @@ +using AStar.Dev.Utilities; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +public sealed class FileSize +{ + private FileSize(long fileLength, int height, int width) + { + FileLength = fileLength; + Height = height; + Width = width; + } + + /// + /// Gets the file length property + /// + public long FileLength { get; } + + /// + /// Gets the file height property + /// + public int Height { get; } + + /// + /// Gets the file width property + /// + public int Width { get; } + + /// + /// The Create method will return a populated instance of the class + /// + /// + /// The length of the file + /// + /// + /// The height of the file if an image + /// + /// + /// The width of the file if an image + /// + /// + /// A populated instance of . + /// + public static FileSize Create(long fileLength, int height, int width) => new(fileLength, height, width); + + /// + /// Returns this object in JSON format + /// + /// + /// This object serialized as a JSON object + /// + public override string ToString() => this.ToJson(); +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSizeEqualityComparer.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSizeEqualityComparer.cs new file mode 100644 index 0000000..2d97fc9 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/FileSizeEqualityComparer.cs @@ -0,0 +1,44 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// The class that defines how the file sizes are deemed to be equal +/// +public sealed class FileSizeEqualityComparer : IEqualityComparer +{ + /// + /// The Equals method has been overridden to perform the equality check currently required. The equality check is for + /// Height, Width and Length - making this more of an ImageComparer... + /// + /// + /// An instance of the class to compare + /// + /// + /// The other instance of the class to compare + /// + /// + /// true if the files are deemed to be the same size, false otherwise + /// + public bool Equals(FileSize? leftFileSize, FileSize? rightFileSize) + => (rightFileSize == null && leftFileSize == null) + || CompareFileSizes(leftFileSize, rightFileSize); + + /// + /// The GetHashCode has been overridden to return the hash-codes as per the fields compared in the overridden Equals + /// method + /// + /// + /// The to calculate the appropriate hash-code for + /// + /// + /// The hash-code, combined from the relevant properties own hash-codes + /// + public int GetHashCode(FileSize fileSize) + => HashCode.Combine(fileSize.Height, fileSize.Width, fileSize.FileLength); + + private static bool CompareFileSizes(FileSize? leftFileSize, FileSize? rightFileSize) + => leftFileSize != null + && rightFileSize != null + && leftFileSize.Height == rightFileSize.Height + && leftFileSize.FileLength == rightFileSize.FileLength + && leftFileSize.Width == rightFileSize.Width; +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/IFileDetail.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/IFileDetail.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/ImageDetail.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/ImageDetail.cs new file mode 100644 index 0000000..4ba1d24 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/ImageDetail.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// +[ComplexType] +public record ImageDetail(int? Width, int? Height); \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/SearchType.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/SearchType.cs new file mode 100644 index 0000000..57e1452 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/SearchType.cs @@ -0,0 +1,27 @@ +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// The enumeration defining the available search types +/// +public enum SearchType +{ + /// + /// Search for images only + /// + Images, + + /// + /// Search for all file types + /// + All, + + /// + /// Search for duplicates - file type is ignored + /// + Duplicates, + + /// + /// Search for duplicate images + /// + DuplicateImages +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Models/SortOrder.cs b/src/AStar.Dev.Infrastructure.FilesDb/Models/SortOrder.cs new file mode 100644 index 0000000..dedb20b --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Models/SortOrder.cs @@ -0,0 +1,30 @@ +using AStar.Dev.Technical.Debt.Reporting; + +namespace AStar.Dev.Infrastructure.FilesDb.Models; + +/// +/// The currently supported SortOrders +/// +[Refactor(1, 1, "I think this is now duplicated too")] +public enum SortOrder +{ + /// + /// Order by the size descending + /// + SizeDescending, + + /// + /// Order by the size ascending + /// + SizeAscending, + + /// + /// Order by the name descending + /// + NameDescending, + + /// + /// Order by the name ascending + /// + NameAscending +} \ No newline at end of file diff --git a/src/AStar.Dev.Infrastructure.FilesDb/Readme.md b/src/AStar.Dev.Infrastructure.FilesDb/Readme.md new file mode 100644 index 0000000..bbfca68 --- /dev/null +++ b/src/AStar.Dev.Infrastructure.FilesDb/Readme.md @@ -0,0 +1,7 @@ +# Infrastructure - FilesDb Notes + +# Run from Infrastructure FilesDb directory + +dotnet ef migrations add "InitialCreation" --startup-project "../../apis/AStar.Dev.Files.Api/AStar.Dev.Files.Api.csproj" + +To apply, simply run the solution - migrations are automatically applied via the migrations project :-) diff --git a/src/AStar.Dev.Infrastructure.FilesDb/astar.ico b/src/AStar.Dev.Infrastructure.FilesDb/astar.ico new file mode 100644 index 0000000..38b6709 Binary files /dev/null and b/src/AStar.Dev.Infrastructure.FilesDb/astar.ico differ diff --git a/src/AStar.Dev.Infrastructure.FilesDb/astar.png b/src/AStar.Dev.Infrastructure.FilesDb/astar.png new file mode 100644 index 0000000..74b197e Binary files /dev/null and b/src/AStar.Dev.Infrastructure.FilesDb/astar.png differ diff --git a/test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/AStar.Dev.Infrastructure.FilesDb.Tests.Unit.csproj b/test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/AStar.Dev.Infrastructure.FilesDb.Tests.Unit.csproj new file mode 100644 index 0000000..e23157b --- /dev/null +++ b/test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/AStar.Dev.Infrastructure.FilesDb.Tests.Unit.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + + + + + diff --git a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs b/test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/UnitTest1.cs similarity index 55% rename from test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs rename to test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/UnitTest1.cs index 3928e91..7e520c3 100644 --- a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/UnitTest1.cs +++ b/test/AStar.Dev.Infrastructure.FilesDb.Tests.Unit/UnitTest1.cs @@ -1,10 +1,9 @@ -namespace AStar.Dev.Example.ClassLib.Tests.Unit; +namespace AStar.Dev.Infrastructure.FilesDb.Tests.Unit; public class UnitTest1 { [Fact] public void Test1() { - } -} +} \ No newline at end of file diff --git a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj b/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj deleted file mode 100644 index 6fbda4a..0000000 --- a/test/unit/AStar.Dev.Example.ClassLib.Tests.Unit/AStar.Dev.Example.ClassLib.Tests.Unit.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net9.0 - enable - enable - false - - - - - - - - - - - - - - - - - -