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:
Shady Nagy
2021-11-06 01:55:48 +02:00
committed by GitHub
parent 64f150dc07
commit 9db2feb930
252 changed files with 6307 additions and 6413 deletions

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
});
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}