├── .editorconfig ├── .gitignore ├── README.md ├── ResultPattern.sln └── src ├── Common ├── Common.csproj └── ResultPattern │ ├── Error.cs │ ├── ErrorType.cs │ ├── Result.cs │ ├── ResultExtensions.cs │ └── ResultT.cs ├── ResultPattern.WebApi ├── Controllers │ ├── BaseController.cs │ └── ConfigurationsController.cs ├── DTOs │ └── Configurations │ │ ├── ConfigurationExtensions.cs │ │ ├── ConfigurationResponse.cs │ │ ├── CreateConfigurationRequest.cs │ │ └── UpdateConfigurationRequest.cs ├── Entities │ └── Configuration.cs ├── Errors │ └── ConfigurationErrors.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── ResultPattern.WebApi.csproj ├── Services │ ├── ConfigurationService.cs │ └── IConfigurationService.cs ├── appsettings.Development.json └── appsettings.json └── ResultPattern ├── Configurations ├── Configuration.cs ├── ConfigurationErrors.cs ├── ConfigurationExtensions.cs └── ConfigurationResponse.cs ├── Program.cs └── ResultPattern.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # All files 4 | [*] 5 | indent_style = space 6 | 7 | # Xml files 8 | [*.xml] 9 | indent_size = 2 10 | 11 | # C# files 12 | [*.cs] 13 | 14 | #### Core EditorConfig Options #### 15 | 16 | # Indentation and spacing 17 | indent_size = 4 18 | tab_width = 4 19 | 20 | # New line preferences 21 | end_of_line = crlf 22 | insert_final_newline = true 23 | 24 | #### .NET Coding Conventions #### 25 | [*.{cs,vb}] 26 | 27 | # Organize usings 28 | dotnet_separate_import_directive_groups = true 29 | dotnet_sort_system_directives_first = true 30 | file_header_template = unset 31 | 32 | # this. and Me. preferences 33 | dotnet_style_qualification_for_event = false:silent 34 | dotnet_style_qualification_for_field = false:silent 35 | dotnet_style_qualification_for_method = false:silent 36 | dotnet_style_qualification_for_property = false:silent 37 | 38 | # Language keywords vs BCL types preferences 39 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 40 | dotnet_style_predefined_type_for_member_access = true:silent 41 | 42 | # Parentheses preferences 43 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 44 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 45 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 46 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 47 | 48 | # Modifier preferences 49 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 50 | 51 | # Expression-level preferences 52 | dotnet_style_coalesce_expression = true:suggestion 53 | dotnet_style_collection_initializer = true:suggestion 54 | dotnet_style_explicit_tuple_names = true:suggestion 55 | dotnet_style_null_propagation = true:suggestion 56 | dotnet_style_object_initializer = true:suggestion 57 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 58 | dotnet_style_prefer_auto_properties = true:suggestion 59 | dotnet_style_prefer_compound_assignment = true:suggestion 60 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 61 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 62 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 63 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 64 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 65 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion 66 | dotnet_style_prefer_simplified_interpolation = true:suggestion 67 | 68 | # Field preferences 69 | dotnet_style_readonly_field = true:warning 70 | 71 | # Parameter preferences 72 | dotnet_code_quality_unused_parameters = all:suggestion 73 | 74 | # Suppression preferences 75 | dotnet_remove_unnecessary_suppression_exclusions = none 76 | 77 | #### C# Coding Conventions #### 78 | [*.cs] 79 | 80 | # var preferences 81 | csharp_style_var_elsewhere = false:silent 82 | csharp_style_var_for_built_in_types = false:silent 83 | csharp_style_var_when_type_is_apparent = false:silent 84 | 85 | # Expression-bodied members 86 | csharp_style_expression_bodied_accessors = true:silent 87 | csharp_style_expression_bodied_constructors = false:silent 88 | csharp_style_expression_bodied_indexers = true:silent 89 | csharp_style_expression_bodied_lambdas = true:suggestion 90 | csharp_style_expression_bodied_local_functions = false:silent 91 | csharp_style_expression_bodied_methods = false:silent 92 | csharp_style_expression_bodied_operators = false:silent 93 | csharp_style_expression_bodied_properties = true:silent 94 | 95 | # Pattern matching preferences 96 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 97 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 98 | csharp_style_prefer_not_pattern = true:suggestion 99 | csharp_style_prefer_pattern_matching = true:silent 100 | csharp_style_prefer_switch_expression = true:suggestion 101 | 102 | # Null-checking preferences 103 | csharp_style_conditional_delegate_call = true:suggestion 104 | 105 | # Modifier preferences 106 | csharp_prefer_static_local_function = true:warning 107 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent 108 | 109 | # Code-block preferences 110 | csharp_prefer_braces = true:silent 111 | csharp_prefer_simple_using_statement = true:suggestion 112 | 113 | # Expression-level preferences 114 | csharp_prefer_simple_default_expression = true:suggestion 115 | csharp_style_deconstructed_variable_declaration = true:suggestion 116 | csharp_style_inlined_variable_declaration = true:suggestion 117 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 118 | csharp_style_prefer_index_operator = true:suggestion 119 | csharp_style_prefer_range_operator = true:suggestion 120 | csharp_style_throw_expression = true:suggestion 121 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 122 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 123 | 124 | # 'using' directive preferences 125 | csharp_using_directive_placement = outside_namespace:silent 126 | 127 | #### C# Formatting Rules #### 128 | 129 | # New line preferences 130 | csharp_new_line_before_catch = true 131 | csharp_new_line_before_else = true 132 | csharp_new_line_before_finally = true 133 | csharp_new_line_before_members_in_anonymous_types = true 134 | csharp_new_line_before_members_in_object_initializers = true 135 | csharp_new_line_before_open_brace = all 136 | csharp_new_line_between_query_expression_clauses = true 137 | 138 | # Indentation preferences 139 | csharp_indent_block_contents = true 140 | csharp_indent_braces = false 141 | csharp_indent_case_contents = true 142 | csharp_indent_case_contents_when_block = true 143 | csharp_indent_labels = one_less_than_current 144 | csharp_indent_switch_labels = true 145 | 146 | # Space preferences 147 | csharp_space_after_cast = false 148 | csharp_space_after_colon_in_inheritance_clause = true 149 | csharp_space_after_comma = true 150 | csharp_space_after_dot = false 151 | csharp_space_after_keywords_in_control_flow_statements = true 152 | csharp_space_after_semicolon_in_for_statement = true 153 | csharp_space_around_binary_operators = before_and_after 154 | csharp_space_around_declaration_statements = false 155 | csharp_space_before_colon_in_inheritance_clause = true 156 | csharp_space_before_comma = false 157 | csharp_space_before_dot = false 158 | csharp_space_before_open_square_brackets = false 159 | csharp_space_before_semicolon_in_for_statement = false 160 | csharp_space_between_empty_square_brackets = false 161 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 162 | csharp_space_between_method_call_name_and_opening_parenthesis = false 163 | csharp_space_between_method_call_parameter_list_parentheses = false 164 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 165 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 166 | csharp_space_between_method_declaration_parameter_list_parentheses = false 167 | csharp_space_between_parentheses = false 168 | csharp_space_between_square_brackets = false 169 | 170 | # Wrapping preferences 171 | csharp_preserve_single_line_blocks = true 172 | csharp_preserve_single_line_statements = true 173 | 174 | #### Naming styles #### 175 | [*.{cs,vb}] 176 | 177 | # Naming rules 178 | 179 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion 180 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces 181 | dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase 182 | 183 | dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion 184 | dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces 185 | dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase 186 | 187 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion 188 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters 189 | dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase 190 | 191 | dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion 192 | dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods 193 | dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase 194 | 195 | dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion 196 | dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties 197 | dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase 198 | 199 | dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion 200 | dotnet_naming_rule.events_should_be_pascalcase.symbols = events 201 | dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase 202 | 203 | dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion 204 | dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables 205 | dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase 206 | 207 | dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion 208 | dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants 209 | dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase 210 | 211 | dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion 212 | dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters 213 | dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase 214 | 215 | dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion 216 | dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields 217 | dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase 218 | 219 | dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion 220 | dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields 221 | dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase 222 | 223 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion 224 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields 225 | dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase 226 | 227 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion 228 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields 229 | dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase 230 | 231 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion 232 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields 233 | dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase 234 | 235 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion 236 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields 237 | dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase 238 | 239 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion 240 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields 241 | dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase 242 | 243 | dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion 244 | dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums 245 | dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase 246 | 247 | dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion 248 | dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions 249 | dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase 250 | 251 | dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion 252 | dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members 253 | dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase 254 | 255 | # Symbol specifications 256 | 257 | dotnet_naming_symbols.interfaces.applicable_kinds = interface 258 | dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 259 | dotnet_naming_symbols.interfaces.required_modifiers = 260 | 261 | dotnet_naming_symbols.enums.applicable_kinds = enum 262 | dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 263 | dotnet_naming_symbols.enums.required_modifiers = 264 | 265 | dotnet_naming_symbols.events.applicable_kinds = event 266 | dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 267 | dotnet_naming_symbols.events.required_modifiers = 268 | 269 | dotnet_naming_symbols.methods.applicable_kinds = method 270 | dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 271 | dotnet_naming_symbols.methods.required_modifiers = 272 | 273 | dotnet_naming_symbols.properties.applicable_kinds = property 274 | dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 275 | dotnet_naming_symbols.properties.required_modifiers = 276 | 277 | dotnet_naming_symbols.public_fields.applicable_kinds = field 278 | dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal 279 | dotnet_naming_symbols.public_fields.required_modifiers = 280 | 281 | dotnet_naming_symbols.private_fields.applicable_kinds = field 282 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 283 | dotnet_naming_symbols.private_fields.required_modifiers = 284 | 285 | dotnet_naming_symbols.private_static_fields.applicable_kinds = field 286 | dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 287 | dotnet_naming_symbols.private_static_fields.required_modifiers = static 288 | 289 | dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum 290 | dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 291 | dotnet_naming_symbols.types_and_namespaces.required_modifiers = 292 | 293 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 294 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 295 | dotnet_naming_symbols.non_field_members.required_modifiers = 296 | 297 | dotnet_naming_symbols.type_parameters.applicable_kinds = namespace 298 | dotnet_naming_symbols.type_parameters.applicable_accessibilities = * 299 | dotnet_naming_symbols.type_parameters.required_modifiers = 300 | 301 | dotnet_naming_symbols.private_constant_fields.applicable_kinds = field 302 | dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 303 | dotnet_naming_symbols.private_constant_fields.required_modifiers = const 304 | 305 | dotnet_naming_symbols.local_variables.applicable_kinds = local 306 | dotnet_naming_symbols.local_variables.applicable_accessibilities = local 307 | dotnet_naming_symbols.local_variables.required_modifiers = 308 | 309 | dotnet_naming_symbols.local_constants.applicable_kinds = local 310 | dotnet_naming_symbols.local_constants.applicable_accessibilities = local 311 | dotnet_naming_symbols.local_constants.required_modifiers = const 312 | 313 | dotnet_naming_symbols.parameters.applicable_kinds = parameter 314 | dotnet_naming_symbols.parameters.applicable_accessibilities = * 315 | dotnet_naming_symbols.parameters.required_modifiers = 316 | 317 | dotnet_naming_symbols.public_constant_fields.applicable_kinds = field 318 | dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal 319 | dotnet_naming_symbols.public_constant_fields.required_modifiers = const 320 | 321 | dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field 322 | dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal 323 | dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static 324 | 325 | dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field 326 | dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected 327 | dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static 328 | 329 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 330 | dotnet_naming_symbols.local_functions.applicable_accessibilities = * 331 | dotnet_naming_symbols.local_functions.required_modifiers = 332 | 333 | # Naming styles 334 | 335 | dotnet_naming_style.pascalcase.required_prefix = 336 | dotnet_naming_style.pascalcase.required_suffix = 337 | dotnet_naming_style.pascalcase.word_separator = 338 | dotnet_naming_style.pascalcase.capitalization = pascal_case 339 | 340 | dotnet_naming_style.ipascalcase.required_prefix = I 341 | dotnet_naming_style.ipascalcase.required_suffix = 342 | dotnet_naming_style.ipascalcase.word_separator = 343 | dotnet_naming_style.ipascalcase.capitalization = pascal_case 344 | 345 | dotnet_naming_style.tpascalcase.required_prefix = T 346 | dotnet_naming_style.tpascalcase.required_suffix = 347 | dotnet_naming_style.tpascalcase.word_separator = 348 | dotnet_naming_style.tpascalcase.capitalization = pascal_case 349 | 350 | dotnet_naming_style._camelcase.required_prefix = _ 351 | dotnet_naming_style._camelcase.required_suffix = 352 | dotnet_naming_style._camelcase.word_separator = 353 | dotnet_naming_style._camelcase.capitalization = camel_case 354 | 355 | dotnet_naming_style.camelcase.required_prefix = 356 | dotnet_naming_style.camelcase.required_suffix = 357 | dotnet_naming_style.camelcase.word_separator = 358 | dotnet_naming_style.camelcase.capitalization = camel_case 359 | 360 | dotnet_naming_style.s_camelcase.required_prefix = s_ 361 | dotnet_naming_style.s_camelcase.required_suffix = 362 | dotnet_naming_style.s_camelcase.word_separator = 363 | dotnet_naming_style.s_camelcase.capitalization = camel_case 364 | 365 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from `dotnet new gitignore` 5 | 6 | # dotenv files 7 | .env 8 | 9 | # User-specific files 10 | *.rsuser 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Mono auto generated files 20 | mono_crash.* 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Ww][Ii][Nn]32/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 67 | 68 | # Tye 69 | .tye/ 70 | 71 | # ASP.NET Scaffolding 72 | ScaffoldingReadMe.txt 73 | 74 | # StyleCop 75 | StyleCopReport.xml 76 | 77 | # Files built by Visual Studio 78 | *_i.c 79 | *_p.c 80 | *_h.h 81 | *.ilk 82 | *.meta 83 | *.obj 84 | *.iobj 85 | *.pch 86 | *.pdb 87 | *.ipdb 88 | *.pgc 89 | *.pgd 90 | *.rsp 91 | *.sbr 92 | *.tlb 93 | *.tli 94 | *.tlh 95 | *.tmp 96 | *.tmp_proj 97 | *_wpftmp.csproj 98 | *.log 99 | *.tlog 100 | *.vspscc 101 | *.vssscc 102 | .builds 103 | *.pidb 104 | *.svclog 105 | *.scc 106 | 107 | # Chutzpah Test files 108 | _Chutzpah* 109 | 110 | # Visual C++ cache files 111 | ipch/ 112 | *.aps 113 | *.ncb 114 | *.opendb 115 | *.opensdf 116 | *.sdf 117 | *.cachefile 118 | *.VC.db 119 | *.VC.VC.opendb 120 | 121 | # Visual Studio profiler 122 | *.psess 123 | *.vsp 124 | *.vspx 125 | *.sap 126 | 127 | # Visual Studio Trace Files 128 | *.e2e 129 | 130 | # TFS 2012 Local Workspace 131 | $tf/ 132 | 133 | # Guidance Automation Toolkit 134 | *.gpState 135 | 136 | # ReSharper is a .NET coding add-in 137 | _ReSharper*/ 138 | *.[Rr]e[Ss]harper 139 | *.DotSettings.user 140 | 141 | # TeamCity is a build add-in 142 | _TeamCity* 143 | 144 | # DotCover is a Code Coverage Tool 145 | *.dotCover 146 | 147 | # AxoCover is a Code Coverage Tool 148 | .axoCover/* 149 | !.axoCover/settings.json 150 | 151 | # Coverlet is a free, cross platform Code Coverage Tool 152 | coverage*.json 153 | coverage*.xml 154 | coverage*.info 155 | 156 | # Visual Studio code coverage results 157 | *.coverage 158 | *.coveragexml 159 | 160 | # NCrunch 161 | _NCrunch_* 162 | .*crunch*.local.xml 163 | nCrunchTemp_* 164 | 165 | # MightyMoose 166 | *.mm.* 167 | AutoTest.Net/ 168 | 169 | # Web workbench (sass) 170 | .sass-cache/ 171 | 172 | # Installshield output folder 173 | [Ee]xpress/ 174 | 175 | # DocProject is a documentation generator add-in 176 | DocProject/buildhelp/ 177 | DocProject/Help/*.HxT 178 | DocProject/Help/*.HxC 179 | DocProject/Help/*.hhc 180 | DocProject/Help/*.hhk 181 | DocProject/Help/*.hhp 182 | DocProject/Help/Html2 183 | DocProject/Help/html 184 | 185 | # Click-Once directory 186 | publish/ 187 | 188 | # Publish Web Output 189 | *.[Pp]ublish.xml 190 | *.azurePubxml 191 | # Note: Comment the next line if you want to checkin your web deploy settings, 192 | # but database connection strings (with potential passwords) will be unencrypted 193 | *.pubxml 194 | *.publishproj 195 | 196 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 197 | # checkin your Azure Web App publish settings, but sensitive information contained 198 | # in these scripts will be unencrypted 199 | PublishScripts/ 200 | 201 | # NuGet Packages 202 | *.nupkg 203 | # NuGet Symbol Packages 204 | *.snupkg 205 | # The packages folder can be ignored because of Package Restore 206 | **/[Pp]ackages/* 207 | # except build/, which is used as an MSBuild target. 208 | !**/[Pp]ackages/build/ 209 | # Uncomment if necessary however generally it will be regenerated when needed 210 | #!**/[Pp]ackages/repositories.config 211 | # NuGet v3's project.json files produces more ignorable files 212 | *.nuget.props 213 | *.nuget.targets 214 | 215 | # Microsoft Azure Build Output 216 | csx/ 217 | *.build.csdef 218 | 219 | # Microsoft Azure Emulator 220 | ecf/ 221 | rcf/ 222 | 223 | # Windows Store app package directories and files 224 | AppPackages/ 225 | BundleArtifacts/ 226 | Package.StoreAssociation.xml 227 | _pkginfo.txt 228 | *.appx 229 | *.appxbundle 230 | *.appxupload 231 | 232 | # Visual Studio cache files 233 | # files ending in .cache can be ignored 234 | *.[Cc]ache 235 | # but keep track of directories ending in .cache 236 | !?*.[Cc]ache/ 237 | 238 | # Others 239 | ClientBin/ 240 | ~$* 241 | *~ 242 | *.dbmdl 243 | *.dbproj.schemaview 244 | *.jfm 245 | *.pfx 246 | *.publishsettings 247 | orleans.codegen.cs 248 | 249 | # Including strong name files can present a security risk 250 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 251 | #*.snk 252 | 253 | # Since there are multiple workflows, uncomment next line to ignore bower_components 254 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 255 | #bower_components/ 256 | 257 | # RIA/Silverlight projects 258 | Generated_Code/ 259 | 260 | # Backup & report files from converting an old project file 261 | # to a newer Visual Studio version. Backup files are not needed, 262 | # because we have git ;-) 263 | _UpgradeReport_Files/ 264 | Backup*/ 265 | UpgradeLog*.XML 266 | UpgradeLog*.htm 267 | ServiceFabricBackup/ 268 | *.rptproj.bak 269 | 270 | # SQL Server files 271 | *.mdf 272 | *.ldf 273 | *.ndf 274 | 275 | # Business Intelligence projects 276 | *.rdl.data 277 | *.bim.layout 278 | *.bim_*.settings 279 | *.rptproj.rsuser 280 | *- [Bb]ackup.rdl 281 | *- [Bb]ackup ([0-9]).rdl 282 | *- [Bb]ackup ([0-9][0-9]).rdl 283 | 284 | # Microsoft Fakes 285 | FakesAssemblies/ 286 | 287 | # GhostDoc plugin setting file 288 | *.GhostDoc.xml 289 | 290 | # Node.js Tools for Visual Studio 291 | .ntvs_analysis.dat 292 | node_modules/ 293 | 294 | # Visual Studio 6 build log 295 | *.plg 296 | 297 | # Visual Studio 6 workspace options file 298 | *.opt 299 | 300 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 301 | *.vbw 302 | 303 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 304 | *.vbp 305 | 306 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 307 | *.dsw 308 | *.dsp 309 | 310 | # Visual Studio 6 technical files 311 | *.ncb 312 | *.aps 313 | 314 | # Visual Studio LightSwitch build output 315 | **/*.HTMLClient/GeneratedArtifacts 316 | **/*.DesktopClient/GeneratedArtifacts 317 | **/*.DesktopClient/ModelManifest.xml 318 | **/*.Server/GeneratedArtifacts 319 | **/*.Server/ModelManifest.xml 320 | _Pvt_Extensions 321 | 322 | # Paket dependency manager 323 | .paket/paket.exe 324 | paket-files/ 325 | 326 | # FAKE - F# Make 327 | .fake/ 328 | 329 | # CodeRush personal settings 330 | .cr/personal 331 | 332 | # Python Tools for Visual Studio (PTVS) 333 | __pycache__/ 334 | *.pyc 335 | 336 | # Cake - Uncomment if you are using it 337 | # tools/** 338 | # !tools/packages.config 339 | 340 | # Tabs Studio 341 | *.tss 342 | 343 | # Telerik's JustMock configuration file 344 | *.jmconfig 345 | 346 | # BizTalk build output 347 | *.btp.cs 348 | *.btm.cs 349 | *.odx.cs 350 | *.xsd.cs 351 | 352 | # OpenCover UI analysis results 353 | OpenCover/ 354 | 355 | # Azure Stream Analytics local run output 356 | ASALocalRun/ 357 | 358 | # MSBuild Binary and Structured Log 359 | *.binlog 360 | 361 | # NVidia Nsight GPU debugger configuration file 362 | *.nvuser 363 | 364 | # MFractors (Xamarin productivity tool) working folder 365 | .mfractor/ 366 | 367 | # Local History for Visual Studio 368 | .localhistory/ 369 | 370 | # Visual Studio History (VSHistory) files 371 | .vshistory/ 372 | 373 | # BeatPulse healthcheck temp database 374 | healthchecksdb 375 | 376 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 377 | MigrationBackup/ 378 | 379 | # Ionide (cross platform F# VS Code tools) working folder 380 | .ionide/ 381 | 382 | # Fody - auto-generated XML schema 383 | FodyWeavers.xsd 384 | 385 | # VS Code files for those working on multiple tools 386 | .vscode/* 387 | !.vscode/settings.json 388 | !.vscode/tasks.json 389 | !.vscode/launch.json 390 | !.vscode/extensions.json 391 | *.code-workspace 392 | 393 | # Local History for Visual Studio Code 394 | .history/ 395 | 396 | # Windows Installer files from build outputs 397 | *.cab 398 | *.msi 399 | *.msix 400 | *.msm 401 | *.msp 402 | 403 | # JetBrains Rider 404 | *.sln.iml 405 | .idea 406 | 407 | ## 408 | ## Visual studio for Mac 409 | ## 410 | 411 | 412 | # globs 413 | Makefile.in 414 | *.userprefs 415 | *.usertasks 416 | config.make 417 | config.status 418 | aclocal.m4 419 | install-sh 420 | autom4te.cache/ 421 | *.tar.gz 422 | tarballs/ 423 | test-results/ 424 | 425 | # Mac bundle stuff 426 | *.dmg 427 | *.app 428 | 429 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 430 | # General 431 | .DS_Store 432 | .AppleDouble 433 | .LSOverride 434 | 435 | # Icon must end with two \r 436 | Icon 437 | 438 | 439 | # Thumbnails 440 | ._* 441 | 442 | # Files that might appear in the root of a volume 443 | .DocumentRevisions-V100 444 | .fseventsd 445 | .Spotlight-V100 446 | .TemporaryItems 447 | .Trashes 448 | .VolumeIcon.icns 449 | .com.apple.timemachine.donotpresent 450 | 451 | # Directories potentially created on remote AFP share 452 | .AppleDB 453 | .AppleDesktop 454 | Network Trash Folder 455 | Temporary Items 456 | .apdisk 457 | 458 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 459 | # Windows thumbnail cache files 460 | Thumbs.db 461 | ehthumbs.db 462 | ehthumbs_vista.db 463 | 464 | # Dump file 465 | *.stackdump 466 | 467 | # Folder config file 468 | [Dd]esktop.ini 469 | 470 | # Recycle Bin used on file shares 471 | $RECYCLE.BIN/ 472 | 473 | # Windows Installer files 474 | *.cab 475 | *.msi 476 | *.msix 477 | *.msm 478 | *.msp 479 | 480 | # Windows shortcuts 481 | *.lnk 482 | 483 | # Vim temporary swap files 484 | *.swp 485 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📝 Result Pattern in .NET and C# 2 | 3 | This repository provides an explanation of the Result pattern in .NET and C#, along with code examples to illustrate its usage. Let's dive in! 🚀 4 | 5 | ## 📚 What is the Result Pattern? 6 | 7 | The Result pattern is a design pattern commonly used to handle operations that can either succeed or fail. It provides a structured way to represent the outcome of an operation, allowing developers to handle both successful and error scenarios in a consistent manner. 8 | 9 | ## 💡 Why use the Result Pattern? 10 | 11 | By using the Result pattern, you can improve the clarity and maintainability of your code. It helps to avoid exceptions for expected error scenarios and promotes a more explicit and predictable flow of control. Additionally, it enables better error handling and provides a clear separation between the happy path and error handling logic. 12 | 13 | ## 🛠️ How to use the Result Pattern? 14 | 15 | To use the Result pattern, you typically define a custom `Result` type that encapsulates the outcome of an operation. This `Result` type can contain properties such as `IsSuccess`, `Error`, and `Value` to represent the success status, error (if any), and the result value (if successful) respectively. 16 | 17 | Here's an example of how the Result pattern can be implemented in C#: 18 | 19 | 1. Define Error types 20 | 21 | First, define an enumeration for different types of errors. 22 | 23 | ```csharp 24 | public enum ErrorType 25 | { 26 | Failure = 0, 27 | NotFound = 1, 28 | Validation = 2, 29 | Conflict = 3, 30 | AccessUnAuthorized = 4, 31 | AccessForbidden = 5 32 | } 33 | ``` 34 | 35 | 2. Create an Error Class 36 | 37 | This class will encapsulate error details. 38 | 39 | ```csharp 40 | public class Error 41 | { 42 | private Error( 43 | string code, 44 | string description, 45 | ErrorType errorType 46 | ) 47 | { 48 | Code = code; 49 | Description = description; 50 | ErrorType = errorType; 51 | } 52 | 53 | public string Code { get; } 54 | 55 | public string Description { get; } 56 | 57 | public ErrorType ErrorType { get; } 58 | 59 | public static Error Failure(string code, string description) => 60 | new(code, description, ErrorType.Failure); 61 | 62 | public static Error NotFound(string code, string description) => 63 | new(code, description, ErrorType.NotFound); 64 | 65 | public static Error Validation(string code, string description) => 66 | new(code, description, ErrorType.Validation); 67 | 68 | public static Error Conflict(string code, string description) => 69 | new(code, description, ErrorType.Conflict); 70 | 71 | public static Error AccessUnAuthorized(string code, string description) => 72 | new(code, description, ErrorType.AccessUnAuthorized); 73 | 74 | public static Error AccessForbidden(string code, string description) => 75 | new(code, description, ErrorType.AccessForbidden); 76 | } 77 | ``` 78 | 79 | 3. Implement the Result Class 80 | 81 | This class represents the outcome of an operation. 82 | 83 | ```csharp 84 | public class Result 85 | { 86 | protected Result() 87 | { 88 | IsSuccess = true; 89 | Error = default; 90 | } 91 | 92 | protected Result(Error error) 93 | { 94 | IsSuccess = false; 95 | Error = error; 96 | } 97 | 98 | public bool IsSuccess { get; } 99 | 100 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 101 | public Error? Error { get; } 102 | 103 | public static implicit operator Result(Error error) => 104 | new(error); 105 | 106 | public static Result Success() => 107 | new(); 108 | 109 | public static Result Failure(Error error) => 110 | new(error); 111 | } 112 | ``` 113 | 114 | 4. Implement the ResultT Class for Generic Results 115 | 116 | This class handles results that contain a value. 117 | 118 | ```csharp 119 | public sealed class ResultT : Result 120 | { 121 | private readonly TValue? _value; 122 | 123 | private ResultT( 124 | TValue value 125 | ) : base() 126 | { 127 | _value = value; 128 | } 129 | 130 | private ResultT( 131 | Error error 132 | ) : base(error) 133 | { 134 | _value = default; 135 | } 136 | 137 | public TValue Value => 138 | IsSuccess ? _value! : throw new InvalidOperationException("Value can not be accessed when IsSuccess is false"); 139 | 140 | public static implicit operator ResultT(Error error) => 141 | new(error); 142 | 143 | public static implicit operator ResultT(TValue value) => 144 | new(value); 145 | 146 | public static ResultT Success(TValue value) => 147 | new(value); 148 | 149 | public static new ResultT Failure(Error error) => 150 | new(error); 151 | } 152 | 153 | ``` 154 | 155 | 5. Add Extension Methods for Result Matching 156 | 157 | These methods help in handling results seamlessly. 158 | 159 | ```csharp 160 | public static class ResultExtensions 161 | { 162 | public static T Match( 163 | this Result result, 164 | Func onSuccess, 165 | Func onFailure) 166 | { 167 | return result.IsSuccess ? onSuccess() : onFailure(result.Error!); 168 | } 169 | 170 | public static T Match( 171 | this ResultT result, 172 | Func onSuccess, 173 | Func onFailure) 174 | { 175 | return result.IsSuccess ? onSuccess(result.Value) : onFailure(result.Error!); 176 | } 177 | } 178 | ``` 179 | 180 | ## 💻 Examples 181 | 182 | Here are a few examples to demonstrate the usage of the Result pattern: 183 | 184 | - Performing a database query to get Configuration by Id: 185 | 186 | ```csharp 187 | public async Task> GetByIdAsync(Guid id, CancellationToken ct) 188 | { 189 | // Fetch configuration from the DB 190 | Configuration? configuration = await repository.GetById(id, ct); 191 | 192 | if (configuration is null) 193 | { 194 | return ResultT.Failure(ConfigurationErrors.NotFound(id.ToString())); 195 | } 196 | return ResultT.Success(configuration.ToDto()); 197 | } 198 | ``` 199 | 200 | - Performing a database query to get Configuration by Id with use of implicit operator: 201 | 202 | ```csharp 203 | public async Task> GetByIdAsync(Guid id, CancellationToken ct) 204 | { 205 | // Fetch configuration from the DB 206 | Configuration? configuration = await repository.GetById(id, ct); 207 | 208 | if (configuration is null) 209 | { 210 | return ConfigurationErrors.NotFound(id.ToString()); 211 | } 212 | return configuration.ToDto(); 213 | } 214 | ``` 215 | 216 | - Handling Result in an API Endpoint: 217 | 218 | ```csharp 219 | public async Task GetById(Guid id, CancellationToken ct) 220 | { 221 | var result = await configurationService.GetByIdAsync(id, ct); 222 | 223 | if (result.IsSuccess) 224 | { 225 | return Ok(result.Value); 226 | } 227 | return Problem(result.Error!); 228 | } 229 | ``` 230 | 231 | - Handling Result in an API Endpoint using the Match extension Method: 232 | 233 | ```csharp 234 | public async Task GetById(Guid id, CancellationToken ct) 235 | { 236 | var result = await configurationService.GetByIdAsync(id, ct); 237 | 238 | return result.Match( 239 | onSuccess: Ok, 240 | onFailure: Problem 241 | ); 242 | } 243 | ``` 244 | 245 | ## 🏁 Conclusion 246 | 247 | The Result pattern is a powerful tool for handling success and error scenarios in .NET and C#. By adopting this pattern, you can write more robust and maintainable code that is easier to understand and debug. 248 | 249 | 🌟 Start using the Result pattern in your projects today and experience the benefits it brings! 250 | -------------------------------------------------------------------------------- /ResultPattern.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B71F979E-87E8-4633-AF40-F4B5DDFBEAF0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResultPattern", "src\ResultPattern\ResultPattern.csproj", "{2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResultPattern.WebApi", "src\ResultPattern.WebApi\ResultPattern.WebApi.csproj", "{04B2E083-743C-43D4-A58A-179B94432CCF}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "src\Common\Common.csproj", "{FA34179C-24B7-4E73-AC7B-257CB5BC6CEE}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {04B2E083-743C-43D4-A58A-179B94432CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {04B2E083-743C-43D4-A58A-179B94432CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {04B2E083-743C-43D4-A58A-179B94432CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {04B2E083-743C-43D4-A58A-179B94432CCF}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {FA34179C-24B7-4E73-AC7B-257CB5BC6CEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {FA34179C-24B7-4E73-AC7B-257CB5BC6CEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {FA34179C-24B7-4E73-AC7B-257CB5BC6CEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {FA34179C-24B7-4E73-AC7B-257CB5BC6CEE}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {2DDE1F4C-40C8-4833-8F8F-710AD04DB4DF} = {B71F979E-87E8-4633-AF40-F4B5DDFBEAF0} 38 | {04B2E083-743C-43D4-A58A-179B94432CCF} = {B71F979E-87E8-4633-AF40-F4B5DDFBEAF0} 39 | {FA34179C-24B7-4E73-AC7B-257CB5BC6CEE} = {B71F979E-87E8-4633-AF40-F4B5DDFBEAF0} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /src/Common/Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Common/ResultPattern/Error.cs: -------------------------------------------------------------------------------- 1 | namespace Common.ResultPattern; 2 | 3 | /// 4 | /// Represents an error in the application. 5 | /// 6 | public class Error 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The error code. 12 | /// The error description. 13 | /// The type of error. 14 | private Error( 15 | string code, 16 | string description, 17 | ErrorType errorType 18 | ) 19 | { 20 | Code = code; 21 | Description = description; 22 | ErrorType = errorType; 23 | } 24 | 25 | /// 26 | /// Gets the error code. 27 | /// 28 | public string Code { get; } 29 | 30 | /// 31 | /// Gets the error description. 32 | /// 33 | public string Description { get; } 34 | 35 | /// 36 | /// Gets the type of error. 37 | /// 38 | public ErrorType ErrorType { get; } 39 | 40 | /// 41 | /// Creates a new instance of with the specified code and description, representing a failure error. 42 | /// 43 | /// The error code. 44 | /// The error description. 45 | /// A new instance of . 46 | public static Error Failure(string code, string description) => 47 | new(code, description, ErrorType.Failure); 48 | 49 | /// 50 | /// Creates a new instance of with the specified code and description, representing a not found error. 51 | /// 52 | /// The error code. 53 | /// The error description. 54 | /// A new instance of . 55 | public static Error NotFound(string code, string description) => 56 | new(code, description, ErrorType.NotFound); 57 | 58 | /// 59 | /// Creates a new instance of with the specified code and description, representing a validation error. 60 | /// 61 | /// The error code. 62 | /// The error description. 63 | /// A new instance of . 64 | public static Error Validation(string code, string description) => 65 | new(code, description, ErrorType.Validation); 66 | 67 | /// 68 | /// Creates a new instance of with the specified code and description, representing a conflict error. 69 | /// 70 | /// The error code. 71 | /// The error description. 72 | /// A new instance of . 73 | public static Error Conflict(string code, string description) => 74 | new(code, description, ErrorType.Conflict); 75 | 76 | /// 77 | /// Creates a new instance of with the specified code and description, representing an access unauthorized error. 78 | /// 79 | /// The error code. 80 | /// The error description. 81 | /// A new instance of . 82 | public static Error AccessUnAuthorized(string code, string description) => 83 | new(code, description, ErrorType.AccessUnAuthorized); 84 | 85 | /// 86 | /// Creates a new instance of with the specified code and description, representing an access forbidden error. 87 | /// 88 | /// The error code. 89 | /// The error description. 90 | /// A new instance of . 91 | public static Error AccessForbidden(string code, string description) => 92 | new(code, description, ErrorType.AccessForbidden); 93 | } 94 | -------------------------------------------------------------------------------- /src/Common/ResultPattern/ErrorType.cs: -------------------------------------------------------------------------------- 1 | namespace Common.ResultPattern; 2 | 3 | /// 4 | /// Represents the types of errors that can occur in the application. 5 | /// 6 | public enum ErrorType 7 | { 8 | Failure = 0, 9 | NotFound = 1, 10 | Validation = 2, 11 | Conflict = 3, 12 | AccessUnAuthorized = 4, 13 | AccessForbidden = 5 14 | } 15 | -------------------------------------------------------------------------------- /src/Common/ResultPattern/Result.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Common.ResultPattern; 4 | 5 | /// 6 | /// Represents the result of an operation that can either be successful or contain an error. 7 | /// 8 | public class Result 9 | { 10 | /// 11 | /// Initializes a new instance of the class representing a successful result. 12 | /// 13 | protected Result() 14 | { 15 | IsSuccess = true; 16 | Error = default; 17 | } 18 | 19 | /// 20 | /// Initializes a new instance of the class representing a failed result with an error. 21 | /// 22 | /// The error associated with the failed result. 23 | protected Result(Error error) 24 | { 25 | IsSuccess = false; 26 | Error = error; 27 | } 28 | 29 | /// 30 | /// Gets a value indicating whether the result is successful. 31 | /// 32 | public bool IsSuccess { get; } 33 | 34 | /// 35 | /// Gets the error associated with the result, if any. 36 | /// 37 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 38 | public Error? Error { get; } 39 | 40 | /// 41 | /// Implicitly converts an to a representing a failed result. 42 | /// 43 | /// The error to convert. 44 | /// A new instance of representing a failed result with the specified error. 45 | public static implicit operator Result(Error error) => 46 | new(error); 47 | 48 | /// 49 | /// Creates a new instance of representing a successful result. 50 | /// 51 | /// A new instance of representing a successful result. 52 | public static Result Success() => 53 | new(); 54 | 55 | /// 56 | /// Creates a new instance of representing a failed result with the specified error. 57 | /// 58 | /// The error associated with the failed result. 59 | /// A new instance of representing a failed result with the specified error. 60 | public static Result Failure(Error error) => 61 | new(error); 62 | } 63 | -------------------------------------------------------------------------------- /src/Common/ResultPattern/ResultExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Common.ResultPattern; 2 | 3 | public static class ResultExtensions 4 | { 5 | /// 6 | /// Matches the result and executes the appropriate function based on whether the result is a success or failure. 7 | /// 8 | /// The return type of the success and failure functions. 9 | /// The result to match. 10 | /// The function to execute if the result is a success. 11 | /// The function to execute if the result is a failure. 12 | /// The result of executing the appropriate function. 13 | public static T Match( 14 | this Result result, 15 | Func onSuccess, 16 | Func onFailure) 17 | { 18 | return result.IsSuccess ? onSuccess() : onFailure(result.Error!); 19 | } 20 | 21 | /// 22 | /// Matches the result and executes the appropriate function based on whether the result is a success or failure. 23 | /// 24 | /// The return type of the success and failure functions. 25 | /// The type of the value contained in the result. 26 | /// The result to match. 27 | /// The function to execute if the result is a success. 28 | /// The function to execute if the result is a failure. 29 | /// The result of executing the appropriate function. 30 | public static T Match( 31 | this ResultT result, 32 | Func onSuccess, 33 | Func onFailure) 34 | { 35 | return result.IsSuccess ? onSuccess(result.Value) : onFailure(result.Error!); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Common/ResultPattern/ResultT.cs: -------------------------------------------------------------------------------- 1 | namespace Common.ResultPattern; 2 | 3 | /// 4 | /// Represents a result with a value of type . 5 | /// 6 | /// The type of the value. 7 | public sealed class ResultT : Result 8 | { 9 | private readonly TValue? _value; 10 | 11 | /// 12 | /// Initializes a new instance of the class with a successful result and a value. 13 | /// 14 | /// The value. 15 | private ResultT( 16 | TValue value 17 | ) : base() 18 | { 19 | _value = value; 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class with a failed result and an error. 24 | /// 25 | /// The error. 26 | private ResultT( 27 | Error error 28 | ) : base(error) 29 | { 30 | _value = default; 31 | } 32 | 33 | /// 34 | /// Gets the value of the result. 35 | /// 36 | /// Thrown when the result is not successful. 37 | public TValue Value => 38 | IsSuccess ? _value! : throw new InvalidOperationException("Value can not be accessed when IsSuccess is false"); 39 | 40 | /// 41 | /// Implicitly converts an to a with a failed result. 42 | /// 43 | /// The error. 44 | public static implicit operator ResultT(Error error) => 45 | new(error); 46 | 47 | /// 48 | /// Implicitly converts a value of type to a with a successful result. 49 | /// 50 | /// The value. 51 | public static implicit operator ResultT(TValue value) => 52 | new(value); 53 | 54 | /// 55 | /// Creates a new with a successful result and a value. 56 | /// 57 | /// The value. 58 | public static ResultT Success(TValue value) => 59 | new(value); 60 | 61 | /// 62 | /// Creates a new with a failed result and an error. 63 | /// 64 | /// The error. 65 | public static new ResultT Failure(Error error) => 66 | new(error); 67 | } 68 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ResultPattern.WebApi.Controllers; 6 | 7 | /// 8 | /// Represents a base controller for the Web API. 9 | /// 10 | [ApiController] 11 | public class BaseController : ControllerBase 12 | { 13 | /// 14 | /// Returns a problem response with the specified error details. 15 | /// 16 | /// The error details. 17 | /// A problem response with the specified error details. 18 | protected IActionResult Problem(Error error) 19 | { 20 | var statusCode = error.ErrorType switch 21 | { 22 | ErrorType.NotFound => StatusCodes.Status404NotFound, 23 | ErrorType.Validation => StatusCodes.Status400BadRequest, 24 | ErrorType.Conflict => StatusCodes.Status409Conflict, 25 | ErrorType.AccessUnAuthorized => StatusCodes.Status401Unauthorized, 26 | ErrorType.AccessForbidden => StatusCodes.Status403Forbidden, 27 | ErrorType.Failure => StatusCodes.Status500InternalServerError, 28 | _ => StatusCodes.Status500InternalServerError 29 | }; 30 | 31 | return Problem( 32 | statusCode: statusCode, 33 | title: error.Description, 34 | detail: error.Code); 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Controllers/ConfigurationsController.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | using ResultPattern.WebApi.DTOs.Configurations; 6 | using ResultPattern.WebApi.Services; 7 | 8 | namespace ResultPattern.WebApi.Controllers; 9 | 10 | /// 11 | /// Represents a Configurations controller. 12 | /// 13 | [Route("configurations")] 14 | public class ConfigurationsController( 15 | IConfigurationService configurationService) 16 | : BaseController 17 | { 18 | /// 19 | /// Retrieves all configurations. 20 | /// 21 | /// Cancellation token. 22 | /// An representing the result of the operation. 23 | [HttpGet] 24 | public async Task Get(CancellationToken ct) 25 | { 26 | var result = await configurationService.GetAsync(ct); 27 | 28 | return result.Match( 29 | onSuccess: Ok, 30 | onFailure: Problem 31 | ); 32 | } 33 | 34 | /// 35 | /// Retrieves a configuration by its ID. 36 | /// 37 | /// The ID of the configuration to retrieve. 38 | /// Cancellation token. 39 | /// An representing the result of the operation. 40 | [HttpGet("{id:guid}")] 41 | public async Task GetById(Guid id, CancellationToken ct) 42 | { 43 | var result = await configurationService.GetByIdAsync(id, ct); 44 | 45 | return result.Match( 46 | onSuccess: Ok, 47 | onFailure: Problem 48 | ); 49 | } 50 | 51 | /// 52 | /// Deletes a configuration by its ID. 53 | /// 54 | /// The ID of the configuration to delete. 55 | /// Cancellation token. 56 | /// An representing the result of the operation. 57 | [HttpDelete("{id:guid}")] 58 | public async Task Delete(Guid id, CancellationToken ct) 59 | { 60 | var result = await configurationService.DeleteAsync(id, ct); 61 | 62 | return result.Match( 63 | onSuccess: NoContent, 64 | onFailure: Problem 65 | ); 66 | } 67 | 68 | /// 69 | /// Updates a configuration by its ID. 70 | /// 71 | /// The ID of the configuration to update. 72 | /// The updated configuration data. 73 | /// Cancellation token. 74 | /// An representing the result of the operation. 75 | [HttpPut("{id:guid}")] 76 | public async Task Update(Guid id, UpdateConfigurationRequest request, CancellationToken ct) 77 | { 78 | var result = await configurationService.UpdateAsync(id, request, ct); 79 | 80 | return result.Match( 81 | onSuccess: NoContent, 82 | onFailure: Problem 83 | ); 84 | } 85 | 86 | /// 87 | /// Creates a new configuration. 88 | /// 89 | /// The configuration data to create. 90 | /// Cancellation token. 91 | /// An representing the result of the operation. 92 | [HttpPost] 93 | public async Task Create(CreateConfigurationRequest request, CancellationToken ct) 94 | { 95 | var result = await configurationService.AddAsync(request, ct); 96 | 97 | return result.Match( 98 | onSuccess: () => CreatedAtAction( 99 | actionName: nameof(GetById), 100 | routeValues: new { id = result.Value.Id }, 101 | value: result.Value 102 | ), 103 | onFailure: Problem 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/DTOs/Configurations/ConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using ResultPattern.WebApi.Entities; 2 | 3 | namespace ResultPattern.WebApi.DTOs.Configurations; 4 | 5 | public static class ConfigurationExtensions 6 | { 7 | /// 8 | /// Converts a object to a object. 9 | /// 10 | /// The object to convert. 11 | /// A object. 12 | public static ConfigurationResponse ToDto( 13 | this Configuration configuration 14 | ) 15 | { 16 | return new ConfigurationResponse( 17 | Id: configuration.Id.ToString(), 18 | Key: configuration.Key, 19 | Value: configuration.Value, 20 | Description: configuration.Description 21 | ); 22 | } 23 | 24 | /// 25 | /// Converts a list of objects to a list of objects. 26 | /// 27 | /// The list of objects to convert. 28 | /// A list of objects. 29 | public static List ToDto( 30 | this List configurations 31 | ) 32 | { 33 | return configurations 34 | .Select(ToDto) 35 | .ToList(); 36 | } 37 | 38 | /// 39 | /// Converts a object to a object. 40 | /// 41 | /// The object to convert. 42 | /// A object. 43 | public static Configuration ToEntity( 44 | this CreateConfigurationRequest request 45 | ) 46 | { 47 | return new Configuration 48 | { 49 | Id = Guid.NewGuid(), 50 | Key = request.Key, 51 | Value = request.Value, 52 | Description = request.Description 53 | }; 54 | } 55 | 56 | /// 57 | /// Converts a object to a object. 58 | /// 59 | /// The object to convert. 60 | /// The ID of the object. 61 | /// A object. 62 | public static Configuration ToEntity( 63 | this UpdateConfigurationRequest request, 64 | Guid id 65 | ) 66 | { 67 | return new Configuration 68 | { 69 | Id = id, 70 | Key = request.Key, 71 | Value = request.Value, 72 | Description = request.Description 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/DTOs/Configurations/ConfigurationResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.WebApi.DTOs.Configurations; 2 | 3 | public record ConfigurationResponse( 4 | string Id, 5 | string Key, 6 | string Value, 7 | string Description 8 | ); 9 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/DTOs/Configurations/CreateConfigurationRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.WebApi.DTOs.Configurations; 2 | 3 | public record CreateConfigurationRequest( 4 | string Key, 5 | string Value, 6 | string Description 7 | ); 8 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/DTOs/Configurations/UpdateConfigurationRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.WebApi.DTOs.Configurations; 2 | 3 | public record UpdateConfigurationRequest( 4 | string Key, 5 | string Value, 6 | string Description 7 | ); 8 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Entities/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.WebApi.Entities; 2 | 3 | public class Configuration 4 | { 5 | public Guid Id { get; init; } 6 | public required string Key { get; init; } 7 | public required string Value { get; init; } 8 | public required string Description { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Errors/ConfigurationErrors.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | namespace ResultPattern.WebApi.Errors; 4 | 5 | public static class ConfigurationErrors 6 | { 7 | public static Error NotFound(string id) => 8 | Error.NotFound("Configurations.NotFound", $"Configuration with Id: {id} not found"); 9 | 10 | public static Error Conflict(string name) => 11 | Error.Conflict("Configurations.Conflict", $"Configuration with Name: {name} already exists"); 12 | 13 | public static Error CreateFailure => 14 | Error.Failure("Configurations.CreateFailure", $"Something went wrong in creating configuration"); 15 | 16 | public static Error UpdateFailure => 17 | Error.Failure("Configurations.UpdateFailure", $"Something went wrong in updating configuration"); 18 | 19 | public static Error DeleteFailure => 20 | Error.Failure("Configurations.DeleteFailure", $"Something went wrong in deleting configuration"); 21 | } 22 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using ResultPattern.WebApi.Services; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | builder.Services.AddControllers(); 6 | builder.Services.AddEndpointsApiExplorer(); 7 | builder.Services.AddSwaggerGen(); 8 | builder.Services.AddScoped(); 9 | 10 | var app = builder.Build(); 11 | 12 | if (app.Environment.IsDevelopment()) 13 | { 14 | app.UseSwagger(); 15 | app.UseSwaggerUI(); 16 | } 17 | 18 | app.UseHttpsRedirection(); 19 | app.MapControllers(); 20 | app.Run(); 21 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:39685", 8 | "sslPort": 44312 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5165", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7209;http://localhost:5165", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/ResultPattern.WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Services/ConfigurationService.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | using ResultPattern.WebApi.DTOs.Configurations; 4 | using ResultPattern.WebApi.Entities; 5 | using ResultPattern.WebApi.Errors; 6 | 7 | namespace ResultPattern.WebApi.Services; 8 | 9 | public class ConfigurationService : IConfigurationService 10 | { 11 | 12 | public async Task>> GetAsync(CancellationToken ct) 13 | { 14 | List configurations = [ 15 | new Configuration 16 | { 17 | Id = Guid.NewGuid(), 18 | Key = "Key1", 19 | Value = "Value1", 20 | Description = "Description1" 21 | }, 22 | new Configuration 23 | { 24 | Id = Guid.NewGuid(), 25 | Key = "Key2", 26 | Value = "Value2", 27 | Description = "Description2" 28 | }, 29 | ]; 30 | 31 | // To mock the computation delay 32 | await Task.Delay(1000, ct); 33 | 34 | return configurations.ToDto(); 35 | } 36 | 37 | public async Task> GetByIdAsync(Guid id, CancellationToken ct) 38 | { 39 | Configuration? configuration = null; 40 | 41 | // To mock the 50% success response, and 50% failure response 42 | if (new Random().Next(2) == 0) 43 | { 44 | configuration = new Configuration 45 | { 46 | Id = Guid.NewGuid(), 47 | Key = "Key1", 48 | Value = "Value1", 49 | Description = "Description1" 50 | }; 51 | } 52 | 53 | // To mock the computation delay 54 | await Task.Delay(1000, ct); 55 | 56 | if (configuration is null) 57 | { 58 | return ConfigurationErrors.NotFound(id.ToString()); 59 | } 60 | return configuration.ToDto(); 61 | } 62 | 63 | public async Task> AddAsync(CreateConfigurationRequest request, CancellationToken ct) 64 | { 65 | var configurationExist = await IsConfigurationExistsAsync(request, ct); 66 | 67 | if (configurationExist) 68 | { 69 | return ConfigurationErrors.Conflict(request.Key.ToString()); 70 | } 71 | 72 | var resultOfCreateConfiguration = await SaveChangesAsync(request.ToEntity(), ct); 73 | 74 | if (!resultOfCreateConfiguration) 75 | { 76 | return ConfigurationErrors.CreateFailure; 77 | } 78 | 79 | var configuration = new Configuration 80 | { 81 | Id = Guid.NewGuid(), 82 | Key = "Key1", 83 | Value = "Value1", 84 | Description = "Description1" 85 | }; 86 | 87 | return configuration.ToDto(); 88 | 89 | } 90 | 91 | public async Task UpdateAsync(Guid id, UpdateConfigurationRequest request, CancellationToken ct) 92 | { 93 | Configuration? configuration = null; 94 | 95 | // To mock the 25% success response, and 25% failure response 96 | if (new Random().Next(4) > 0) 97 | { 98 | configuration = new Configuration 99 | { 100 | Id = Guid.NewGuid(), 101 | Key = "Key1", 102 | Value = "Value1", 103 | Description = "Description1" 104 | }; 105 | } 106 | 107 | // To mock the computation delay 108 | await Task.Delay(1000, ct); 109 | 110 | if (configuration is null) 111 | { 112 | return ConfigurationErrors.NotFound(id.ToString()); 113 | } 114 | 115 | var resultOfUpdateConfiguration = await SaveChangesAsync(request.ToEntity(id), ct); 116 | 117 | if (!resultOfUpdateConfiguration) 118 | { 119 | return ConfigurationErrors.UpdateFailure; 120 | } 121 | 122 | return Result.Success(); 123 | } 124 | 125 | public async Task DeleteAsync(Guid id, CancellationToken ct) 126 | { 127 | Configuration? configuration = null; 128 | 129 | // To mock the 50% success response, and 50% failure response 130 | if (new Random().Next(2) == 0) 131 | { 132 | configuration = new Configuration 133 | { 134 | Id = Guid.NewGuid(), 135 | Key = "Key1", 136 | Value = "Value1", 137 | Description = "Description1" 138 | }; 139 | } 140 | 141 | // To mock the computation delay 142 | await Task.Delay(1000, ct); 143 | 144 | if (configuration is null) 145 | { 146 | return ConfigurationErrors.NotFound(id.ToString()); 147 | } 148 | 149 | var resultOfDeleteConfiguration = await SaveChangesAsync(configuration, ct); 150 | 151 | if (!resultOfDeleteConfiguration) 152 | { 153 | return ConfigurationErrors.DeleteFailure; 154 | } 155 | 156 | return Result.Success(); 157 | } 158 | 159 | 160 | /// 161 | /// Dummy method to check if a configuration exists or not. if key is Test it will return true. 162 | /// 163 | /// The request containing the configuration key. 164 | /// The cancellation token. 165 | /// A task representing the asynchronous operation. The task result contains a boolean value indicating whether the configuration exists. 166 | private static async Task IsConfigurationExistsAsync( 167 | CreateConfigurationRequest request, 168 | CancellationToken ct) 169 | { 170 | // To mock the computation delay 171 | await Task.Delay(1000, ct); 172 | 173 | return request.Key == "Test"; 174 | } 175 | 176 | 177 | /// 178 | /// Dummy method Saves the changes. If the key is Test1 it will save it successfully. 179 | /// 180 | /// The configuration to save. 181 | /// The cancellation token. 182 | /// A task representing the asynchronous operation. The task result contains a boolean value indicating whether the changes was saved successfully. 183 | private static async Task SaveChangesAsync( 184 | Configuration configuration, 185 | CancellationToken ct) 186 | { 187 | // To mock the computation delay 188 | await Task.Delay(1000, ct); 189 | 190 | return configuration.Key == "Test1"; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/Services/IConfigurationService.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | using ResultPattern.WebApi.DTOs.Configurations; 4 | 5 | namespace ResultPattern.WebApi.Services; 6 | 7 | public interface IConfigurationService 8 | { 9 | /// 10 | /// Retrieves all configurations asynchronously. 11 | /// 12 | /// The cancellation token. 13 | /// A task that represents the asynchronous operation. The task result contains the result of the operation. 14 | Task>> GetAsync(CancellationToken ct); 15 | 16 | /// 17 | /// Retrieves a configuration by its ID asynchronously. 18 | /// 19 | /// The ID of the configuration to retrieve. 20 | /// The cancellation token. 21 | /// A task that represents the asynchronous operation. The task result contains the result of the operation. 22 | Task> GetByIdAsync(Guid id, CancellationToken ct); 23 | 24 | /// 25 | /// Adds a new configuration asynchronously. 26 | /// 27 | /// The request containing the configuration data. 28 | /// The cancellation token. 29 | /// A task that represents the asynchronous operation. The task result contains the result of the operation. 30 | Task> AddAsync(CreateConfigurationRequest request, CancellationToken ct); 31 | 32 | /// 33 | /// Updates a configuration asynchronously. 34 | /// 35 | /// The ID of the configuration to update. 36 | /// The request containing the updated configuration data. 37 | /// The cancellation token. 38 | /// A task that represents the asynchronous operation. The task result contains the result of the operation. 39 | Task UpdateAsync(Guid id, UpdateConfigurationRequest request, CancellationToken ct); 40 | 41 | /// 42 | /// Deletes a configuration asynchronously. 43 | /// 44 | /// The ID of the configuration to delete. 45 | /// The cancellation token. 46 | /// A task that represents the asynchronous operation. The task result contains the result of the operation. 47 | Task DeleteAsync(Guid id, CancellationToken ct); 48 | } 49 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/ResultPattern.WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /src/ResultPattern/Configurations/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.Configurations; 2 | 3 | public class Configuration 4 | { 5 | public Guid Id { get; init; } 6 | public required string Key { get; init; } 7 | public required string Value { get; init; } 8 | public required string Description { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /src/ResultPattern/Configurations/ConfigurationErrors.cs: -------------------------------------------------------------------------------- 1 | using Common.ResultPattern; 2 | 3 | namespace ResultPattern.Configurations; 4 | 5 | public static class ConfigurationErrors 6 | { 7 | public static Error NotFound(string id) => 8 | Error.NotFound("Configurations.NotFound", $"Configuration with Id: {id} not found"); 9 | 10 | public static Error Conflict(string name) => 11 | Error.Conflict("Configurations.Conflict", $"Configuration with Name: {name} already exists"); 12 | 13 | public static Error CreateFailure => 14 | Error.Failure("Configurations.CreateFailure", $"Something went wrong in creating configuration"); 15 | 16 | public static Error UpdateFailure => 17 | Error.Failure("Configurations.UpdateFailure", $"Something went wrong in updating configuration"); 18 | 19 | public static Error DeleteFailure => 20 | Error.Failure("Configurations.DeleteFailure", $"Something went wrong in deleting configuration"); 21 | } 22 | -------------------------------------------------------------------------------- /src/ResultPattern/Configurations/ConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.Configurations; 2 | 3 | public static class ConfigurationExtensions 4 | { 5 | public static ConfigurationResponse ToDto( 6 | this Configuration configuration 7 | ) 8 | { 9 | return new ConfigurationResponse( 10 | configuration.Key, 11 | configuration.Value, 12 | configuration.Description 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ResultPattern/Configurations/ConfigurationResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ResultPattern.Configurations; 2 | 3 | public record ConfigurationResponse( 4 | string Key, 5 | string Value, 6 | string Description 7 | ); 8 | -------------------------------------------------------------------------------- /src/ResultPattern/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | using Common.ResultPattern; 4 | 5 | using ResultPattern.Configurations; 6 | 7 | // Get By Id with success 8 | var getByIdResult = GetByIdWithSuccess(Guid.NewGuid()); 9 | Console.WriteLine($"GetByIdWithSuccess: {getByIdResult.IsSuccess}, {(getByIdResult.IsSuccess ? getByIdResult.Value : JsonSerializer.Serialize(getByIdResult.Error))}"); 10 | 11 | // Get By Id with failure 12 | var getByIdWihFailureResult = GetByIdWithFailure(Guid.NewGuid()); 13 | Console.WriteLine($"GetByIdWithFailure: {getByIdWihFailureResult.IsSuccess}, {(getByIdWihFailureResult.IsSuccess ? getByIdWihFailureResult.Value : JsonSerializer.Serialize(getByIdWihFailureResult.Error))}"); 14 | 15 | // Create with success 16 | var createdResult = CreateWithSuccess(); 17 | Console.WriteLine($"CreateWithSuccess: {createdResult.IsSuccess}, {(createdResult.IsSuccess ? createdResult.Value : JsonSerializer.Serialize(createdResult.Error))}"); 18 | 19 | // Create with failure 20 | var createdWithFailureResult = CreateWithFailure(); 21 | Console.WriteLine($"CreateWithFailure: {createdWithFailureResult.IsSuccess}, {(createdWithFailureResult.IsSuccess ? createdWithFailureResult.Value : JsonSerializer.Serialize(createdWithFailureResult.Error))}"); 22 | 23 | // Create with conflict failure 24 | var createdWithConflictFailureResult = CreateWithConflictFailure(); 25 | Console.WriteLine($"createdWithConflictFailureResult: {createdWithConflictFailureResult.IsSuccess}, {(createdWithConflictFailureResult.IsSuccess ? createdWithConflictFailureResult.Value : JsonSerializer.Serialize(createdWithConflictFailureResult.Error))}"); 26 | 27 | // Update with success 28 | var updatedResult = UpdateWithSuccess(); 29 | Console.WriteLine($"UpdateWithSuccess: {updatedResult.IsSuccess}, {(updatedResult.IsSuccess ? "No Content" : JsonSerializer.Serialize(updatedResult.Error))}"); 30 | 31 | // Update with failure 32 | var updatedWithFailureResult = UpdateWithFailure(); 33 | Console.WriteLine($"UpdateWithFailure: {updatedWithFailureResult.IsSuccess}, {(updatedWithFailureResult.IsSuccess ? "No Content" : JsonSerializer.Serialize(updatedWithFailureResult.Error))}"); 34 | 35 | // Dummy method to mock to Get By Id with success response 36 | static ResultT GetByIdWithSuccess( 37 | Guid id 38 | ) 39 | { 40 | // Configuration found from data source 41 | var configuration = new Configuration 42 | { 43 | Id = id, 44 | Key = "Key", 45 | Value = "Value", 46 | Description = "Description" 47 | }; 48 | 49 | // Map the entity to Dto 50 | return configuration.ToDto(); 51 | } 52 | 53 | // Dummy method to mock to Get By Id with failure response - NotFound 54 | static ResultT GetByIdWithFailure( 55 | Guid id 56 | ) 57 | { 58 | // Configuration not found from data source 59 | return ConfigurationErrors.NotFound(id.ToString()); 60 | } 61 | 62 | // Dummy method to mock to create with success response 63 | static ResultT CreateWithSuccess() 64 | { 65 | // Configuration created successfully 66 | var configuration = new Configuration 67 | { 68 | Id = Guid.NewGuid(), 69 | Key = "Key", 70 | Value = "Value", 71 | Description = "Description" 72 | }; 73 | 74 | // Map the entity to Dto 75 | return configuration.ToDto(); 76 | } 77 | 78 | // Dummy method to mock to create with failure response - CreateFailure 79 | static ResultT CreateWithFailure() 80 | { 81 | // Error in creating configuration 82 | return ConfigurationErrors.CreateFailure; 83 | } 84 | 85 | // Dummy method to mock to create with failure response - Conflict 86 | static ResultT CreateWithConflictFailure() 87 | { 88 | // Error in creating configuration as configuration with same key already exists 89 | return ConfigurationErrors.Conflict("Key"); 90 | } 91 | 92 | // Dummy method to mock to update with success response 93 | static Result UpdateWithSuccess() 94 | { 95 | // Configuration updated successfully 96 | return Result.Success(); 97 | } 98 | 99 | // Dummy method to mock to update with failure response - UpdateFailure 100 | static Result UpdateWithFailure() 101 | { 102 | // Error in updating configuration 103 | return ConfigurationErrors.UpdateFailure; 104 | } 105 | -------------------------------------------------------------------------------- /src/ResultPattern/ResultPattern.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | net8.0 10 | enable 11 | enable 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------