Shady nagy/net6 (#614)
* udated to .net6 * used the .net6 version RC2 * added editconfig. * App core new Scoped Namespaces style. * BlazorAdmin new Scoped Namespaces style. * Blazor Shared new Scoped Namespaces style. * Infra new Scoped Namespaces style. * public api new Scoped Namespaces style. * web new Scoped Namespaces style. * FunctionalTests new Scoped Namespaces style. * Integrational tests new Scoped Namespaces style. * unit tests new Scoped Namespaces style. * update github action. * update github action. * change the global.
This commit is contained in:
134
.editorconfig
Normal file
134
.editorconfig
Normal file
@@ -0,0 +1,134 @@
|
||||
###############################
|
||||
# Core EditorConfig Options #
|
||||
###############################
|
||||
root = true
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# XML project files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
|
||||
# XML config files
|
||||
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
|
||||
indent_size = 2
|
||||
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
charset = utf-8-bom
|
||||
###############################
|
||||
# .NET Coding Conventions #
|
||||
###############################
|
||||
[*.{cs,vb}]
|
||||
# Organize usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
# this. preferences
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
###############################
|
||||
# Naming Conventions #
|
||||
###############################
|
||||
# Style Definitions
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
# Use PascalCase for constant fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
###############################
|
||||
# C# Coding Conventions #
|
||||
###############################
|
||||
[*.cs]
|
||||
# var preferences
|
||||
csharp_style_var_for_built_in_types = true:silent
|
||||
csharp_style_var_when_type_is_apparent = true:silent
|
||||
csharp_style_var_elsewhere = true:silent
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
# Null-checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
# Modifier preferences
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
|
||||
# Expression-level preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
# Namespaces
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
###############################
|
||||
# C# Formatting Rules #
|
||||
###############################
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
# Indentation preferences
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
###############################
|
||||
# VB Coding Conventions #
|
||||
###############################
|
||||
[*.vb]
|
||||
# Modifier preferences
|
||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||
3
.github/workflows/dotnetcore.yml
vendored
3
.github/workflows/dotnetcore.yml
vendored
@@ -12,7 +12,8 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
dotnet-version: '6.0.x'
|
||||
include-prerelease: true
|
||||
|
||||
- name: Build with dotnet
|
||||
run: dotnet build ./eShopOnWeb.sln --configuration Release
|
||||
|
||||
6
global.json
Normal file
6
global.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "6.0.x",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace Microsoft.eShopWeb
|
||||
namespace Microsoft.eShopWeb;
|
||||
|
||||
public class CatalogSettings
|
||||
{
|
||||
public class CatalogSettings
|
||||
{
|
||||
public string CatalogBaseUrl { get; set; }
|
||||
}
|
||||
public string CatalogBaseUrl { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Constants
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Constants;
|
||||
|
||||
public class AuthorizationConstants
|
||||
{
|
||||
public class AuthorizationConstants
|
||||
{
|
||||
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
|
||||
// TODO: Don't use this in production
|
||||
public const string DEFAULT_PASSWORD = "Pass@word1";
|
||||
// TODO: Don't use this in production
|
||||
public const string DEFAULT_PASSWORD = "Pass@word1";
|
||||
|
||||
// TODO: Change this to an environment variable
|
||||
public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
}
|
||||
// TODO: Change this to an environment variable
|
||||
public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
|
||||
// Using non-generic integer types for simplicity and to ease caching logic
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
|
||||
// Using non-generic integer types for simplicity and to ease caching logic
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
public virtual int Id { get; protected set; }
|
||||
}
|
||||
public virtual int Id { get; protected set; }
|
||||
}
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
public class Basket : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Basket : BaseEntity, IAggregateRoot
|
||||
public string BuyerId { get; private set; }
|
||||
private readonly List<BasketItem> _items = new List<BasketItem>();
|
||||
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();
|
||||
|
||||
public Basket(string buyerId)
|
||||
{
|
||||
public string BuyerId { get; private set; }
|
||||
private readonly List<BasketItem> _items = new List<BasketItem>();
|
||||
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
|
||||
public Basket(string buyerId)
|
||||
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
|
||||
{
|
||||
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
|
||||
return;
|
||||
}
|
||||
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
|
||||
existingItem.AddQuantity(quantity);
|
||||
}
|
||||
|
||||
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
|
||||
{
|
||||
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
|
||||
{
|
||||
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
|
||||
return;
|
||||
}
|
||||
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
|
||||
existingItem.AddQuantity(quantity);
|
||||
}
|
||||
public void RemoveEmptyItems()
|
||||
{
|
||||
_items.RemoveAll(i => i.Quantity == 0);
|
||||
}
|
||||
|
||||
public void RemoveEmptyItems()
|
||||
{
|
||||
_items.RemoveAll(i => i.Quantity == 0);
|
||||
}
|
||||
|
||||
public void SetNewBuyerId(string buyerId)
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
public void SetNewBuyerId(string buyerId)
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
using Ardalis.GuardClauses;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
public class BasketItem : BaseEntity
|
||||
{
|
||||
public class BasketItem : BaseEntity
|
||||
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Quantity { get; private set; }
|
||||
public int CatalogItemId { get; private set; }
|
||||
public int BasketId { get; private set; }
|
||||
|
||||
public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
|
||||
{
|
||||
CatalogItemId = catalogItemId;
|
||||
UnitPrice = unitPrice;
|
||||
SetQuantity(quantity);
|
||||
}
|
||||
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Quantity { get; private set; }
|
||||
public int CatalogItemId { get; private set; }
|
||||
public int BasketId { get; private set; }
|
||||
public void AddQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
|
||||
{
|
||||
CatalogItemId = catalogItemId;
|
||||
UnitPrice = unitPrice;
|
||||
SetQuantity(quantity);
|
||||
}
|
||||
Quantity += quantity;
|
||||
}
|
||||
|
||||
public void AddQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
public void SetQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
Quantity += quantity;
|
||||
}
|
||||
|
||||
public void SetQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
Quantity = quantity;
|
||||
}
|
||||
Quantity = quantity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Collections.Generic;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
|
||||
|
||||
public class Buyer : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Buyer : BaseEntity, IAggregateRoot
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
private Buyer()
|
||||
{
|
||||
public string IdentityGuid { get; private set; }
|
||||
// required by EF
|
||||
}
|
||||
|
||||
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
private Buyer()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Buyer(string identity) : this()
|
||||
{
|
||||
Guard.Against.NullOrEmpty(identity, nameof(identity));
|
||||
IdentityGuid = identity;
|
||||
}
|
||||
public Buyer(string identity) : this()
|
||||
{
|
||||
Guard.Against.NullOrEmpty(identity, nameof(identity));
|
||||
IdentityGuid = identity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
|
||||
|
||||
public class PaymentMethod : BaseEntity
|
||||
{
|
||||
public class PaymentMethod : BaseEntity
|
||||
{
|
||||
public string Alias { get; private set; }
|
||||
public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
public string Last4 { get; private set; }
|
||||
}
|
||||
public string Alias { get; private set; }
|
||||
public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
public string Last4 { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogBrand : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogBrand : BaseEntity, IAggregateRoot
|
||||
public string Brand { get; private set; }
|
||||
public CatalogBrand(string brand)
|
||||
{
|
||||
public string Brand { get; private set; }
|
||||
public CatalogBrand(string brand)
|
||||
{
|
||||
Brand = brand;
|
||||
}
|
||||
Brand = brand;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,65 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogItem : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogItem : BaseEntity, IAggregateRoot
|
||||
public string Name { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public decimal Price { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
public int CatalogTypeId { get; private set; }
|
||||
public CatalogType CatalogType { get; private set; }
|
||||
public int CatalogBrandId { get; private set; }
|
||||
public CatalogBrand CatalogBrand { get; private set; }
|
||||
|
||||
public CatalogItem(int catalogTypeId,
|
||||
int catalogBrandId,
|
||||
string description,
|
||||
string name,
|
||||
decimal price,
|
||||
string pictureUri)
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public decimal Price { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
public int CatalogTypeId { get; private set; }
|
||||
public CatalogType CatalogType { get; private set; }
|
||||
public int CatalogBrandId { get; private set; }
|
||||
public CatalogBrand CatalogBrand { get; private set; }
|
||||
|
||||
public CatalogItem(int catalogTypeId,
|
||||
int catalogBrandId,
|
||||
string description,
|
||||
string name,
|
||||
decimal price,
|
||||
string pictureUri)
|
||||
{
|
||||
CatalogTypeId = catalogTypeId;
|
||||
CatalogBrandId = catalogBrandId;
|
||||
Description = description;
|
||||
Name = name;
|
||||
Price = price;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
public void UpdateDetails(string name, string description, decimal price)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(name, nameof(name));
|
||||
Guard.Against.NullOrEmpty(description, nameof(description));
|
||||
Guard.Against.NegativeOrZero(price, nameof(price));
|
||||
|
||||
Name = name;
|
||||
Description = description;
|
||||
Price = price;
|
||||
}
|
||||
|
||||
public void UpdateBrand(int catalogBrandId)
|
||||
{
|
||||
Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId));
|
||||
CatalogBrandId = catalogBrandId;
|
||||
}
|
||||
|
||||
public void UpdateType(int catalogTypeId)
|
||||
{
|
||||
Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId));
|
||||
CatalogTypeId = catalogTypeId;
|
||||
}
|
||||
|
||||
public void UpdatePictureUri(string pictureName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pictureName))
|
||||
{
|
||||
PictureUri = string.Empty;
|
||||
return;
|
||||
}
|
||||
PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}";
|
||||
}
|
||||
CatalogTypeId = catalogTypeId;
|
||||
CatalogBrandId = catalogBrandId;
|
||||
Description = description;
|
||||
Name = name;
|
||||
Price = price;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDetails(string name, string description, decimal price)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(name, nameof(name));
|
||||
Guard.Against.NullOrEmpty(description, nameof(description));
|
||||
Guard.Against.NegativeOrZero(price, nameof(price));
|
||||
|
||||
Name = name;
|
||||
Description = description;
|
||||
Price = price;
|
||||
}
|
||||
|
||||
public void UpdateBrand(int catalogBrandId)
|
||||
{
|
||||
Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId));
|
||||
CatalogBrandId = catalogBrandId;
|
||||
}
|
||||
|
||||
public void UpdateType(int catalogTypeId)
|
||||
{
|
||||
Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId));
|
||||
CatalogTypeId = catalogTypeId;
|
||||
}
|
||||
|
||||
public void UpdatePictureUri(string pictureName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pictureName))
|
||||
{
|
||||
PictureUri = string.Empty;
|
||||
return;
|
||||
}
|
||||
PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogType : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogType : BaseEntity, IAggregateRoot
|
||||
public string Type { get; private set; }
|
||||
public CatalogType(string type)
|
||||
{
|
||||
public string Type { get; private set; }
|
||||
public CatalogType(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class Address // ValueObject
|
||||
{
|
||||
public class Address // ValueObject
|
||||
public string Street { get; private set; }
|
||||
|
||||
public string City { get; private set; }
|
||||
|
||||
public string State { get; private set; }
|
||||
|
||||
public string Country { get; private set; }
|
||||
|
||||
public string ZipCode { get; private set; }
|
||||
|
||||
private Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
public string Street { get; private set; }
|
||||
|
||||
public string City { get; private set; }
|
||||
|
||||
public string State { get; private set; }
|
||||
|
||||
public string Country { get; private set; }
|
||||
|
||||
public string ZipCode { get; private set; }
|
||||
|
||||
private Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
using Ardalis.GuardClauses;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a snapshot of the item that was ordered. If catalog item details change, details of
|
||||
/// the item that was part of a completed order should not change.
|
||||
/// </summary>
|
||||
public class CatalogItemOrdered // ValueObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a snapshot of the item that was ordered. If catalog item details change, details of
|
||||
/// the item that was part of a completed order should not change.
|
||||
/// </summary>
|
||||
public class CatalogItemOrdered // ValueObject
|
||||
public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri)
|
||||
{
|
||||
public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri)
|
||||
{
|
||||
Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue);
|
||||
Guard.Against.NullOrEmpty(productName, nameof(productName));
|
||||
Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri));
|
||||
Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue);
|
||||
Guard.Against.NullOrEmpty(productName, nameof(productName));
|
||||
Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri));
|
||||
|
||||
CatalogItemId = catalogItemId;
|
||||
ProductName = productName;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
private CatalogItemOrdered()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public int CatalogItemId { get; private set; }
|
||||
public string ProductName { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
CatalogItemId = catalogItemId;
|
||||
ProductName = productName;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
private CatalogItemOrdered()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public int CatalogItemId { get; private set; }
|
||||
public string ProductName { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class Order : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Order : BaseEntity, IAggregateRoot
|
||||
private Order()
|
||||
{
|
||||
private Order()
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
|
||||
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
|
||||
Guard.Against.Null(items, nameof(items));
|
||||
|
||||
BuyerId = buyerId;
|
||||
ShipToAddress = shipToAddress;
|
||||
_orderItems = items;
|
||||
}
|
||||
|
||||
public string BuyerId { get; private set; }
|
||||
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
|
||||
public Address ShipToAddress { get; private set; }
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method Order.AddOrderItem() which includes behavior.
|
||||
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
|
||||
|
||||
// Using List<>.AsReadOnly()
|
||||
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
|
||||
|
||||
public decimal Total()
|
||||
{
|
||||
var total = 0m;
|
||||
foreach (var item in _orderItems)
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
|
||||
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
|
||||
Guard.Against.Null(items, nameof(items));
|
||||
|
||||
BuyerId = buyerId;
|
||||
ShipToAddress = shipToAddress;
|
||||
_orderItems = items;
|
||||
}
|
||||
|
||||
public string BuyerId { get; private set; }
|
||||
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
|
||||
public Address ShipToAddress { get; private set; }
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method Order.AddOrderItem() which includes behavior.
|
||||
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
|
||||
|
||||
// Using List<>.AsReadOnly()
|
||||
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
|
||||
|
||||
public decimal Total()
|
||||
{
|
||||
var total = 0m;
|
||||
foreach (var item in _orderItems)
|
||||
{
|
||||
total += item.UnitPrice * item.Units;
|
||||
}
|
||||
return total;
|
||||
total += item.UnitPrice * item.Units;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class OrderItem : BaseEntity
|
||||
{
|
||||
public class OrderItem : BaseEntity
|
||||
public CatalogItemOrdered ItemOrdered { get; private set; }
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Units { get; private set; }
|
||||
|
||||
private OrderItem()
|
||||
{
|
||||
public CatalogItemOrdered ItemOrdered { get; private set; }
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Units { get; private set; }
|
||||
// required by EF
|
||||
}
|
||||
|
||||
private OrderItem()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
|
||||
{
|
||||
ItemOrdered = itemOrdered;
|
||||
UnitPrice = unitPrice;
|
||||
Units = units;
|
||||
}
|
||||
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
|
||||
{
|
||||
ItemOrdered = itemOrdered;
|
||||
UnitPrice = unitPrice;
|
||||
Units = units;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class BasketNotFoundException : Exception
|
||||
{
|
||||
public class BasketNotFoundException : Exception
|
||||
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
|
||||
{
|
||||
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
{
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class DuplicateException : Exception
|
||||
public class DuplicateException : Exception
|
||||
{
|
||||
public DuplicateException(string message) : base(message)
|
||||
{
|
||||
public DuplicateException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class EmptyBasketOnCheckoutException : Exception
|
||||
{
|
||||
public class EmptyBasketOnCheckoutException : Exception
|
||||
public EmptyBasketOnCheckoutException()
|
||||
: base($"Basket cannot have 0 items on checkout")
|
||||
{
|
||||
public EmptyBasketOnCheckoutException()
|
||||
: base($"Basket cannot have 0 items on checkout")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public EmptyBasketOnCheckoutException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
public EmptyBasketOnCheckoutException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
namespace Ardalis.GuardClauses
|
||||
namespace Ardalis.GuardClauses;
|
||||
|
||||
public static class BasketGuards
|
||||
{
|
||||
public static class BasketGuards
|
||||
public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket)
|
||||
{
|
||||
public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket)
|
||||
{
|
||||
if (basket == null)
|
||||
throw new BasketNotFoundException(basketId);
|
||||
}
|
||||
|
||||
public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection<BasketItem> basketItems)
|
||||
{
|
||||
if (!basketItems.Any())
|
||||
throw new EmptyBasketOnCheckoutException();
|
||||
}
|
||||
if (basket == null)
|
||||
throw new BasketNotFoundException(basketId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection<BasketItem> basketItems)
|
||||
{
|
||||
if (!basketItems.Any())
|
||||
throw new EmptyBasketOnCheckoutException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.eShopWeb
|
||||
namespace Microsoft.eShopWeb;
|
||||
|
||||
public static class JsonExtensions
|
||||
{
|
||||
public static class JsonExtensions
|
||||
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
public static T FromJson<T>(this string json) =>
|
||||
JsonSerializer.Deserialize<T>(json, _jsonOptions);
|
||||
public static T FromJson<T>(this string json) =>
|
||||
JsonSerializer.Deserialize<T>(json, _jsonOptions);
|
||||
|
||||
public static string ToJson<T>(this T obj) =>
|
||||
JsonSerializer.Serialize<T>(obj, _jsonOptions);
|
||||
}
|
||||
public static string ToJson<T>(this T obj) =>
|
||||
JsonSerializer.Serialize<T>(obj, _jsonOptions);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IAggregateRoot
|
||||
{ }
|
||||
}
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IAggregateRoot
|
||||
{ }
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IAppLogger<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IAppLogger<T>
|
||||
{
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogWarning(string message, params object[] args);
|
||||
}
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogWarning(string message, params object[] args);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IBasketQueryService
|
||||
{
|
||||
public interface IBasketQueryService
|
||||
{
|
||||
Task<int> CountTotalBasketItems(string username);
|
||||
}
|
||||
Task<int> CountTotalBasketItems(string username);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IBasketService
|
||||
{
|
||||
public interface IBasketService
|
||||
{
|
||||
Task TransferBasketAsync(string anonymousId, string userName);
|
||||
Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1);
|
||||
Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities);
|
||||
Task DeleteBasketAsync(int basketId);
|
||||
}
|
||||
Task TransferBasketAsync(string anonymousId, string userName);
|
||||
Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1);
|
||||
Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities);
|
||||
Task DeleteBasketAsync(int basketId);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
{
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendEmailAsync(string email, string subject, string message);
|
||||
}
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendEmailAsync(string email, string subject, string message);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IOrderService
|
||||
{
|
||||
public interface IOrderService
|
||||
{
|
||||
Task CreateOrderAsync(int basketId, Address shippingAddress);
|
||||
}
|
||||
Task CreateOrderAsync(int basketId, Address shippingAddress);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Ardalis.Specification;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Ardalis.Specification;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface ITokenClaimsService
|
||||
{
|
||||
public interface ITokenClaimsService
|
||||
{
|
||||
Task<string> GetTokenAsync(string userName);
|
||||
}
|
||||
Task<string> GetTokenAsync(string userName);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IUriComposer
|
||||
{
|
||||
public interface IUriComposer
|
||||
{
|
||||
string ComposePicUri(string uriTemplate);
|
||||
}
|
||||
string ComposePicUri(string uriTemplate);
|
||||
}
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class BasketService : IBasketService
|
||||
{
|
||||
public class BasketService : IBasketService
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IAppLogger<BasketService> _logger;
|
||||
|
||||
public BasketService(IRepository<Basket> basketRepository,
|
||||
IAppLogger<BasketService> logger)
|
||||
{
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IAppLogger<BasketService> _logger;
|
||||
_basketRepository = basketRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public BasketService(IRepository<Basket> basketRepository,
|
||||
IAppLogger<BasketService> logger)
|
||||
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(username);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
|
||||
if (basket == null)
|
||||
{
|
||||
_basketRepository = basketRepository;
|
||||
_logger = logger;
|
||||
basket = new Basket(username);
|
||||
await _basketRepository.AddAsync(basket);
|
||||
}
|
||||
|
||||
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(username);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
basket.AddItem(catalogItemId, price, quantity);
|
||||
|
||||
if (basket == null)
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task DeleteBasketAsync(int basketId)
|
||||
{
|
||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||
await _basketRepository.DeleteAsync(basket);
|
||||
}
|
||||
|
||||
public async Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities)
|
||||
{
|
||||
Guard.Against.Null(quantities, nameof(quantities));
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
|
||||
foreach (var item in basket.Items)
|
||||
{
|
||||
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
|
||||
{
|
||||
basket = new Basket(username);
|
||||
await _basketRepository.AddAsync(basket);
|
||||
if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
|
||||
item.SetQuantity(quantity);
|
||||
}
|
||||
|
||||
basket.AddItem(catalogItemId, price, quantity);
|
||||
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
basket.RemoveEmptyItems();
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task DeleteBasketAsync(int basketId)
|
||||
public async Task TransferBasketAsync(string anonymousId, string userName)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId));
|
||||
Guard.Against.NullOrEmpty(userName, nameof(userName));
|
||||
var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId);
|
||||
var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec);
|
||||
if (anonymousBasket == null) return;
|
||||
var userBasketSpec = new BasketWithItemsSpecification(userName);
|
||||
var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec);
|
||||
if (userBasket == null)
|
||||
{
|
||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||
await _basketRepository.DeleteAsync(basket);
|
||||
userBasket = new Basket(userName);
|
||||
await _basketRepository.AddAsync(userBasket);
|
||||
}
|
||||
|
||||
public async Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities)
|
||||
foreach (var item in anonymousBasket.Items)
|
||||
{
|
||||
Guard.Against.Null(quantities, nameof(quantities));
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
|
||||
foreach (var item in basket.Items)
|
||||
{
|
||||
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
|
||||
{
|
||||
if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
|
||||
item.SetQuantity(quantity);
|
||||
}
|
||||
}
|
||||
basket.RemoveEmptyItems();
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task TransferBasketAsync(string anonymousId, string userName)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId));
|
||||
Guard.Against.NullOrEmpty(userName, nameof(userName));
|
||||
var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId);
|
||||
var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec);
|
||||
if (anonymousBasket == null) return;
|
||||
var userBasketSpec = new BasketWithItemsSpecification(userName);
|
||||
var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec);
|
||||
if (userBasket == null)
|
||||
{
|
||||
userBasket = new Basket(userName);
|
||||
await _basketRepository.AddAsync(userBasket);
|
||||
}
|
||||
foreach (var item in anonymousBasket.Items)
|
||||
{
|
||||
userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity);
|
||||
}
|
||||
await _basketRepository.UpdateAsync(userBasket);
|
||||
await _basketRepository.DeleteAsync(anonymousBasket);
|
||||
userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity);
|
||||
}
|
||||
await _basketRepository.UpdateAsync(userBasket);
|
||||
await _basketRepository.DeleteAsync(anonymousBasket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,53 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class OrderService : IOrderService
|
||||
{
|
||||
public class OrderService : IOrderService
|
||||
private readonly IRepository<Order> _orderRepository;
|
||||
private readonly IUriComposer _uriComposer;
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IRepository<CatalogItem> _itemRepository;
|
||||
|
||||
public OrderService(IRepository<Basket> basketRepository,
|
||||
IRepository<CatalogItem> itemRepository,
|
||||
IRepository<Order> orderRepository,
|
||||
IUriComposer uriComposer)
|
||||
{
|
||||
private readonly IRepository<Order> _orderRepository;
|
||||
private readonly IUriComposer _uriComposer;
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IRepository<CatalogItem> _itemRepository;
|
||||
_orderRepository = orderRepository;
|
||||
_uriComposer = uriComposer;
|
||||
_basketRepository = basketRepository;
|
||||
_itemRepository = itemRepository;
|
||||
}
|
||||
|
||||
public OrderService(IRepository<Basket> basketRepository,
|
||||
IRepository<CatalogItem> itemRepository,
|
||||
IRepository<Order> orderRepository,
|
||||
IUriComposer uriComposer)
|
||||
public async Task CreateOrderAsync(int basketId, Address shippingAddress)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
Guard.Against.EmptyBasketOnCheckout(basket.Items);
|
||||
|
||||
var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray());
|
||||
var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification);
|
||||
|
||||
var items = basket.Items.Select(basketItem =>
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
_uriComposer = uriComposer;
|
||||
_basketRepository = basketRepository;
|
||||
_itemRepository = itemRepository;
|
||||
}
|
||||
var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId);
|
||||
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
|
||||
var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity);
|
||||
return orderItem;
|
||||
}).ToList();
|
||||
|
||||
public async Task CreateOrderAsync(int basketId, Address shippingAddress)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
var order = new Order(basket.BuyerId, shippingAddress, items);
|
||||
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
Guard.Against.EmptyBasketOnCheckout(basket.Items);
|
||||
|
||||
var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray());
|
||||
var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification);
|
||||
|
||||
var items = basket.Items.Select(basketItem =>
|
||||
{
|
||||
var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId);
|
||||
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
|
||||
var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity);
|
||||
return orderItem;
|
||||
}).ToList();
|
||||
|
||||
var order = new Order(basket.BuyerId, shippingAddress, items);
|
||||
|
||||
await _orderRepository.AddAsync(order);
|
||||
}
|
||||
await _orderRepository.AddAsync(order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class UriComposer : IUriComposer
|
||||
{
|
||||
public class UriComposer : IUriComposer
|
||||
private readonly CatalogSettings _catalogSettings;
|
||||
|
||||
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;
|
||||
|
||||
public string ComposePicUri(string uriTemplate)
|
||||
{
|
||||
private readonly CatalogSettings _catalogSettings;
|
||||
|
||||
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;
|
||||
|
||||
public string ComposePicUri(string uriTemplate)
|
||||
{
|
||||
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
|
||||
}
|
||||
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
{
|
||||
public sealed class BasketWithItemsSpecification : Specification<Basket>, ISingleResultSpecification
|
||||
{
|
||||
public BasketWithItemsSpecification(int basketId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.Id == basketId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public BasketWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.BuyerId == buyerId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
public sealed class BasketWithItemsSpecification : Specification<Basket>, ISingleResultSpecification
|
||||
{
|
||||
public BasketWithItemsSpecification(int basketId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.Id == basketId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
|
||||
public BasketWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.BuyerId == buyerId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
|
||||
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
|
||||
: base()
|
||||
{
|
||||
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
|
||||
: base()
|
||||
if (take == 0)
|
||||
{
|
||||
if (take == 0)
|
||||
{
|
||||
take = int.MaxValue;
|
||||
}
|
||||
Query
|
||||
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId))
|
||||
.Skip(skip).Take(take);
|
||||
take = int.MaxValue;
|
||||
}
|
||||
Query
|
||||
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId))
|
||||
.Skip(skip).Take(take);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogFilterSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogFilterSpecification : Specification<CatalogItem>
|
||||
public CatalogFilterSpecification(int? brandId, int? typeId)
|
||||
{
|
||||
public CatalogFilterSpecification(int? brandId, int? typeId)
|
||||
{
|
||||
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId));
|
||||
}
|
||||
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogItemNameSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogItemNameSpecification : Specification<CatalogItem>
|
||||
public CatalogItemNameSpecification(string catalogItemName)
|
||||
{
|
||||
public CatalogItemNameSpecification(string catalogItemName)
|
||||
{
|
||||
Query.Where(item => catalogItemName == item.Name);
|
||||
}
|
||||
Query.Where(item => catalogItemName == item.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogItemsSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogItemsSpecification : Specification<CatalogItem>
|
||||
public CatalogItemsSpecification(params int[] ids)
|
||||
{
|
||||
public CatalogItemsSpecification(params int[] ids)
|
||||
{
|
||||
Query.Where(c => ids.Contains(c.Id));
|
||||
}
|
||||
Query.Where(c => ids.Contains(c.Id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CustomerOrdersWithItemsSpecification : Specification<Order>
|
||||
{
|
||||
public class CustomerOrdersWithItemsSpecification : Specification<Order>
|
||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||
{
|
||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query.Where(o => o.BuyerId == buyerId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
Query.Where(o => o.BuyerId == buyerId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class OrderWithItemsByIdSpec : Specification<Order>, ISingleResultSpecification
|
||||
{
|
||||
public class OrderWithItemsByIdSpec : Specification<Order>, ISingleResultSpecification
|
||||
public OrderWithItemsByIdSpec(int orderId)
|
||||
{
|
||||
public OrderWithItemsByIdSpec(int orderId)
|
||||
{
|
||||
Query
|
||||
.Where(order => order.Id == orderId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
Query
|
||||
.Where(order => order.Id == orderId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
using BlazorShared.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Authorization;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin
|
||||
namespace BlazorAdmin;
|
||||
|
||||
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
public class CustomAuthStateProvider : AuthenticationStateProvider
|
||||
// TODO: Get Default Cache Duration from Config
|
||||
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CustomAuthStateProvider> _logger;
|
||||
|
||||
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
||||
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
public CustomAuthStateProvider(HttpClient httpClient,
|
||||
ILogger<CustomAuthStateProvider> logger)
|
||||
{
|
||||
// TODO: Get Default Cache Duration from Config
|
||||
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CustomAuthStateProvider> _logger;
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
return new AuthenticationState(await GetUser(useCache: true));
|
||||
}
|
||||
|
||||
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
|
||||
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
|
||||
|
||||
public CustomAuthStateProvider(HttpClient httpClient,
|
||||
ILogger<CustomAuthStateProvider> logger)
|
||||
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
return new AuthenticationState(await GetUser(useCache: true));
|
||||
}
|
||||
|
||||
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
|
||||
{
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
_cachedUser = await FetchUser();
|
||||
_userLastCheck = now;
|
||||
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
private async Task<ClaimsPrincipal> FetchUser()
|
||||
_cachedUser = await FetchUser();
|
||||
_userLastCheck = now;
|
||||
|
||||
return _cachedUser;
|
||||
}
|
||||
|
||||
private async Task<ClaimsPrincipal> FetchUser()
|
||||
{
|
||||
UserInfo user = null;
|
||||
|
||||
try
|
||||
{
|
||||
UserInfo user = null;
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Fetching user details from web api.");
|
||||
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
_logger.LogWarning(exc, "Fetching user failed.");
|
||||
}
|
||||
|
||||
if (user == null || !user.IsAuthenticated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(
|
||||
nameof(CustomAuthStateProvider),
|
||||
user.NameClaimType,
|
||||
user.RoleClaimType);
|
||||
|
||||
if (user.Claims != null)
|
||||
{
|
||||
foreach (var claim in user.Claims)
|
||||
{
|
||||
identity.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
|
||||
|
||||
return new ClaimsPrincipal(identity);
|
||||
_logger.LogInformation("Fetching user details from web api.");
|
||||
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
_logger.LogWarning(exc, "Fetching user failed.");
|
||||
}
|
||||
|
||||
if (user == null || !user.IsAuthenticated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(
|
||||
nameof(CustomAuthStateProvider),
|
||||
user.NameClaimType,
|
||||
user.RoleClaimType);
|
||||
|
||||
if (user.Claims != null)
|
||||
{
|
||||
foreach (var claim in user.Claims)
|
||||
{
|
||||
identity.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
|
||||
|
||||
return new ClaimsPrincipal(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class BlazorComponent : ComponentBase
|
||||
{
|
||||
public class BlazorComponent : ComponentBase
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class BlazorLayoutComponent : LayoutComponentBase
|
||||
{
|
||||
public class BlazorLayoutComponent : LayoutComponentBase
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_refresh.RefreshRequested += DoRefresh;
|
||||
base.OnInitialized();
|
||||
}
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
_refresh.CallRequestRefresh();
|
||||
}
|
||||
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
private void DoRefresh()
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
internal sealed class RefreshBroadcast
|
||||
{
|
||||
internal sealed class RefreshBroadcast
|
||||
private static readonly Lazy<RefreshBroadcast>
|
||||
Lazy =
|
||||
new Lazy<RefreshBroadcast>
|
||||
(() => new RefreshBroadcast());
|
||||
|
||||
public static RefreshBroadcast Instance => Lazy.Value;
|
||||
|
||||
private RefreshBroadcast()
|
||||
{
|
||||
private static readonly Lazy<RefreshBroadcast>
|
||||
Lazy =
|
||||
new Lazy<RefreshBroadcast>
|
||||
(() => new RefreshBroadcast());
|
||||
}
|
||||
|
||||
public static RefreshBroadcast Instance => Lazy.Value;
|
||||
|
||||
private RefreshBroadcast()
|
||||
{
|
||||
}
|
||||
|
||||
public event Action RefreshRequested;
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
RefreshRequested?.Invoke();
|
||||
}
|
||||
public event Action RefreshRequested;
|
||||
public void CallRequestRefresh()
|
||||
{
|
||||
RefreshRequested?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,87 @@
|
||||
using BlazorAdmin.Services;
|
||||
using System;
|
||||
using BlazorAdmin.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Helpers
|
||||
namespace BlazorAdmin.Helpers;
|
||||
|
||||
public class ToastComponent : ComponentBase, IDisposable
|
||||
{
|
||||
public class ToastComponent : ComponentBase, IDisposable
|
||||
[Inject]
|
||||
ToastService ToastService
|
||||
{
|
||||
[Inject]
|
||||
ToastService ToastService
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Heading
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected bool IsVisible
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string BackgroundCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string IconCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
ToastService.OnShow += ShowToast;
|
||||
ToastService.OnHide += HideToast;
|
||||
}
|
||||
private void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
BuildToastSettings(level, message);
|
||||
IsVisible = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideToast()
|
||||
{
|
||||
IsVisible = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void BuildToastSettings(ToastLevel level, string message)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Heading
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected bool IsVisible
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string BackgroundCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected string IconCssClass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
ToastService.OnShow += ShowToast;
|
||||
ToastService.OnHide += HideToast;
|
||||
}
|
||||
private void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
BuildToastSettings(level, message);
|
||||
IsVisible = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideToast()
|
||||
{
|
||||
IsVisible = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void BuildToastSettings(ToastLevel level, string message)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ToastLevel.Info:
|
||||
BackgroundCssClass = "bg-info";
|
||||
IconCssClass = "info";
|
||||
Heading = "Info";
|
||||
break;
|
||||
case ToastLevel.Success:
|
||||
BackgroundCssClass = "bg-success";
|
||||
IconCssClass = "check";
|
||||
Heading = "Success";
|
||||
break;
|
||||
case ToastLevel.Warning:
|
||||
BackgroundCssClass = "bg-warning";
|
||||
IconCssClass = "exclamation";
|
||||
Heading = "Warning";
|
||||
break;
|
||||
case ToastLevel.Error:
|
||||
BackgroundCssClass = "bg-danger";
|
||||
IconCssClass = "times";
|
||||
Heading = "Error";
|
||||
break;
|
||||
}
|
||||
Message = message;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
ToastService.OnShow -= ShowToast;
|
||||
case ToastLevel.Info:
|
||||
BackgroundCssClass = "bg-info";
|
||||
IconCssClass = "info";
|
||||
Heading = "Info";
|
||||
break;
|
||||
case ToastLevel.Success:
|
||||
BackgroundCssClass = "bg-success";
|
||||
IconCssClass = "check";
|
||||
Heading = "Success";
|
||||
break;
|
||||
case ToastLevel.Warning:
|
||||
BackgroundCssClass = "bg-warning";
|
||||
IconCssClass = "exclamation";
|
||||
Heading = "Warning";
|
||||
break;
|
||||
case ToastLevel.Error:
|
||||
BackgroundCssClass = "bg-danger";
|
||||
IconCssClass = "times";
|
||||
Heading = "Error";
|
||||
break;
|
||||
}
|
||||
Message = message;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
ToastService.OnShow -= ShowToast;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Cookies
|
||||
{
|
||||
public class Cookies
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Cookies(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Cookies(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
public async Task DeleteCookie(string name)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.DeleteCookie, name);
|
||||
}
|
||||
|
||||
public async Task DeleteCookie(string name)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.DeleteCookie, name);
|
||||
}
|
||||
|
||||
public async Task<string> GetCookie(string name)
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.GetCookie, name);
|
||||
}
|
||||
public async Task<string> GetCookie(string name)
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.GetCookie, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Css
|
||||
{
|
||||
public class Css
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Css(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Css(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task ShowBodyOverflow()
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.ShowBodyOverflow);
|
||||
}
|
||||
public async Task ShowBodyOverflow()
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.ShowBodyOverflow);
|
||||
}
|
||||
|
||||
public async Task<string> HideBodyOverflow()
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.HideBodyOverflow);
|
||||
}
|
||||
public async Task<string> HideBodyOverflow()
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.HideBodyOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public static class JSInteropConstants
|
||||
{
|
||||
public static class JSInteropConstants
|
||||
{
|
||||
public static string DeleteCookie => "deleteCookie";
|
||||
public static string GetCookie => "getCookie";
|
||||
public static string RouteOutside => "routeOutside";
|
||||
public static string HideBodyOverflow => "hideBodyOverflow";
|
||||
public static string ShowBodyOverflow => "showBodyOverflow";
|
||||
}
|
||||
public static string DeleteCookie => "deleteCookie";
|
||||
public static string GetCookie => "getCookie";
|
||||
public static string RouteOutside => "routeOutside";
|
||||
public static string HideBodyOverflow => "hideBodyOverflow";
|
||||
public static string ShowBodyOverflow => "showBodyOverflow";
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorAdmin.JavaScript
|
||||
namespace BlazorAdmin.JavaScript;
|
||||
|
||||
public class Route
|
||||
{
|
||||
public class Route
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Route(IJSRuntime jsRuntime)
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Route(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task RouteOutside(string path)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.RouteOutside, path);
|
||||
}
|
||||
public async Task RouteOutside(string path)
|
||||
{
|
||||
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.RouteOutside, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,68 @@
|
||||
using BlazorAdmin.Helpers;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorAdmin.Helpers;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin.Pages.CatalogItemPage
|
||||
namespace BlazorAdmin.Pages.CatalogItemPage;
|
||||
|
||||
public partial class List : BlazorComponent
|
||||
{
|
||||
public partial class List : BlazorComponent
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogItemService CatalogItemService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogBrand> CatalogBrandService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogType> CatalogTypeService { get; set; }
|
||||
|
||||
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
||||
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
||||
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
||||
|
||||
private Edit EditComponent { get; set; }
|
||||
private Delete DeleteComponent { get; set; }
|
||||
private Details DetailsComponent { get; set; }
|
||||
private Create CreateComponent { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogItemService CatalogItemService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogBrand> CatalogBrandService { get; set; }
|
||||
|
||||
[Microsoft.AspNetCore.Components.Inject]
|
||||
public ICatalogLookupDataService<CatalogType> CatalogTypeService { get; set; }
|
||||
|
||||
private List<CatalogItem> catalogItems = new List<CatalogItem>();
|
||||
private List<CatalogType> catalogTypes = new List<CatalogType>();
|
||||
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
|
||||
|
||||
private Edit EditComponent { get; set; }
|
||||
private Delete DeleteComponent { get; set; }
|
||||
private Details DetailsComponent { get; set; }
|
||||
private Create CreateComponent { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
catalogTypes = await CatalogTypeService.List();
|
||||
catalogBrands = await CatalogBrandService.List();
|
||||
|
||||
CallRequestRefresh();
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async void DetailsClick(int id)
|
||||
{
|
||||
await DetailsComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task CreateClick()
|
||||
{
|
||||
await CreateComponent.Open();
|
||||
}
|
||||
|
||||
private async Task EditClick(int id)
|
||||
{
|
||||
await EditComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task DeleteClick(int id)
|
||||
{
|
||||
await DeleteComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task ReloadCatalogItems()
|
||||
if (firstRender)
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
StateHasChanged();
|
||||
catalogTypes = await CatalogTypeService.List();
|
||||
catalogBrands = await CatalogBrandService.List();
|
||||
|
||||
CallRequestRefresh();
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async void DetailsClick(int id)
|
||||
{
|
||||
await DetailsComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task CreateClick()
|
||||
{
|
||||
await CreateComponent.Open();
|
||||
}
|
||||
|
||||
private async Task EditClick(int id)
|
||||
{
|
||||
await EditComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task DeleteClick(int id)
|
||||
{
|
||||
await DeleteComponent.Open(id);
|
||||
}
|
||||
|
||||
private async Task ReloadCatalogItems()
|
||||
{
|
||||
catalogItems = await CatalogItemService.List();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorAdmin.Services;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared;
|
||||
@@ -7,50 +10,46 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin
|
||||
namespace BlazorAdmin;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public class Program
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#admin");
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#admin");
|
||||
|
||||
var baseUrlConfig = new BaseUrlConfiguration();
|
||||
builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||
builder.Services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||
var baseUrlConfig = new BaseUrlConfiguration();
|
||||
builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
|
||||
builder.Services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
|
||||
builder.Services.AddScoped<ToastService>();
|
||||
builder.Services.AddScoped<HttpService>();
|
||||
builder.Services.AddScoped<ToastService>();
|
||||
builder.Services.AddScoped<HttpService>();
|
||||
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
|
||||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
|
||||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
|
||||
|
||||
builder.Services.AddBlazorServices();
|
||||
builder.Services.AddBlazorServices();
|
||||
|
||||
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||
|
||||
await ClearLocalStorageCache(builder.Services);
|
||||
await ClearLocalStorageCache(builder.Services);
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
|
||||
private static async Task ClearLocalStorageCache(IServiceCollection services)
|
||||
{
|
||||
var sp = services.BuildServiceProvider();
|
||||
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
|
||||
private static async Task ClearLocalStorageCache(IServiceCollection services)
|
||||
{
|
||||
var sp = services.BuildServiceProvider();
|
||||
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
|
||||
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
|
||||
}
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
|
||||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CacheEntry<T>
|
||||
{
|
||||
public class CacheEntry<T>
|
||||
public CacheEntry(T item)
|
||||
{
|
||||
public CacheEntry(T item)
|
||||
{
|
||||
Value = item;
|
||||
}
|
||||
public CacheEntry()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public T Value { get; set; }
|
||||
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||
Value = item;
|
||||
}
|
||||
public CacheEntry()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public T Value { get; set; }
|
||||
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
@@ -1,114 +1,113 @@
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CachedCatalogItemServiceDecorator : ICatalogItemService
|
||||
{
|
||||
public class CachedCatalogItemServiceDecorator : ICatalogItemService
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogItemService _catalogItemService;
|
||||
private ILogger<CachedCatalogItemServiceDecorator> _logger;
|
||||
|
||||
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogItemService catalogItemService,
|
||||
ILogger<CachedCatalogItemServiceDecorator> logger)
|
||||
{
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogItemService _catalogItemService;
|
||||
private ILogger<CachedCatalogItemServiceDecorator> _logger;
|
||||
_localStorageService = localStorageService;
|
||||
_catalogItemService = catalogItemService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogItemService catalogItemService,
|
||||
ILogger<CachedCatalogItemServiceDecorator> logger)
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
_localStorageService = localStorageService;
|
||||
_catalogItemService = catalogItemService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
|
||||
var items = await _catalogItemService.ListPaged(pageSize);
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
var items = await _catalogItemService.ListPaged(pageSize);
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
string key = "items";
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||
_logger.LogInformation("Loading items from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Create(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return items;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
return (await List()).FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Edit(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Create(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int id)
|
||||
{
|
||||
var result = await _catalogItemService.Delete(id);
|
||||
await RefreshLocalStorageList();
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
var result = await _catalogItemService.Edit(catalogItem);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task RefreshLocalStorageList()
|
||||
{
|
||||
string key = "items";
|
||||
public async Task<string> Delete(int id)
|
||||
{
|
||||
var result = await _catalogItemService.Delete(id);
|
||||
await RefreshLocalStorageList();
|
||||
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task RefreshLocalStorageList()
|
||||
{
|
||||
string key = "items";
|
||||
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
var items = await _catalogItemService.List();
|
||||
var entry = new CacheEntry<List<CatalogItem>>(items);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
using Blazored.LocalStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Blazored.LocalStorage;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
{
|
||||
public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogLookupDataService<TLookupData, TReponse> _catalogTypeService;
|
||||
private ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> _logger;
|
||||
|
||||
public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogLookupDataService<TLookupData, TReponse> catalogTypeService,
|
||||
ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> logger)
|
||||
{
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly CatalogLookupDataService<TLookupData, TReponse> _catalogTypeService;
|
||||
private ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> _logger;
|
||||
_localStorageService = localStorageService;
|
||||
_catalogTypeService = catalogTypeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService,
|
||||
CatalogLookupDataService<TLookupData, TReponse> catalogTypeService,
|
||||
ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> logger)
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
string key = typeof(TLookupData).Name;
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<TLookupData>>>(key);
|
||||
if (cacheEntry != null)
|
||||
{
|
||||
_localStorageService = localStorageService;
|
||||
_catalogTypeService = catalogTypeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
string key = typeof(TLookupData).Name;
|
||||
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<TLookupData>>>(key);
|
||||
if (cacheEntry != null)
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
_logger.LogInformation($"Loading {key} from local storage.");
|
||||
if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow)
|
||||
{
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Cache expired; removing {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
return cacheEntry.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Cache expired; removing {key} from local storage.");
|
||||
await _localStorageService.RemoveItemAsync(key);
|
||||
}
|
||||
|
||||
var types = await _catalogTypeService.List();
|
||||
var entry = new CacheEntry<List<TLookupData>>(types);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return types;
|
||||
}
|
||||
|
||||
var types = await _catalogTypeService.List();
|
||||
var entry = new CacheEntry<List<TLookupData>>(types);
|
||||
await _localStorageService.SetItemAsync(key, entry);
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,96 @@
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CatalogItemService : ICatalogItemService
|
||||
{
|
||||
public class CatalogItemService : ICatalogItemService
|
||||
private readonly ICatalogLookupDataService<CatalogBrand> _brandService;
|
||||
private readonly ICatalogLookupDataService<CatalogType> _typeService;
|
||||
private readonly HttpService _httpService;
|
||||
private readonly ILogger<CatalogItemService> _logger;
|
||||
|
||||
public CatalogItemService(ICatalogLookupDataService<CatalogBrand> brandService,
|
||||
ICatalogLookupDataService<CatalogType> typeService,
|
||||
HttpService httpService,
|
||||
ILogger<CatalogItemService> logger)
|
||||
{
|
||||
private readonly ICatalogLookupDataService<CatalogBrand> _brandService;
|
||||
private readonly ICatalogLookupDataService<CatalogType> _typeService;
|
||||
private readonly HttpService _httpService;
|
||||
private readonly ILogger<CatalogItemService> _logger;
|
||||
_brandService = brandService;
|
||||
_typeService = typeService;
|
||||
_httpService = httpService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public CatalogItemService(ICatalogLookupDataService<CatalogBrand> brandService,
|
||||
ICatalogLookupDataService<CatalogType> typeService,
|
||||
HttpService httpService,
|
||||
ILogger<CatalogItemService> logger)
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
{
|
||||
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
|
||||
return response?.CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int catalogItemId)
|
||||
{
|
||||
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{id}");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemGetTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var catalogItem = itemGetTask.Result.CatalogItem;
|
||||
catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name;
|
||||
catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name;
|
||||
return catalogItem;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=10");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
_brandService = brandService;
|
||||
_typeService = typeService;
|
||||
_httpService = httpService;
|
||||
_logger = logger;
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
|
||||
return response?.CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
|
||||
{
|
||||
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
|
||||
}
|
||||
|
||||
public async Task<string> Delete(int catalogItemId)
|
||||
{
|
||||
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
|
||||
}
|
||||
|
||||
public async Task<CatalogItem> GetById(int id)
|
||||
{
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"catalog-items/{id}");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemGetTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var catalogItem = itemGetTask.Result.CatalogItem;
|
||||
catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name;
|
||||
catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name;
|
||||
return catalogItem;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> ListPaged(int pageSize)
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items?PageSize=10");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<List<CatalogItem>> List()
|
||||
{
|
||||
_logger.LogInformation("Fetching catalog items from API.");
|
||||
|
||||
var brandListTask = _brandService.List();
|
||||
var typeListTask = _typeService.List();
|
||||
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"catalog-items");
|
||||
await Task.WhenAll(brandListTask, typeListTask, itemListTask);
|
||||
var brands = brandListTask.Result;
|
||||
var types = typeListTask.Result;
|
||||
var items = itemListTask.Result.CatalogItems;
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name;
|
||||
item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
using BlazorShared;
|
||||
using BlazorShared.Attributes;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared;
|
||||
using BlazorShared.Attributes;
|
||||
using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class CatalogLookupDataService<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
{
|
||||
public class CatalogLookupDataService<TLookupData, TReponse>
|
||||
: ICatalogLookupDataService<TLookupData>
|
||||
where TLookupData : LookupData
|
||||
where TReponse : ILookupDataResponse<TLookupData>
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CatalogLookupDataService<TLookupData, TReponse>> _logger;
|
||||
private readonly string _apiUrl;
|
||||
|
||||
public CatalogLookupDataService(HttpClient httpClient,
|
||||
BaseUrlConfiguration baseUrlConfiguration,
|
||||
ILogger<CatalogLookupDataService<TLookupData, TReponse>> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CatalogLookupDataService<TLookupData, TReponse>> _logger;
|
||||
private readonly string _apiUrl;
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
var endpointName = typeof(TLookupData).GetCustomAttribute<EndpointAttribute>().Name;
|
||||
_logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}");
|
||||
|
||||
public CatalogLookupDataService(HttpClient httpClient,
|
||||
BaseUrlConfiguration baseUrlConfiguration,
|
||||
ILogger<CatalogLookupDataService<TLookupData, TReponse>> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
public async Task<List<TLookupData>> List()
|
||||
{
|
||||
var endpointName = typeof(TLookupData).GetCustomAttribute<EndpointAttribute>().Name;
|
||||
_logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}");
|
||||
|
||||
var response = await _httpClient.GetFromJsonAsync<TReponse>($"{_apiUrl}{endpointName}");
|
||||
return response.List;
|
||||
}
|
||||
var response = await _httpClient.GetFromJsonAsync<TReponse>($"{_apiUrl}{endpointName}");
|
||||
return response.List;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +1,95 @@
|
||||
using BlazorShared;
|
||||
using BlazorShared.Models;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared;
|
||||
using BlazorShared.Models;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class HttpService
|
||||
{
|
||||
public class HttpService
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ToastService _toastService;
|
||||
private readonly string _apiUrl;
|
||||
|
||||
|
||||
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService)
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ToastService _toastService;
|
||||
private readonly string _apiUrl;
|
||||
_httpClient = httpClient;
|
||||
_toastService = toastService;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
}
|
||||
|
||||
|
||||
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService)
|
||||
public async Task<T> HttpGet<T>(string uri)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.GetAsync($"{_apiUrl}{uri}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_toastService = toastService;
|
||||
_apiUrl = baseUrlConfiguration.ApiBase;
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<T> HttpGet<T>(string uri)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.GetAsync($"{_apiUrl}{uri}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
public async Task<T> HttpDelete<T>(string uri, int id)
|
||||
where T : class
|
||||
{
|
||||
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<T> HttpDelete<T>(string uri, int id)
|
||||
where T : class
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPost<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}");
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPost<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPut<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_toastService.ShowToast("Error", ToastLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
private StringContent ToJson(object obj)
|
||||
{
|
||||
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage result)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
public async Task<T> HttpPut<T>(string uri, object dataToSend)
|
||||
where T : class
|
||||
{
|
||||
var content = ToJson(dataToSend);
|
||||
|
||||
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_toastService.ShowToast("Error", ToastLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHttpResponseMessage<T>(result);
|
||||
}
|
||||
|
||||
private StringContent ToJson(object obj)
|
||||
{
|
||||
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage result)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
using System;
|
||||
using System.Timers;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
{
|
||||
public enum ToastLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
namespace BlazorAdmin.Services;
|
||||
|
||||
public class ToastService : IDisposable
|
||||
public enum ToastLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public class ToastService : IDisposable
|
||||
{
|
||||
public event Action<string, ToastLevel> OnShow;
|
||||
public event Action OnHide;
|
||||
private Timer Countdown;
|
||||
public void ShowToast(string message, ToastLevel level)
|
||||
{
|
||||
public event Action<string, ToastLevel> OnShow;
|
||||
public event Action OnHide;
|
||||
private Timer Countdown;
|
||||
public void ShowToast(string message, ToastLevel level)
|
||||
OnShow?.Invoke(message, level);
|
||||
StartCountdown();
|
||||
}
|
||||
private void StartCountdown()
|
||||
{
|
||||
SetCountdown();
|
||||
if (Countdown.Enabled)
|
||||
{
|
||||
OnShow?.Invoke(message, level);
|
||||
StartCountdown();
|
||||
Countdown.Stop();
|
||||
Countdown.Start();
|
||||
}
|
||||
private void StartCountdown()
|
||||
else
|
||||
{
|
||||
SetCountdown();
|
||||
if (Countdown.Enabled)
|
||||
{
|
||||
Countdown.Stop();
|
||||
Countdown.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Countdown.Start();
|
||||
}
|
||||
Countdown.Start();
|
||||
}
|
||||
private void SetCountdown()
|
||||
}
|
||||
private void SetCountdown()
|
||||
{
|
||||
if (Countdown == null)
|
||||
{
|
||||
if (Countdown == null)
|
||||
{
|
||||
Countdown = new Timer(3000);
|
||||
Countdown.Elapsed += HideToast;
|
||||
Countdown.AutoReset = false;
|
||||
}
|
||||
}
|
||||
private void HideToast(object source, ElapsedEventArgs args)
|
||||
{
|
||||
OnHide?.Invoke();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Countdown?.Dispose();
|
||||
Countdown = new Timer(3000);
|
||||
Countdown.Elapsed += HideToast;
|
||||
Countdown.AutoReset = false;
|
||||
}
|
||||
}
|
||||
private void HideToast(object source, ElapsedEventArgs args)
|
||||
{
|
||||
OnHide?.Invoke();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Countdown?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,19 @@ using BlazorShared.Interfaces;
|
||||
using BlazorShared.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BlazorAdmin
|
||||
{
|
||||
public static class ServicesConfiguration
|
||||
{
|
||||
public static IServiceCollection AddBlazorServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogBrand>, CachedCatalogLookupDataServiceDecorator<CatalogBrand,CatalogBrandResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogType>, CachedCatalogLookupDataServiceDecorator<CatalogType,CatalogTypeResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<ICatalogItemService, CachedCatalogItemServiceDecorator>();
|
||||
services.AddScoped<CatalogItemService>();
|
||||
namespace BlazorAdmin;
|
||||
|
||||
return services;
|
||||
}
|
||||
public static class ServicesConfiguration
|
||||
{
|
||||
public static IServiceCollection AddBlazorServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogBrand>, CachedCatalogLookupDataServiceDecorator<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogBrand, CatalogBrandResponse>>();
|
||||
services.AddScoped<ICatalogLookupDataService<CatalogType>, CachedCatalogLookupDataServiceDecorator<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<CatalogLookupDataService<CatalogType, CatalogTypeResponse>>();
|
||||
services.AddScoped<ICatalogItemService, CachedCatalogItemServiceDecorator>();
|
||||
services.AddScoped<CatalogItemService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace BlazorAdmin.Shared
|
||||
namespace BlazorAdmin.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// This is needed until 5.0 ships with native support
|
||||
/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class CustomInputSelect<TValue> : InputSelect<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// This is needed until 5.0 ships with native support
|
||||
/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class CustomInputSelect<TValue> : InputSelect<TValue>
|
||||
protected override bool TryParseValueFromString(string value, out TValue result,
|
||||
out string validationErrorMessage)
|
||||
{
|
||||
protected override bool TryParseValueFromString(string value, out TValue result,
|
||||
out string validationErrorMessage)
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
if (int.TryParse(value, out var resultInt))
|
||||
{
|
||||
if (int.TryParse(value, out var resultInt))
|
||||
{
|
||||
result = (TValue)(object)resultInt;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
validationErrorMessage =
|
||||
$"The selected value {value} is not a valid number.";
|
||||
return false;
|
||||
}
|
||||
result = (TValue)(object)resultInt;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryParseValueFromString(value, out result,
|
||||
out validationErrorMessage);
|
||||
result = default;
|
||||
validationErrorMessage =
|
||||
$"The selected value {value} is not a valid number.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.TryParseValueFromString(value, out result,
|
||||
out validationErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace BlazorShared.Attributes
|
||||
namespace BlazorShared.Attributes;
|
||||
|
||||
public class EndpointAttribute : Attribute
|
||||
{
|
||||
public class EndpointAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
namespace BlazorShared.Authorization
|
||||
namespace BlazorShared.Authorization;
|
||||
|
||||
public class ClaimValue
|
||||
{
|
||||
public class ClaimValue
|
||||
public ClaimValue()
|
||||
{
|
||||
public ClaimValue()
|
||||
{
|
||||
}
|
||||
|
||||
public ClaimValue(string type, string value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public ClaimValue(string type, string value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace BlazorShared.Authorization
|
||||
namespace BlazorShared.Authorization;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static class Constants
|
||||
public static class Roles
|
||||
{
|
||||
public static class Roles
|
||||
{
|
||||
public const string ADMINISTRATORS = "Administrators";
|
||||
}
|
||||
public const string ADMINISTRATORS = "Administrators";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BlazorShared.Authorization
|
||||
namespace BlazorShared.Authorization;
|
||||
|
||||
public class UserInfo
|
||||
{
|
||||
public class UserInfo
|
||||
{
|
||||
public static readonly UserInfo Anonymous = new UserInfo();
|
||||
public bool IsAuthenticated { get; set; }
|
||||
public string NameClaimType { get; set; }
|
||||
public string RoleClaimType { get; set; }
|
||||
public string Token { get; set; }
|
||||
public IEnumerable<ClaimValue> Claims { get; set; }
|
||||
}
|
||||
public static readonly UserInfo Anonymous = new UserInfo();
|
||||
public bool IsAuthenticated { get; set; }
|
||||
public string NameClaimType { get; set; }
|
||||
public string RoleClaimType { get; set; }
|
||||
public string Token { get; set; }
|
||||
public IEnumerable<ClaimValue> Claims { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace BlazorShared
|
||||
{
|
||||
public class BaseUrlConfiguration
|
||||
{
|
||||
public const string CONFIG_NAME = "baseUrls";
|
||||
namespace BlazorShared;
|
||||
|
||||
public string ApiBase { get; set; }
|
||||
public string WebBase { get; set; }
|
||||
}
|
||||
public class BaseUrlConfiguration
|
||||
{
|
||||
public const string CONFIG_NAME = "baseUrls";
|
||||
|
||||
public string ApiBase { get; set; }
|
||||
public string WebBase { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>BlazorShared</RootNamespace>
|
||||
<AssemblyName>BlazorShared</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Models;
|
||||
|
||||
namespace BlazorShared.Interfaces
|
||||
namespace BlazorShared.Interfaces;
|
||||
|
||||
public interface ICatalogItemService
|
||||
{
|
||||
public interface ICatalogItemService
|
||||
{
|
||||
Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem);
|
||||
Task<CatalogItem> Edit(CatalogItem catalogItem);
|
||||
Task<string> Delete(int id);
|
||||
Task<CatalogItem> GetById(int id);
|
||||
Task<List<CatalogItem>> ListPaged(int pageSize);
|
||||
Task<List<CatalogItem>> List();
|
||||
}
|
||||
Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem);
|
||||
Task<CatalogItem> Edit(CatalogItem catalogItem);
|
||||
Task<string> Delete(int id);
|
||||
Task<CatalogItem> GetById(int id);
|
||||
Task<List<CatalogItem>> ListPaged(int pageSize);
|
||||
Task<List<CatalogItem>> List();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using BlazorShared.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorShared.Models;
|
||||
|
||||
namespace BlazorShared.Interfaces
|
||||
namespace BlazorShared.Interfaces;
|
||||
|
||||
public interface ICatalogLookupDataService<TLookupData> where TLookupData : LookupData
|
||||
{
|
||||
public interface ICatalogLookupDataService<TLookupData> where TLookupData : LookupData
|
||||
{
|
||||
Task<List<TLookupData>> List();
|
||||
}
|
||||
Task<List<TLookupData>> List();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using BlazorShared.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using BlazorShared.Models;
|
||||
|
||||
namespace BlazorShared.Interfaces
|
||||
namespace BlazorShared.Interfaces;
|
||||
|
||||
public interface ILookupDataResponse<TLookupData> where TLookupData : LookupData
|
||||
{
|
||||
public interface ILookupDataResponse<TLookupData> where TLookupData : LookupData
|
||||
{
|
||||
List<TLookupData> List { get; set; }
|
||||
}
|
||||
List<TLookupData> List { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using BlazorShared.Attributes;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
[Endpoint(Name = "catalog-brands")]
|
||||
public class CatalogBrand : LookupData
|
||||
{
|
||||
[Endpoint(Name = "catalog-brands")]
|
||||
public class CatalogBrand : LookupData
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using BlazorShared.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using BlazorShared.Interfaces;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class CatalogBrandResponse : ILookupDataResponse<CatalogBrand>
|
||||
{
|
||||
public class CatalogBrandResponse : ILookupDataResponse<CatalogBrand>
|
||||
{
|
||||
[JsonPropertyName("CatalogBrands")]
|
||||
public List<CatalogBrand> List { get; set; } = new List<CatalogBrand>();
|
||||
}
|
||||
[JsonPropertyName("CatalogBrands")]
|
||||
public List<CatalogBrand> List { get; set; } = new List<CatalogBrand>();
|
||||
}
|
||||
|
||||
@@ -4,85 +4,84 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorInputFile;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class CatalogItem
|
||||
{
|
||||
public class CatalogItem
|
||||
public int Id { get; set; }
|
||||
|
||||
public int CatalogTypeId { get; set; }
|
||||
public string CatalogType { get; set; } = "NotSet";
|
||||
|
||||
public int CatalogBrandId { get; set; }
|
||||
public string CatalogBrand { get; set; } = "NotSet";
|
||||
|
||||
[Required(ErrorMessage = "The Name field is required")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "The Description field is required")]
|
||||
public string Description { get; set; }
|
||||
|
||||
// decimal(18,2)
|
||||
[RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")]
|
||||
[Range(0.01, 1000)]
|
||||
[DataType(DataType.Currency)]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public string PictureUri { get; set; }
|
||||
public string PictureBase64 { get; set; }
|
||||
public string PictureName { get; set; }
|
||||
|
||||
private const int ImageMaximumBytes = 512000;
|
||||
|
||||
public static string IsValidImage(string pictureName, string pictureBase64)
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int CatalogTypeId { get; set; }
|
||||
public string CatalogType { get; set; } = "NotSet";
|
||||
|
||||
public int CatalogBrandId { get; set; }
|
||||
public string CatalogBrand { get; set; } = "NotSet";
|
||||
|
||||
[Required(ErrorMessage = "The Name field is required")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "The Description field is required")]
|
||||
public string Description { get; set; }
|
||||
|
||||
// decimal(18,2)
|
||||
[RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")]
|
||||
[Range(0.01, 1000)]
|
||||
[DataType(DataType.Currency)]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public string PictureUri { get; set; }
|
||||
public string PictureBase64 { get; set; }
|
||||
public string PictureName { get; set; }
|
||||
|
||||
private const int ImageMaximumBytes = 512000;
|
||||
|
||||
public static string IsValidImage(string pictureName, string pictureBase64)
|
||||
if (string.IsNullOrEmpty(pictureBase64))
|
||||
{
|
||||
if (string.IsNullOrEmpty(pictureBase64))
|
||||
{
|
||||
return "File not found!";
|
||||
}
|
||||
var fileData = Convert.FromBase64String(pictureBase64);
|
||||
return "File not found!";
|
||||
}
|
||||
var fileData = Convert.FromBase64String(pictureBase64);
|
||||
|
||||
if (fileData.Length <= 0)
|
||||
{
|
||||
return "File length is 0!";
|
||||
}
|
||||
|
||||
if (fileData.Length > ImageMaximumBytes)
|
||||
{
|
||||
return "Maximum length is 512KB";
|
||||
}
|
||||
|
||||
if (!IsExtensionValid(pictureName))
|
||||
{
|
||||
return "File is not image";
|
||||
}
|
||||
|
||||
return null;
|
||||
if (fileData.Length <= 0)
|
||||
{
|
||||
return "File length is 0!";
|
||||
}
|
||||
|
||||
public static async Task<string> DataToBase64(IFileListEntry fileItem)
|
||||
if (fileData.Length > ImageMaximumBytes)
|
||||
{
|
||||
using ( var reader = new StreamReader(fileItem.Data))
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
await reader.BaseStream.CopyToAsync(memStream);
|
||||
var fileData = memStream.ToArray();
|
||||
var encodedBase64 = Convert.ToBase64String(fileData);
|
||||
|
||||
return encodedBase64;
|
||||
}
|
||||
}
|
||||
return "Maximum length is 512KB";
|
||||
}
|
||||
|
||||
private static bool IsExtensionValid(string fileName)
|
||||
if (!IsExtensionValid(pictureName))
|
||||
{
|
||||
var extension = Path.GetExtension(fileName);
|
||||
return "File is not image";
|
||||
}
|
||||
|
||||
return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<string> DataToBase64(IFileListEntry fileItem)
|
||||
{
|
||||
using (var reader = new StreamReader(fileItem.Data))
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
await reader.BaseStream.CopyToAsync(memStream);
|
||||
var fileData = memStream.ToArray();
|
||||
var encodedBase64 = Convert.ToBase64String(fileData);
|
||||
|
||||
return encodedBase64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsExtensionValid(string fileName)
|
||||
{
|
||||
var extension = Path.GetExtension(fileName);
|
||||
|
||||
return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using BlazorShared.Attributes;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
[Endpoint(Name = "catalog-types")]
|
||||
public class CatalogType : LookupData
|
||||
{
|
||||
[Endpoint(Name = "catalog-types")]
|
||||
public class CatalogType : LookupData
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using BlazorShared.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using BlazorShared.Interfaces;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class CatalogTypeResponse : ILookupDataResponse<CatalogType>
|
||||
{
|
||||
public class CatalogTypeResponse : ILookupDataResponse<CatalogType>
|
||||
{
|
||||
|
||||
[JsonPropertyName("CatalogTypes")]
|
||||
public List<CatalogType> List { get; set; } = new List<CatalogType>();
|
||||
}
|
||||
[JsonPropertyName("CatalogTypes")]
|
||||
public List<CatalogType> List { get; set; } = new List<CatalogType>();
|
||||
}
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class CreateCatalogItemRequest
|
||||
{
|
||||
public class CreateCatalogItemRequest
|
||||
{
|
||||
public int CatalogTypeId { get; set; }
|
||||
public int CatalogTypeId { get; set; }
|
||||
|
||||
public int CatalogBrandId { get; set; }
|
||||
public int CatalogBrandId { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "The Name field is required")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
[Required(ErrorMessage = "The Name field is required")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "The Description field is required")]
|
||||
public string Description { get; set; } = string.Empty;
|
||||
[Required(ErrorMessage = "The Description field is required")]
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
// decimal(18,2)
|
||||
[RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")]
|
||||
[Range(0.01, 1000)]
|
||||
[DataType(DataType.Currency)]
|
||||
public decimal Price { get; set; } = 0;
|
||||
// decimal(18,2)
|
||||
[RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")]
|
||||
[Range(0.01, 1000)]
|
||||
[DataType(DataType.Currency)]
|
||||
public decimal Price { get; set; } = 0;
|
||||
|
||||
public string PictureUri { get; set; } = string.Empty;
|
||||
public string PictureBase64 { get; set; } = string.Empty;
|
||||
public string PictureName { get; set; } = string.Empty;
|
||||
public string PictureUri { get; set; } = string.Empty;
|
||||
public string PictureBase64 { get; set; } = string.Empty;
|
||||
public string PictureName { get; set; } = string.Empty;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class CreateCatalogItemResponse
|
||||
{
|
||||
public class CreateCatalogItemResponse
|
||||
{
|
||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
||||
}
|
||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class DeleteCatalogItemResponse
|
||||
{
|
||||
public class DeleteCatalogItemResponse
|
||||
{
|
||||
public string Status { get; set; } = "Deleted";
|
||||
}
|
||||
public string Status { get; set; } = "Deleted";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class EditCatalogItemResult
|
||||
{
|
||||
public class EditCatalogItemResult
|
||||
{
|
||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
||||
}
|
||||
public CatalogItem CatalogItem { get; set; } = new CatalogItem();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class ErrorDetails
|
||||
{
|
||||
public class ErrorDetails
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public abstract class LookupData
|
||||
{
|
||||
public abstract class LookupData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BlazorShared.Models
|
||||
namespace BlazorShared.Models;
|
||||
|
||||
public class PagedCatalogItemResponse
|
||||
{
|
||||
public class PagedCatalogItemResponse
|
||||
{
|
||||
public List<CatalogItem> CatalogItems { get; set; } = new List<CatalogItem>();
|
||||
public int PageCount { get; set; } = 0;
|
||||
}
|
||||
public List<CatalogItem> CatalogItems { get; set; } = new List<CatalogItem>();
|
||||
public int PageCount { get; set; } = 0;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data;
|
||||
|
||||
public class BasketQueryService : IBasketQueryService
|
||||
{
|
||||
public class BasketQueryService : IBasketQueryService
|
||||
private readonly CatalogContext _dbContext;
|
||||
|
||||
public BasketQueryService(CatalogContext dbContext)
|
||||
{
|
||||
private readonly CatalogContext _dbContext;
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public BasketQueryService(CatalogContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
public async Task<int> CountTotalBasketItems(string username)
|
||||
{
|
||||
var totalItems = await _dbContext.Baskets
|
||||
.Where(basket => basket.BuyerId == username)
|
||||
.SelectMany(item => item.Items)
|
||||
.SumAsync(sum => sum.Quantity);
|
||||
|
||||
public async Task<int> CountTotalBasketItems(string username)
|
||||
{
|
||||
var totalItems = await _dbContext.Baskets
|
||||
.Where(basket => basket.BuyerId == username)
|
||||
.SelectMany(item => item.Items)
|
||||
.SumAsync(sum => sum.Quantity);
|
||||
|
||||
return totalItems;
|
||||
}
|
||||
return totalItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data;
|
||||
|
||||
public class CatalogContext : DbContext
|
||||
{
|
||||
|
||||
public class CatalogContext : DbContext
|
||||
public CatalogContext(DbContextOptions<CatalogContext> options) : base(options)
|
||||
{
|
||||
public CatalogContext(DbContextOptions<CatalogContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public DbSet<Basket> Baskets { get; set; }
|
||||
public DbSet<CatalogItem> CatalogItems { get; set; }
|
||||
public DbSet<CatalogBrand> CatalogBrands { get; set; }
|
||||
public DbSet<CatalogType> CatalogTypes { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<OrderItem> OrderItems { get; set; }
|
||||
public DbSet<BasketItem> BasketItems { get; set; }
|
||||
public DbSet<Basket> Baskets { get; set; }
|
||||
public DbSet<CatalogItem> CatalogItems { get; set; }
|
||||
public DbSet<CatalogBrand> CatalogBrands { get; set; }
|
||||
public DbSet<CatalogType> CatalogTypes { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<OrderItem> OrderItems { get; set; }
|
||||
public DbSet<BasketItem> BasketItems { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data;
|
||||
|
||||
public class CatalogContextSeed
|
||||
{
|
||||
public class CatalogContextSeed
|
||||
public static async Task SeedAsync(CatalogContext catalogContext,
|
||||
ILoggerFactory loggerFactory, int retry = 0)
|
||||
{
|
||||
public static async Task SeedAsync(CatalogContext catalogContext,
|
||||
ILoggerFactory loggerFactory, int retry = 0)
|
||||
var retryForAvailability = retry;
|
||||
try
|
||||
{
|
||||
var retryForAvailability = retry;
|
||||
try
|
||||
if (catalogContext.Database.IsSqlServer())
|
||||
{
|
||||
if (catalogContext.Database.IsSqlServer())
|
||||
{
|
||||
catalogContext.Database.Migrate();
|
||||
}
|
||||
|
||||
if (!await catalogContext.CatalogBrands.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogBrands.AddRangeAsync(
|
||||
GetPreconfiguredCatalogBrands());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!await catalogContext.CatalogTypes.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogTypes.AddRangeAsync(
|
||||
GetPreconfiguredCatalogTypes());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!await catalogContext.CatalogItems.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogItems.AddRangeAsync(
|
||||
GetPreconfiguredItems());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
catalogContext.Database.Migrate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (retryForAvailability >= 10) throw;
|
||||
|
||||
retryForAvailability++;
|
||||
var log = loggerFactory.CreateLogger<CatalogContextSeed>();
|
||||
log.LogError(ex.Message);
|
||||
await SeedAsync(catalogContext, loggerFactory, retryForAvailability);
|
||||
throw;
|
||||
if (!await catalogContext.CatalogBrands.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogBrands.AddRangeAsync(
|
||||
GetPreconfiguredCatalogBrands());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!await catalogContext.CatalogTypes.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogTypes.AddRangeAsync(
|
||||
GetPreconfiguredCatalogTypes());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!await catalogContext.CatalogItems.AnyAsync())
|
||||
{
|
||||
await catalogContext.CatalogItems.AddRangeAsync(
|
||||
GetPreconfiguredItems());
|
||||
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new List<CatalogBrand>
|
||||
if (retryForAvailability >= 10) throw;
|
||||
|
||||
retryForAvailability++;
|
||||
var log = loggerFactory.CreateLogger<CatalogContextSeed>();
|
||||
log.LogError(ex.Message);
|
||||
await SeedAsync(catalogContext, loggerFactory, retryForAvailability);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
|
||||
{
|
||||
return new List<CatalogBrand>
|
||||
{
|
||||
new("Azure"),
|
||||
new(".NET"),
|
||||
@@ -66,22 +66,22 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
new("SQL Server"),
|
||||
new("Other")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
|
||||
{
|
||||
return new List<CatalogType>
|
||||
static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
|
||||
{
|
||||
return new List<CatalogType>
|
||||
{
|
||||
new("Mug"),
|
||||
new("T-Shirt"),
|
||||
new("Sheet"),
|
||||
new("USB Memory Stick")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<CatalogItem> GetPreconfiguredItems()
|
||||
{
|
||||
return new List<CatalogItem>
|
||||
static IEnumerable<CatalogItem> GetPreconfiguredItems()
|
||||
{
|
||||
return new List<CatalogItem>
|
||||
{
|
||||
new(2,2, ".NET Bot Black Sweatshirt", ".NET Bot Black Sweatshirt", 19.5M, "http://catalogbaseurltobereplaced/images/products/1.png"),
|
||||
new(1,2, ".NET Black & White Mug", ".NET Black & White Mug", 8.50M, "http://catalogbaseurltobereplaced/images/products/2.png"),
|
||||
@@ -96,6 +96,5 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
new(3,2, "Cup<T> Sheet", "Cup<T> Sheet", 8.5M, "http://catalogbaseurltobereplaced/images/products/11.png"),
|
||||
new(2,5, "Prism White TShirt", "Prism White TShirt", 12, "http://catalogbaseurltobereplaced/images/products/12.png")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
{
|
||||
public class BasketConfiguration : IEntityTypeConfiguration<Basket>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Basket> builder)
|
||||
{
|
||||
var navigation = builder.Metadata.FindNavigation(nameof(Basket.Items));
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
builder.Property(b => b.BuyerId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
}
|
||||
public class BasketConfiguration : IEntityTypeConfiguration<Basket>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Basket> builder)
|
||||
{
|
||||
var navigation = builder.Metadata.FindNavigation(nameof(Basket.Items));
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
builder.Property(b => b.BuyerId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class BasketItemConfiguration : IEntityTypeConfiguration<BasketItem>
|
||||
{
|
||||
public class BasketItemConfiguration : IEntityTypeConfiguration<BasketItem>
|
||||
public void Configure(EntityTypeBuilder<BasketItem> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<BasketItem> builder)
|
||||
{
|
||||
builder.Property(bi => bi.UnitPrice)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
}
|
||||
builder.Property(bi => bi.UnitPrice)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class CatalogBrandConfiguration : IEntityTypeConfiguration<CatalogBrand>
|
||||
{
|
||||
public class CatalogBrandConfiguration : IEntityTypeConfiguration<CatalogBrand>
|
||||
public void Configure(EntityTypeBuilder<CatalogBrand> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<CatalogBrand> builder)
|
||||
{
|
||||
builder.HasKey(ci => ci.Id);
|
||||
builder.HasKey(ci => ci.Id);
|
||||
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_brand_hilo")
|
||||
.IsRequired();
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_brand_hilo")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(cb => cb.Brand)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
builder.Property(cb => cb.Brand)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,36 +2,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class CatalogItemConfiguration : IEntityTypeConfiguration<CatalogItem>
|
||||
{
|
||||
public class CatalogItemConfiguration : IEntityTypeConfiguration<CatalogItem>
|
||||
public void Configure(EntityTypeBuilder<CatalogItem> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<CatalogItem> builder)
|
||||
{
|
||||
builder.ToTable("Catalog");
|
||||
builder.ToTable("Catalog");
|
||||
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_hilo")
|
||||
.IsRequired();
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_hilo")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(ci => ci.Name)
|
||||
.IsRequired(true)
|
||||
.HasMaxLength(50);
|
||||
builder.Property(ci => ci.Name)
|
||||
.IsRequired(true)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(ci => ci.Price)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
builder.Property(ci => ci.Price)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
builder.Property(ci => ci.PictureUri)
|
||||
.IsRequired(false);
|
||||
builder.Property(ci => ci.PictureUri)
|
||||
.IsRequired(false);
|
||||
|
||||
builder.HasOne(ci => ci.CatalogBrand)
|
||||
.WithMany()
|
||||
.HasForeignKey(ci => ci.CatalogBrandId);
|
||||
builder.HasOne(ci => ci.CatalogBrand)
|
||||
.WithMany()
|
||||
.HasForeignKey(ci => ci.CatalogBrandId);
|
||||
|
||||
builder.HasOne(ci => ci.CatalogType)
|
||||
.WithMany()
|
||||
.HasForeignKey(ci => ci.CatalogTypeId);
|
||||
}
|
||||
builder.HasOne(ci => ci.CatalogType)
|
||||
.WithMany()
|
||||
.HasForeignKey(ci => ci.CatalogTypeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,20 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class CatalogTypeConfiguration : IEntityTypeConfiguration<CatalogType>
|
||||
{
|
||||
public class CatalogTypeConfiguration : IEntityTypeConfiguration<CatalogType>
|
||||
public void Configure(EntityTypeBuilder<CatalogType> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<CatalogType> builder)
|
||||
{
|
||||
builder.HasKey(ci => ci.Id);
|
||||
builder.HasKey(ci => ci.Id);
|
||||
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_type_hilo")
|
||||
.IsRequired();
|
||||
builder.Property(ci => ci.Id)
|
||||
.UseHiLo("catalog_type_hilo")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(cb => cb.Type)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
builder.Property(cb => cb.Type)
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,43 +2,42 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||
{
|
||||
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||
public void Configure(EntityTypeBuilder<Order> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Order> builder)
|
||||
var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems));
|
||||
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
builder.Property(b => b.BuyerId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
|
||||
builder.OwnsOne(o => o.ShipToAddress, a =>
|
||||
{
|
||||
var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems));
|
||||
a.WithOwner();
|
||||
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
a.Property(a => a.ZipCode)
|
||||
.HasMaxLength(18)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(b => b.BuyerId)
|
||||
.IsRequired()
|
||||
.HasMaxLength(256);
|
||||
a.Property(a => a.Street)
|
||||
.HasMaxLength(180)
|
||||
.IsRequired();
|
||||
|
||||
builder.OwnsOne(o => o.ShipToAddress, a =>
|
||||
{
|
||||
a.WithOwner();
|
||||
|
||||
a.Property(a => a.ZipCode)
|
||||
.HasMaxLength(18)
|
||||
.IsRequired();
|
||||
a.Property(a => a.State)
|
||||
.HasMaxLength(60);
|
||||
|
||||
a.Property(a => a.Street)
|
||||
.HasMaxLength(180)
|
||||
.IsRequired();
|
||||
a.Property(a => a.Country)
|
||||
.HasMaxLength(90)
|
||||
.IsRequired();
|
||||
|
||||
a.Property(a => a.State)
|
||||
.HasMaxLength(60);
|
||||
|
||||
a.Property(a => a.Country)
|
||||
.HasMaxLength(90)
|
||||
.IsRequired();
|
||||
|
||||
a.Property(a => a.City)
|
||||
.HasMaxLength(100)
|
||||
.IsRequired();
|
||||
});
|
||||
}
|
||||
a.Property(a => a.City)
|
||||
.HasMaxLength(100)
|
||||
.IsRequired();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,23 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Config;
|
||||
|
||||
public class OrderItemConfiguration : IEntityTypeConfiguration<OrderItem>
|
||||
{
|
||||
public class OrderItemConfiguration : IEntityTypeConfiguration<OrderItem>
|
||||
public void Configure(EntityTypeBuilder<OrderItem> builder)
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<OrderItem> builder)
|
||||
builder.OwnsOne(i => i.ItemOrdered, io =>
|
||||
{
|
||||
builder.OwnsOne(i => i.ItemOrdered, io =>
|
||||
{
|
||||
io.WithOwner();
|
||||
io.WithOwner();
|
||||
|
||||
io.Property(cio => cio.ProductName)
|
||||
.HasMaxLength(50)
|
||||
.IsRequired();
|
||||
});
|
||||
io.Property(cio => cio.ProductName)
|
||||
.HasMaxLength(50)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
builder.Property(oi => oi.UnitPrice)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
}
|
||||
builder.Property(oi => oi.UnitPrice)
|
||||
.IsRequired(true)
|
||||
.HasColumnType("decimal(18,2)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Ardalis.Specification.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data;
|
||||
|
||||
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
|
||||
{
|
||||
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
|
||||
public EfRepository(CatalogContext dbContext) : base(dbContext)
|
||||
{
|
||||
public EfRepository(CatalogContext dbContext) : base(dbContext)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data;
|
||||
|
||||
public class FileItem
|
||||
{
|
||||
public class FileItem
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
public string Url { get; set; }
|
||||
public long Size { get; set; }
|
||||
public string Ext { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string DataBase64 { get; set; }
|
||||
}
|
||||
}
|
||||
public string FileName { get; set; }
|
||||
public string Url { get; set; }
|
||||
public long Size { get; set; }
|
||||
public string Ext { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string DataBase64 { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,207 +1,206 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations;
|
||||
|
||||
public partial class InitialModel : Migration
|
||||
{
|
||||
public partial class InitialModel : Migration
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_brand_hilo",
|
||||
incrementBy: 10);
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_brand_hilo",
|
||||
incrementBy: 10);
|
||||
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_hilo",
|
||||
incrementBy: 10);
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_hilo",
|
||||
incrementBy: 10);
|
||||
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_type_hilo",
|
||||
incrementBy: 10);
|
||||
migrationBuilder.CreateSequence(
|
||||
name: "catalog_type_hilo",
|
||||
incrementBy: 10);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Baskets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
BuyerId = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Baskets", x => x.Id);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Baskets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
BuyerId = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Baskets", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CatalogBrands",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Brand = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CatalogBrands", x => x.Id);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CatalogBrands",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Brand = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CatalogBrands", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CatalogTypes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Type = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CatalogTypes", x => x.Id);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CatalogTypes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Type = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CatalogTypes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Orders",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
BuyerId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
OrderDate = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
|
||||
ShipToAddress_Street = table.Column<string>(type: "nvarchar(180)", maxLength: 180, nullable: true),
|
||||
ShipToAddress_City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||
ShipToAddress_State = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: true),
|
||||
ShipToAddress_Country = table.Column<string>(type: "nvarchar(90)", maxLength: 90, nullable: true),
|
||||
ShipToAddress_ZipCode = table.Column<string>(type: "nvarchar(18)", maxLength: 18, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Orders", x => x.Id);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Orders",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
BuyerId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
OrderDate = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
|
||||
ShipToAddress_Street = table.Column<string>(type: "nvarchar(180)", maxLength: 180, nullable: true),
|
||||
ShipToAddress_City = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||
ShipToAddress_State = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: true),
|
||||
ShipToAddress_Country = table.Column<string>(type: "nvarchar(90)", maxLength: 90, nullable: true),
|
||||
ShipToAddress_ZipCode = table.Column<string>(type: "nvarchar(18)", maxLength: 18, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Orders", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BasketItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
UnitPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
CatalogItemId = table.Column<int>(type: "int", nullable: false),
|
||||
BasketId = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BasketItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BasketItems_Baskets_BasketId",
|
||||
column: x => x.BasketId,
|
||||
principalTable: "Baskets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BasketItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
UnitPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
CatalogItemId = table.Column<int>(type: "int", nullable: false),
|
||||
BasketId = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BasketItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BasketItems_Baskets_BasketId",
|
||||
column: x => x.BasketId,
|
||||
principalTable: "Baskets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Catalog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
PictureUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CatalogTypeId = table.Column<int>(type: "int", nullable: false),
|
||||
CatalogBrandId = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Catalog", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Catalog_CatalogBrands_CatalogBrandId",
|
||||
column: x => x.CatalogBrandId,
|
||||
principalTable: "CatalogBrands",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Catalog_CatalogTypes_CatalogTypeId",
|
||||
column: x => x.CatalogTypeId,
|
||||
principalTable: "CatalogTypes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Catalog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
PictureUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CatalogTypeId = table.Column<int>(type: "int", nullable: false),
|
||||
CatalogBrandId = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Catalog", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Catalog_CatalogBrands_CatalogBrandId",
|
||||
column: x => x.CatalogBrandId,
|
||||
principalTable: "CatalogBrands",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Catalog_CatalogTypes_CatalogTypeId",
|
||||
column: x => x.CatalogTypeId,
|
||||
principalTable: "CatalogTypes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OrderItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ItemOrdered_CatalogItemId = table.Column<int>(type: "int", nullable: true),
|
||||
ItemOrdered_ProductName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||
ItemOrdered_PictureUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
UnitPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
Units = table.Column<int>(type: "int", nullable: false),
|
||||
OrderId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OrderItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OrderItems_Orders_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Orders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OrderItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ItemOrdered_CatalogItemId = table.Column<int>(type: "int", nullable: true),
|
||||
ItemOrdered_ProductName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||
ItemOrdered_PictureUri = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
UnitPrice = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
Units = table.Column<int>(type: "int", nullable: false),
|
||||
OrderId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OrderItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OrderItems_Orders_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Orders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BasketItems_BasketId",
|
||||
table: "BasketItems",
|
||||
column: "BasketId");
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BasketItems_BasketId",
|
||||
table: "BasketItems",
|
||||
column: "BasketId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Catalog_CatalogBrandId",
|
||||
table: "Catalog",
|
||||
column: "CatalogBrandId");
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Catalog_CatalogBrandId",
|
||||
table: "Catalog",
|
||||
column: "CatalogBrandId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Catalog_CatalogTypeId",
|
||||
table: "Catalog",
|
||||
column: "CatalogTypeId");
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Catalog_CatalogTypeId",
|
||||
table: "Catalog",
|
||||
column: "CatalogTypeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OrderItems_OrderId",
|
||||
table: "OrderItems",
|
||||
column: "OrderId");
|
||||
}
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OrderItems_OrderId",
|
||||
table: "OrderItems",
|
||||
column: "OrderId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BasketItems");
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BasketItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Catalog");
|
||||
migrationBuilder.DropTable(
|
||||
name: "Catalog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OrderItems");
|
||||
migrationBuilder.DropTable(
|
||||
name: "OrderItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Baskets");
|
||||
migrationBuilder.DropTable(
|
||||
name: "Baskets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "CatalogBrands");
|
||||
migrationBuilder.DropTable(
|
||||
name: "CatalogBrands");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "CatalogTypes");
|
||||
migrationBuilder.DropTable(
|
||||
name: "CatalogTypes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Orders");
|
||||
migrationBuilder.DropTable(
|
||||
name: "Orders");
|
||||
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_brand_hilo");
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_brand_hilo");
|
||||
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_hilo");
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_hilo");
|
||||
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_type_hilo");
|
||||
}
|
||||
migrationBuilder.DropSequence(
|
||||
name: "catalog_type_hilo");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations;
|
||||
|
||||
public partial class FixBuyerId : Migration
|
||||
{
|
||||
public partial class FixBuyerId : Migration
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Orders",
|
||||
type: "nvarchar(256)",
|
||||
maxLength: 256,
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)",
|
||||
oldNullable: true);
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Orders",
|
||||
type: "nvarchar(256)",
|
||||
maxLength: 256,
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Baskets",
|
||||
type: "nvarchar(256)",
|
||||
maxLength: 256,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(40)",
|
||||
oldMaxLength: 40);
|
||||
}
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Baskets",
|
||||
type: "nvarchar(256)",
|
||||
maxLength: 256,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(40)",
|
||||
oldMaxLength: 40);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Orders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(256)",
|
||||
oldMaxLength: 256);
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Orders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(256)",
|
||||
oldMaxLength: 256);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Baskets",
|
||||
type: "nvarchar(40)",
|
||||
maxLength: 40,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(256)",
|
||||
oldMaxLength: 256);
|
||||
}
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "BuyerId",
|
||||
table: "Baskets",
|
||||
type: "nvarchar(40)",
|
||||
maxLength: 40,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(256)",
|
||||
oldMaxLength: 256);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Identity
|
||||
{
|
||||
public class AppIdentityDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
namespace Microsoft.eShopWeb.Infrastructure.Identity;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
// Customize the ASP.NET Identity model and override the defaults if needed.
|
||||
// For example, you can rename the ASP.NET Identity table names and more.
|
||||
// Add your customizations after calling base.OnModelCreating(builder);
|
||||
}
|
||||
public class AppIdentityDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
// Customize the ASP.NET Identity model and override the defaults if needed.
|
||||
// For example, you can rename the ASP.NET Identity table names and more.
|
||||
// Add your customizations after calling base.OnModelCreating(builder);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user