diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..111933fd
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,242 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+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
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+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 = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+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 = suggestion
+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 = suggestion
+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
+
+# 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.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 =
+
+# 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.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:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_object_initializer = true:suggestion
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 3a868930..b42fe34c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ riderModule.iml
/_ReSharper.Caches/
.fake
.vscode
+/MyFirstBlog.sln
+.vs/
\ No newline at end of file
diff --git a/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2 b/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2
deleted file mode 100644
index e3a42a61..00000000
Binary files a/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2 and /dev/null differ
diff --git a/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx b/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx
deleted file mode 100644
index ae1b509b..00000000
Binary files a/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx and /dev/null differ
diff --git a/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx b/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx
deleted file mode 100644
index 78b55c80..00000000
Binary files a/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx and /dev/null differ
diff --git a/.vs/MyFirstBlog/config/applicationhost.config b/.vs/MyFirstBlog/config/applicationhost.config
deleted file mode 100644
index 0d88f0db..00000000
--- a/.vs/MyFirstBlog/config/applicationhost.config
+++ /dev/null
@@ -1,1016 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.vs/MyFirstBlog/v17/.futdcache.v2 b/.vs/MyFirstBlog/v17/.futdcache.v2
deleted file mode 100644
index 128982d8..00000000
Binary files a/.vs/MyFirstBlog/v17/.futdcache.v2 and /dev/null differ
diff --git a/.vs/MyFirstBlog/v17/.suo b/.vs/MyFirstBlog/v17/.suo
deleted file mode 100644
index 74e1a08b..00000000
Binary files a/.vs/MyFirstBlog/v17/.suo and /dev/null differ
diff --git a/.vs/MyFirstBlog/v17/DocumentLayout.json b/.vs/MyFirstBlog/v17/DocumentLayout.json
deleted file mode 100644
index 653eb6b4..00000000
--- a/.vs/MyFirstBlog/v17/DocumentLayout.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{
- "Version": 1,
- "WorkspaceRootPath": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\",
- "Documents": [
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\properties\\launchsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\properties\\launchsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\entities\\post.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\entities\\post.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\services\\postservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\services\\postservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\extensions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\extensions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\dtos\\postdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\dtos\\postdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\controllers\\postscontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\controllers\\postscontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- }
- ],
- "DocumentGroupContainers": [
- {
- "Orientation": 0,
- "VerticalTabListWidth": 256,
- "DocumentGroups": [
- {
- "DockedWidth": 200,
- "SelectedChildIndex": 6,
- "Children": [
- {
- "$type": "Document",
- "DocumentIndex": 1,
- "Title": "Program.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Program.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Program.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Program.cs",
- "RelativeToolTip": "MyFirstBlog\\Program.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAA4AAAAjAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:44:39.864Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 2,
- "Title": "Post.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Entities\\Post.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Entities\\Post.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Entities\\Post.cs",
- "RelativeToolTip": "MyFirstBlog\\Entities\\Post.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAEAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:43:03.248Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 6,
- "Title": "PostDto.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Dtos\\PostDto.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Dtos\\PostDto.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Dtos\\PostDto.cs",
- "RelativeToolTip": "MyFirstBlog\\Dtos\\PostDto.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAgAAAABAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:42:47.081Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 4,
- "Title": "appsettings.json",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\appsettings.json",
- "RelativeDocumentMoniker": "MyFirstBlog\\appsettings.json",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\appsettings.json",
- "RelativeToolTip": "MyFirstBlog\\appsettings.json",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
- "WhenOpened": "2024-05-15T14:49:58.169Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 3,
- "Title": "PostService.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Services\\PostService.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Services\\PostService.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Services\\PostService.cs",
- "RelativeToolTip": "MyFirstBlog\\Services\\PostService.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAkAAAAkAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:45:00.15Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 5,
- "Title": "Extensions.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Extensions.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Extensions.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Extensions.cs",
- "RelativeToolTip": "MyFirstBlog\\Extensions.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAwAAAAuAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:44:53.417Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 0,
- "Title": "launchSettings.json",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Properties\\launchSettings.json",
- "RelativeDocumentMoniker": "MyFirstBlog\\Properties\\launchSettings.json",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Properties\\launchSettings.json",
- "RelativeToolTip": "MyFirstBlog\\Properties\\launchSettings.json",
- "ViewState": "AQIAAA8AAAAAAAAAAAAAABkAAAAfAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
- "WhenOpened": "2024-05-15T14:50:05.724Z",
- "EditorCaption": ""
- },
- {
- "$type": "Document",
- "DocumentIndex": 7,
- "Title": "PostsController.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Controllers\\PostsController.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Controllers\\PostsController.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Controllers\\PostsController.cs",
- "RelativeToolTip": "MyFirstBlog\\Controllers\\PostsController.cs",
- "ViewState": "AQIAAAYAAAAAAAAAAAAAAAsAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:42:54.111Z"
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin b/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin
deleted file mode 100644
index a3a97897..00000000
Binary files a/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin and /dev/null differ
diff --git a/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin b/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin
deleted file mode 100644
index cfc3e6a4..00000000
Binary files a/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin and /dev/null differ
diff --git a/MyFirstBlog.sln b/MyFirstBlog.sln
deleted file mode 100644
index e2ffab4c..00000000
--- a/MyFirstBlog.sln
+++ /dev/null
@@ -1,22 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFirstBlog", "MyFirstBlog\MyFirstBlog.csproj", "{BB64A02C-C5D9-4D64-A357-7322C54B1419}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFirstBlogTests", "MyFirstBlogTests\MyFirstBlogTests.csproj", "{84D99732-A7E9-4E69-B5C7-7DFAFE9815B7}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {BB64A02C-C5D9-4D64-A357-7322C54B1419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BB64A02C-C5D9-4D64-A357-7322C54B1419}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BB64A02C-C5D9-4D64-A357-7322C54B1419}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BB64A02C-C5D9-4D64-A357-7322C54B1419}.Release|Any CPU.Build.0 = Release|Any CPU
- {84D99732-A7E9-4E69-B5C7-7DFAFE9815B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {84D99732-A7E9-4E69-B5C7-7DFAFE9815B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {84D99732-A7E9-4E69-B5C7-7DFAFE9815B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {84D99732-A7E9-4E69-B5C7-7DFAFE9815B7}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/MyFirstBlog/Controllers/PostsController.cs b/MyFirstBlog/Controllers/PostsController.cs
index 8fa6bf2c..89447bc5 100644
--- a/MyFirstBlog/Controllers/PostsController.cs
+++ b/MyFirstBlog/Controllers/PostsController.cs
@@ -1,34 +1,55 @@
-namespace MyFirstBlog.Controllers;
-
using Microsoft.AspNetCore.Mvc;
using MyFirstBlog.Dtos;
+using MyFirstBlog.Entities;
using MyFirstBlog.Services;
+using System.Collections.Generic;
+
+namespace MyFirstBlog.Controllers
+{
+ [ApiController]
+ [Route("posts")]
+ public class PostsController : ControllerBase
+ {
+ private readonly IPostService _postService;
+
+ public PostsController(IPostService postService)
+ {
+ _postService = postService;
+ }
-[ApiController]
-[Route("posts")]
-
-public class PostsController : ControllerBase {
- private IPostService _postService;
+ // Get /posts
+ [HttpGet]
+ public IEnumerable GetPosts()
+ {
+ return _postService.GetPosts();
+ }
- public PostsController(IPostService postService) {
- _postService = postService;
- }
+ // Get /posts/:slug
+ [HttpGet("{slug}")]
+ public ActionResult GetPost(string slug)
+ {
+ var post = _postService.GetPost(slug);
+
+ if (post is null)
+ {
+ return NotFound();
+ }
+ return post;
+ }
- // Get /posts
- [HttpGet]
- public IEnumerable GetPosts() {
- return _postService.GetPosts();
- }
+ // Post /posts
+ [HttpPost("createnewpost")]
+ public IActionResult CreatePost([FromBody] CreatePostDto createPostDto)
+ {
+ if (string.IsNullOrWhiteSpace(createPostDto.Title))
+ {
+ return BadRequest(new { errors = new[] { "Title cannot be blank" } });
+ }
- // Get /posts/:slug
- [HttpGet("{slug}")]
- public ActionResult GetPost(string slug) {
- var post = _postService.GetPost(slug);
+ var post = _postService.CreatePost(createPostDto.Title, createPostDto.Description, createPostDto.Body);
- if (post is null) {
- return NotFound();
+ return CreatedAtAction(nameof(GetPost), new { slug = post.Slug }, post);
}
- return post;
}
}
diff --git a/MyFirstBlog/Dtos/CreatePostDto.cs b/MyFirstBlog/Dtos/CreatePostDto.cs
new file mode 100644
index 00000000..410b0235
--- /dev/null
+++ b/MyFirstBlog/Dtos/CreatePostDto.cs
@@ -0,0 +1,10 @@
+namespace MyFirstBlog.Dtos
+{
+ public class CreatePostDto
+ {
+ public string Title { get; set; }
+ public string Description { get; set; }
+
+ public string Body { get; set; }
+ }
+}
diff --git a/MyFirstBlog/Dtos/PostDto.cs b/MyFirstBlog/Dtos/PostDto.cs
index 12ca4818..ea3a835d 100644
--- a/MyFirstBlog/Dtos/PostDto.cs
+++ b/MyFirstBlog/Dtos/PostDto.cs
@@ -4,6 +4,8 @@ public record PostDto {
public Guid Id { get; init; }
public string Title { get; init; } = default!;
public string Slug { get; init; } = default!;
+
+ public string Description { get; init; } = default!;
public string Body { get; init; } = default!;
public DateTime CreatedDate { get; init; }
}
diff --git a/MyFirstBlog/Entities/Post.cs b/MyFirstBlog/Entities/Post.cs
index 9347e859..bc0b8c8e 100644
--- a/MyFirstBlog/Entities/Post.cs
+++ b/MyFirstBlog/Entities/Post.cs
@@ -5,4 +5,5 @@ public record Post {
public string Slug { get; init; } = default!;
public string Body { get; init; } = default!;
public DateTime CreatedDate { get; init; }
+ public string Description { get; init; } = default!;
}
diff --git a/MyFirstBlog/Helpers/DataContext.cs b/MyFirstBlog/Helpers/DataContext.cs
index 60ad2bfc..0b3ef1a0 100644
--- a/MyFirstBlog/Helpers/DataContext.cs
+++ b/MyFirstBlog/Helpers/DataContext.cs
@@ -1,22 +1,23 @@
-namespace MyFirstBlog.Helpers;
-
using Microsoft.EntityFrameworkCore;
using MyFirstBlog.Entities;
+using Microsoft.Extensions.Configuration;
-
-public class DataContext : DbContext
+namespace MyFirstBlog.Helpers
{
- protected readonly IConfiguration Configuration;
-
- public DataContext(IConfiguration configuration)
+ public class DataContext : DbContext
{
- Configuration = configuration;
- }
+ protected readonly IConfiguration Configuration;
- protected override void OnConfiguring(DbContextOptionsBuilder options)
- {
- options.UseNpgsql(ConnectionHelper.GetConnectionString(Configuration));
- }
+ public DataContext(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
- public DbSet Posts { get; set; }
-}
\ No newline at end of file
+ protected override void OnConfiguring(DbContextOptionsBuilder options)
+ {
+ options.UseNpgsql(ConnectionHelper.GetConnectionString(Configuration));
+ }
+
+ public DbSet Posts { get; set; }
+ }
+}
diff --git a/MyFirstBlog/Program.cs b/MyFirstBlog/Program.cs
index 4042b610..266729f1 100644
--- a/MyFirstBlog/Program.cs
+++ b/MyFirstBlog/Program.cs
@@ -35,7 +35,7 @@
if (env.IsDevelopment())
{
app.UseSwagger();
- app.UseSwaggerUI();
+ app.UseSwaggerUI();
app.UseCors(MyAllowLocalhostOrigins);
}
diff --git a/MyFirstBlog/Services/PostService.cs b/MyFirstBlog/Services/PostService.cs
index 6bac099f..ef4e8845 100644
--- a/MyFirstBlog/Services/PostService.cs
+++ b/MyFirstBlog/Services/PostService.cs
@@ -1,37 +1,81 @@
-namespace MyFirstBlog.Services;
-
using MyFirstBlog.Helpers;
using MyFirstBlog.Entities;
using System.Text.RegularExpressions;
using MyFirstBlog.Dtos;
+using System.Collections.Generic;
+using System.Linq;
-public interface IPostService
-{
- IEnumerable GetPosts();
- PostDto GetPost(String slug);
-}
-
-public class PostService : IPostService
+namespace MyFirstBlog.Services
{
- private DataContext _context;
-
- public PostService(DataContext context)
+ public interface IPostService
{
- _context = context;
+ IEnumerable GetPosts();
+ PostDto GetPost(string slug);
+ Post CreatePost(string title, string description, string body);
}
- public IEnumerable GetPosts()
+ public class PostService : IPostService
{
- return _context.Posts.Select(post => post.AsDto());
- }
+ private readonly DataContext _context;
- public PostDto GetPost(string slug)
- {
- return getPost(slug).AsDto();
- }
+ public PostService(DataContext context)
+ {
+ _context = context;
+ }
- private Post getPost(string slug)
- {
- return _context.Posts.Where(a=>a.Slug==slug.ToString()).SingleOrDefault();
+ public IEnumerable GetPosts()
+ {
+ return _context.Posts.Select(post => new PostDto
+ {
+ Title = post.Title,
+ Description = post.Description,
+ Slug = post.Slug,
+ CreatedDate = post.CreatedDate
+ });
+ }
+
+ public PostDto GetPost(string slug)
+ {
+ var post = getPost(slug);
+ if (post == null) return null;
+
+ return new PostDto
+ {
+ Title = post.Title,
+ Description = post.Description,
+ Slug = post.Slug,
+ CreatedDate = post.CreatedDate
+ };
+ }
+
+ private Post getPost(string slug)
+ {
+ return _context.Posts.FirstOrDefault(a => a.Slug == slug);
+ }
+
+ public Post CreatePost(string title, string description, string body)
+ {
+ var post = new Post
+ {
+ Title = title,
+ Description = description,
+ Body = body,
+ Slug = GenerateSlug(title),
+ CreatedDate = DateTime.UtcNow
+ };
+
+ _context.Posts.Add(post);
+ _context.SaveChanges();
+
+ return post;
+ }
+
+ private string GenerateSlug(string title)
+ {
+ var slug = Regex.Replace(title.ToLower(), @"[^a-z0-9\s-]", "");
+ slug = Regex.Replace(slug, @"\s+", " ").Trim();
+ slug = Regex.Replace(slug, @"\s", "-");
+ return slug;
+ }
}
}