├── BlazorServerCleanArchitecture.WebUI ├── wwwroot │ ├── favicon.png │ └── css │ │ └── site.css ├── Pages │ ├── Index.razor │ ├── Counter.razor │ ├── Error.cshtml.cs │ ├── FetchData.razor │ ├── _Host.cshtml │ ├── Stadiums.razor │ └── Error.cshtml ├── appsettings.Development.json ├── appsettings.json ├── Data │ ├── WeatherForecast.cs │ └── WeatherForecastService.cs ├── BlazorServerCleanArchitecture.WebUI.csproj.user ├── _Imports.razor ├── App.razor ├── Shared │ ├── MainLayout.razor │ ├── SurveyPrompt.razor │ ├── NavMenu.razor.css │ ├── MainLayout.razor.css │ └── NavMenu.razor ├── BlazorServerCleanArchitecture.WebUI.csproj ├── Properties │ └── launchSettings.json └── Program.cs ├── README.md ├── BlazorServerCleanArchitecture.Domain ├── Common │ ├── Interfaces │ │ ├── IEntity.cs │ │ └── IAuditableEntity.cs │ ├── BaseEntity.cs │ └── BaseAuditableEntity.cs ├── BlazorServerCleanArchitecture.Domain.csproj └── Entities │ └── Stadium.cs ├── BlazorServerCleanArchitecture.Application ├── Common │ └── Mappings │ │ ├── IMapFrom.cs │ │ └── MappingProfile.cs ├── Interfaces │ └── Repositories │ │ ├── IStadiumRepository.cs │ │ ├── IGenericRepository.cs │ │ └── IUnitOfWork.cs ├── Features │ └── Stadiums │ │ └── Queries │ │ └── GetAllStadiums │ │ ├── GetAllStadiumsDto.cs │ │ └── GetAllStadiumsQuery.cs ├── Extensions │ └── IServiceCollectionExtensions.cs └── BlazorServerCleanArchitecture.Application.csproj ├── BlazorServerCleanArchitecture.Persistence ├── BlazorServerCleanArchitecture.Persistence.csproj ├── Repositories │ ├── StadiumRepository.cs │ ├── GenericRepository.cs │ └── UnitOfWork.cs ├── Contexts │ └── ApplicationDbContext.cs └── Extensions │ └── IServiceCollectionExtensions.cs └── BlazorServerCleanArchitecture.sln /BlazorServerCleanArchitecture.WebUI/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezzylearning/BlazorServerCleanArchitecture/HEAD/BlazorServerCleanArchitecture.WebUI/wwwroot/favicon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlazorServerCleanArchitecture 2 | Building Blazor Server Apps with Clean Architecture - Demo Project for a blog post available at https://www.ezzylearning.net/tutorial/building-blazor-server-apps-with-clean-architecture 3 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/Common/Interfaces/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerCleanArchitecture.Domain.Common.Interfaces 2 | { 3 | public interface IEntity 4 | { 5 | public int Id { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Index 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | 9 | 10 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Common/Mappings/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace BlazorServerCleanArchitecture.Application.Common.Mappings 4 | { 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Interfaces/Repositories/IStadiumRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Entities; 2 | 3 | namespace BlazorServerCleanArchitecture.Application.Interfaces.Repositories 4 | { 5 | public interface IStadiumRepository 6 | { 7 | Task> GetStadiumByCityAsync(string cityName); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/Common/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common.Interfaces; 2 | 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace BlazorServerCleanArchitecture.Domain.Common 6 | { 7 | public abstract class BaseEntity : IEntity 8 | { 9 | public int Id { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/Common/Interfaces/IAuditableEntity.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerCleanArchitecture.Domain.Common.Interfaces 2 | { 3 | public interface IAuditableEntity : IEntity 4 | { 5 | int? CreatedBy { get; set; } 6 | DateTime? CreatedDate { get; set; } 7 | int? UpdatedBy { get; set; } 8 | DateTime? UpdatedDate { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=MyServer;Database=PremierLeagueAppDb;User Id=MyUser;Password=MyPassword;MultipleActiveResultSets=true;TrustServerCertificate=True;" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft.AspNetCore": "Warning" 9 | } 10 | }, 11 | "AllowedHosts": "*" 12 | } 13 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Data/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorServerCleanArchitecture.WebUI.Data 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateOnly Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string? Summary { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/BlazorServerCleanArchitecture.WebUI.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RazorPageScaffolder 5 | root/Common/RazorPage 6 | 7 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/Common/BaseAuditableEntity.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common.Interfaces; 2 | 3 | namespace BlazorServerCleanArchitecture.Domain.Common 4 | { 5 | public abstract class BaseAuditableEntity : BaseEntity, IAuditableEntity 6 | { 7 | public int? CreatedBy { get; set; } 8 | public DateTime? CreatedDate { get; set; } 9 | public int? UpdatedBy { get; set; } 10 | public DateTime? UpdatedDate { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorServerCleanArchitecture.WebUI 10 | @using BlazorServerCleanArchitecture.WebUI.Shared 11 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/BlazorServerCleanArchitecture.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Domain/Entities/Stadium.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common; 2 | 3 | namespace BlazorServerCleanArchitecture.Domain.Entities 4 | { 5 | public class Stadium : BaseAuditableEntity 6 | { 7 | public string Name { get; set; } 8 | public string City { get; set; } 9 | public int? Capacity { get; set; } 10 | public int? BuiltYear { get; set; } 11 | public int? PitchLength { get; set; } 12 | public int? PitchWidth { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | BlazorServerCleanArchitecture.WebUI 4 | 5 |
6 | 9 | 10 |
11 |
12 | About 13 |
14 | 15 |
16 | @Body 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Interfaces/Repositories/IGenericRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common.Interfaces; 2 | 3 | namespace BlazorServerCleanArchitecture.Application.Interfaces.Repositories 4 | { 5 | public interface IGenericRepository where T : class, IEntity 6 | { 7 | IQueryable Entities { get; } 8 | 9 | Task GetByIdAsync(int id); 10 | Task> GetAllAsync(); 11 | Task AddAsync(T entity); 12 | Task UpdateAsync(T entity); 13 | Task DeleteAsync(T entity); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Interfaces/Repositories/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common; 2 | 3 | namespace BlazorServerCleanArchitecture.Application.Interfaces.Repositories 4 | { 5 | public interface IUnitOfWork : IDisposable 6 | { 7 | IGenericRepository Repository() where T : BaseAuditableEntity; 8 | 9 | Task Save(CancellationToken cancellationToken); 10 | 11 | Task SaveAndRemoveCache(CancellationToken cancellationToken, params string[] cacheKeys); 12 | 13 | Task Rollback(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 |
2 | 3 | @Title 4 | 5 | 6 | Please take our 7 | brief survey 8 | 9 | and tell us what you think. 10 |
11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string? Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/BlazorServerCleanArchitecture.WebUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/BlazorServerCleanArchitecture.Persistence.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8478", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "http": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "applicationUrl": "http://localhost:5280", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "IIS Express": { 21 | "commandName": "IISExpress", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Data/WeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorServerCleanArchitecture.WebUI.Data 6 | { 7 | public class WeatherForecastService 8 | { 9 | private static readonly string[] Summaries = new[] 10 | { 11 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 12 | }; 13 | 14 | public Task GetForecastAsync(DateOnly startDate) 15 | { 16 | return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast 17 | { 18 | Date = startDate.AddDays(index), 19 | TemperatureC = Random.Shared.Next(-20, 55), 20 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 21 | }).ToArray()); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Features/Stadiums/Queries/GetAllStadiums/GetAllStadiumsDto.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Application.Common.Mappings; 2 | using BlazorServerCleanArchitecture.Domain.Entities; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Security.Cryptography; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BlazorServerCleanArchitecture.Application.Features.Stadiums.Queries.GetAllStadiums 12 | { 13 | public class GetAllStadiumsDto : IMapFrom 14 | { 15 | public int Id { get; init; } 16 | public string Name { get; set; } 17 | public string City { get; set; } 18 | public int? Capacity { get; set; } 19 | public int? BuiltYear { get; set; } 20 | public int? PitchLength { get; set; } 21 | public int? PitchWidth { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | 5 | using System.Diagnostics; 6 | 7 | namespace BlazorServerCleanArchitecture.WebUI.Pages 8 | { 9 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 10 | [IgnoreAntiforgeryToken] 11 | public class ErrorModel : PageModel 12 | { 13 | public string? RequestId { get; set; } 14 | 15 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 16 | 17 | private readonly ILogger _logger; 18 | 19 | public ErrorModel(ILogger logger) 20 | { 21 | _logger = logger; 22 | } 23 | 24 | public void OnGet() 25 | { 26 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Extensions/IServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using MediatR; 3 | using System.Reflection; 4 | 5 | namespace BlazorServerCleanArchitecture.Application.Extensions 6 | { 7 | public static class IServiceCollectionExtensions 8 | { 9 | public static void AddApplicationLayer(this IServiceCollection services) 10 | { 11 | services.AddAutoMapper(); 12 | services.AddMediator(); 13 | } 14 | 15 | private static void AddAutoMapper(this IServiceCollection services) 16 | { 17 | services.AddAutoMapper(Assembly.GetExecutingAssembly()); 18 | } 19 | 20 | private static void AddMediator(this IServiceCollection services) 21 | { 22 | services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/BlazorServerCleanArchitecture.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/Repositories/StadiumRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorServerCleanArchitecture.Domain.Entities; 3 | using BlazorServerCleanArchitecture.Persistence.Contexts; 4 | 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Numerics; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace BlazorServerCleanArchitecture.Persistence.Repositories 15 | { 16 | public class StadiumRepository : IStadiumRepository 17 | { 18 | private readonly IGenericRepository _repository; 19 | 20 | public StadiumRepository(IGenericRepository repository) 21 | { 22 | _repository = repository; 23 | } 24 | 25 | public async Task> GetStadiumByCityAsync(string cityName) 26 | { 27 | return await _repository.Entities.Where(x => x.City == cityName).ToListAsync(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.WebUI.Data; 2 | 3 | using BlazorServerCleanArchitecture.Application.Extensions; 4 | using BlazorServerCleanArchitecture.Persistence.Extensions; 5 | 6 | 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Components; 9 | using Microsoft.AspNetCore.Components.Web; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Hosting; 12 | 13 | var builder = WebApplication.CreateBuilder(args); 14 | 15 | // Add services to the container. 16 | builder.Services.AddRazorPages(); 17 | builder.Services.AddServerSideBlazor(); 18 | builder.Services.AddSingleton(); 19 | 20 | builder.Services.AddApplicationLayer(); 21 | builder.Services.AddPersistenceLayer(builder.Configuration); 22 | 23 | var app = builder.Build(); 24 | 25 | // Configure the HTTP request pipeline. 26 | if (!app.Environment.IsDevelopment()) 27 | { 28 | app.UseExceptionHandler("/Error"); 29 | } 30 | 31 | 32 | app.UseStaticFiles(); 33 | 34 | app.UseRouting(); 35 | 36 | app.MapBlazorHub(); 37 | app.MapFallbackToPage("/_Host"); 38 | 39 | app.Run(); 40 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Features/Stadiums/Queries/GetAllStadiums/GetAllStadiumsQuery.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using AutoMapper.QueryableExtensions; 3 | 4 | using BlazorServerCleanArchitecture.Application.Interfaces.Repositories; 5 | using BlazorServerCleanArchitecture.Domain.Entities; 6 | 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace BlazorServerCleanArchitecture.Application.Features.Stadiums.Queries.GetAllStadiums 11 | { 12 | public record GetAllStadiumsQuery : IRequest>; 13 | 14 | internal class GetAllPlayersQueryHandler : IRequestHandler> 15 | { 16 | private readonly IUnitOfWork _unitOfWork; 17 | private readonly IMapper _mapper; 18 | 19 | public GetAllPlayersQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) 20 | { 21 | _unitOfWork = unitOfWork; 22 | _mapper = mapper; 23 | } 24 | 25 | public async Task> Handle(GetAllStadiumsQuery query, CancellationToken cancellationToken) 26 | { 27 | return await _unitOfWork.Repository().Entities 28 | .ProjectTo(_mapper.ConfigurationProvider) 29 | .ToListAsync(cancellationToken); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/Contexts/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Domain.Common; 2 | using BlazorServerCleanArchitecture.Domain.Common.Interfaces; 3 | using BlazorServerCleanArchitecture.Domain.Entities; 4 | 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | using System.Reflection; 8 | 9 | namespace BlazorServerCleanArchitecture.Persistence.Contexts 10 | { 11 | public class ApplicationDbContext : DbContext 12 | { 13 | 14 | public ApplicationDbContext(DbContextOptions options) 15 | : base(options) 16 | { 17 | 18 | } 19 | 20 | public DbSet Stadiums => Set(); 21 | 22 | protected override void OnModelCreating(ModelBuilder modelBuilder) 23 | { 24 | base.OnModelCreating(modelBuilder); 25 | modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); 26 | } 27 | 28 | public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) 29 | { 30 | return await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false); 31 | } 32 | 33 | public override int SaveChanges() 34 | { 35 | return SaveChangesAsync().GetAwaiter().GetResult(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/FetchData.razor: -------------------------------------------------------------------------------- 1 | @page "/fetchdata" 2 | @using BlazorServerCleanArchitecture.WebUI.Data 3 | @inject WeatherForecastService ForecastService 4 | 5 | Weather forecast 6 | 7 |

Weather forecast

8 | 9 |

This component demonstrates fetching data from a service.

10 | 11 | @if (forecasts == null) 12 | { 13 |

Loading...

14 | } 15 | else 16 | { 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | @foreach (var forecast in forecasts) 28 | { 29 | 30 | 31 | 32 | 33 | 34 | 35 | } 36 | 37 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
38 | } 39 | 40 | @code { 41 | private WeatherForecast[]? forecasts; 42 | 43 | protected override async Task OnInitializedAsync() 44 | { 45 | forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Microsoft.AspNetCore.Components.Web 3 | @namespace BlazorServerCleanArchitecture.WebUI.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | An error has occurred. This application may no longer respond until reloaded. 24 | 25 | 26 | An unhandled exception has occurred. See browser dev tools for details. 27 | 28 | Reload 29 | 🗙 30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/Stadiums.razor: -------------------------------------------------------------------------------- 1 | @page "/stadiums" 2 | @using BlazorServerCleanArchitecture.Application.Features.Stadiums.Queries.GetAllStadiums; 3 | @using MediatR 4 | @inject IMediator Mediator 5 | 6 | Stadiums 7 | 8 |

Stadiums

9 | 10 | @if (stadiums == null) 11 | { 12 |

Loading...

13 | } 14 | else 15 | { 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @foreach (var stadium in stadiums) 29 | { 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | } 39 | 40 |
NameCityCapacityBuilt YearPitch LengthPitch Width
@stadium.Name@stadium.City@stadium.Capacity@stadium.BuiltYear@stadium.PitchLength@stadium.PitchWidth
41 | } 42 | 43 | @code { 44 | private List? stadiums; 45 | 46 | protected override async Task OnInitializedAsync() 47 | { 48 | stadiums = await Mediator.Send(new GetAllStadiumsQuery()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | 63 | .nav-scrollable { 64 | /* Allow sidebar to scroll for tall menus */ 65 | height: calc(100vh - 3.5rem); 66 | overflow-y: auto; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | } 28 | 29 | .top-row a:first-child { 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | } 33 | 34 | @media (max-width: 640.98px) { 35 | .top-row:not(.auth) { 36 | display: none; 37 | } 38 | 39 | .top-row.auth { 40 | justify-content: space-between; 41 | } 42 | 43 | .top-row a, .top-row .btn-link { 44 | margin-left: 0; 45 | } 46 | } 47 | 48 | @media (min-width: 641px) { 49 | .page { 50 | flex-direction: row; 51 | } 52 | 53 | .sidebar { 54 | width: 250px; 55 | height: 100vh; 56 | position: sticky; 57 | top: 0; 58 | } 59 | 60 | .top-row { 61 | position: sticky; 62 | top: 0; 63 | z-index: 1; 64 | } 65 | 66 | .top-row, article { 67 | padding-left: 2rem !important; 68 | padding-right: 1.5rem !important; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/Repositories/GenericRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorServerCleanArchitecture.Domain.Common; 3 | using BlazorServerCleanArchitecture.Persistence.Contexts; 4 | 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace BlazorServerCleanArchitecture.Persistence.Repositories 8 | { 9 | public class GenericRepository : IGenericRepository where T : BaseAuditableEntity 10 | { 11 | private readonly ApplicationDbContext _dbContext; 12 | 13 | public GenericRepository(ApplicationDbContext dbContext) 14 | { 15 | _dbContext = dbContext; 16 | } 17 | 18 | public IQueryable Entities => _dbContext.Set(); 19 | 20 | public async Task AddAsync(T entity) 21 | { 22 | await _dbContext.Set().AddAsync(entity); 23 | return entity; 24 | } 25 | 26 | public Task UpdateAsync(T entity) 27 | { 28 | T exist = _dbContext.Set().Find(entity.Id); 29 | _dbContext.Entry(exist).CurrentValues.SetValues(entity); 30 | return Task.CompletedTask; 31 | } 32 | 33 | public Task DeleteAsync(T entity) 34 | { 35 | _dbContext.Set().Remove(entity); 36 | return Task.CompletedTask; 37 | } 38 | 39 | public async Task> GetAllAsync() 40 | { 41 | return await _dbContext 42 | .Set() 43 | .ToListAsync(); 44 | } 45 | 46 | public async Task GetByIdAsync(int id) 47 | { 48 | return await _dbContext.Set().FindAsync(id); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 9 | 10 | 34 | 35 | @code { 36 | private bool collapseNavMenu = true; 37 | 38 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 39 | 40 | private void ToggleNavMenu() 41 | { 42 | collapseNavMenu = !collapseNavMenu; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorServerCleanArchitecture.WebUI.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Error.

19 |

An error occurred while processing your request.

20 | 21 | @if (Model.ShowRequestId) 22 | { 23 |

24 | Request ID: @Model.RequestId 25 |

26 | } 27 | 28 |

Development Mode

29 |

30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |

32 |

33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |

38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/Extensions/IServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorServerCleanArchitecture.Persistence.Contexts; 3 | using BlazorServerCleanArchitecture.Persistence.Repositories; 4 | 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Configuration; 7 | using System.Reflection; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace BlazorServerCleanArchitecture.Persistence.Extensions 11 | { 12 | public static class IServiceCollectionExtensions 13 | { 14 | public static void AddPersistenceLayer(this IServiceCollection services, IConfiguration configuration) 15 | { 16 | //services.AddMappings(); 17 | services.AddDbContext(configuration); 18 | services.AddRepositories(); 19 | } 20 | 21 | //private static void AddMappings(this IServiceCollection services) 22 | //{ 23 | // services.AddAutoMapper(Assembly.GetExecutingAssembly()); 24 | //} 25 | 26 | public static void AddDbContext(this IServiceCollection services, IConfiguration configuration) 27 | { 28 | var connectionString = configuration.GetConnectionString("DefaultConnection"); 29 | 30 | services.AddDbContext(options => 31 | options.UseSqlServer(connectionString, 32 | builder => builder.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName))); 33 | } 34 | 35 | private static void AddRepositories(this IServiceCollection services) 36 | { 37 | services 38 | .AddTransient(typeof(IUnitOfWork), typeof(UnitOfWork)) 39 | .AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>)) 40 | .AddTransient(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Application/Common/Mappings/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | using System.Reflection; 4 | 5 | namespace BlazorServerCleanArchitecture.Application.Common.Mappings 6 | { 7 | public class MappingProfile : Profile 8 | { 9 | public MappingProfile() 10 | { 11 | ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly()); 12 | } 13 | 14 | private void ApplyMappingsFromAssembly(Assembly assembly) 15 | { 16 | var mapFromType = typeof(IMapFrom<>); 17 | 18 | var mappingMethodName = nameof(IMapFrom.Mapping); 19 | 20 | bool HasInterface(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == mapFromType; 21 | 22 | var types = assembly.GetExportedTypes().Where(t => t.GetInterfaces().Any(HasInterface)).ToList(); 23 | 24 | var argumentTypes = new Type[] { typeof(Profile) }; 25 | 26 | foreach (var type in types) 27 | { 28 | var instance = Activator.CreateInstance(type); 29 | 30 | var methodInfo = type.GetMethod(mappingMethodName); 31 | 32 | if (methodInfo != null) 33 | { 34 | methodInfo.Invoke(instance, new object[] { this }); 35 | } 36 | else 37 | { 38 | var interfaces = type.GetInterfaces().Where(HasInterface).ToList(); 39 | 40 | if (interfaces.Count > 0) 41 | { 42 | foreach (var @interface in interfaces) 43 | { 44 | var interfaceMethodInfo = @interface.GetMethod(mappingMethodName, argumentTypes); 45 | 46 | interfaceMethodInfo.Invoke(instance, new object[] { this }); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.Persistence/Repositories/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerCleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorServerCleanArchitecture.Domain.Common; 3 | using BlazorServerCleanArchitecture.Domain.Common.Interfaces; 4 | using BlazorServerCleanArchitecture.Persistence.Contexts; 5 | 6 | using System.Collections; 7 | 8 | namespace BlazorServerCleanArchitecture.Persistence.Repositories 9 | { 10 | public class UnitOfWork : IUnitOfWork 11 | { 12 | private readonly ApplicationDbContext _dbContext; 13 | private Hashtable _repositories; 14 | private bool disposed; 15 | 16 | public UnitOfWork(ApplicationDbContext dbContext) 17 | { 18 | _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); 19 | } 20 | 21 | public IGenericRepository Repository() where T : BaseAuditableEntity 22 | { 23 | if (_repositories == null) 24 | _repositories = new Hashtable(); 25 | 26 | var type = typeof(T).Name; 27 | 28 | if (!_repositories.ContainsKey(type)) 29 | { 30 | var repositoryType = typeof(GenericRepository<>); 31 | 32 | var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), _dbContext); 33 | 34 | _repositories.Add(type, repositoryInstance); 35 | } 36 | 37 | return (IGenericRepository) _repositories[type]; 38 | } 39 | 40 | public Task Rollback() 41 | { 42 | _dbContext.ChangeTracker.Entries().ToList().ForEach(x => x.Reload()); 43 | return Task.CompletedTask; 44 | } 45 | 46 | public async Task Save(CancellationToken cancellationToken) 47 | { 48 | return await _dbContext.SaveChangesAsync(cancellationToken); 49 | } 50 | 51 | public Task SaveAndRemoveCache(CancellationToken cancellationToken, params string[] cacheKeys) 52 | { 53 | throw new NotImplementedException(); 54 | } 55 | 56 | public void Dispose() 57 | { 58 | Dispose(true); 59 | GC.SuppressFinalize(this); 60 | } 61 | 62 | protected virtual void Dispose(bool disposing) 63 | { 64 | if (disposed) 65 | { 66 | if (disposing) 67 | { 68 | //dispose managed resources 69 | _dbContext.Dispose(); 70 | } 71 | } 72 | //dispose unmanaged resources 73 | disposed = true; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.WebUI/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | h1:focus { 8 | outline: none; 9 | } 10 | 11 | a, .btn-link { 12 | color: #0071c1; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 22 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 23 | } 24 | 25 | .content { 26 | padding-top: 1.1rem; 27 | } 28 | 29 | .valid.modified:not([type=checkbox]) { 30 | outline: 1px solid #26b050; 31 | } 32 | 33 | .invalid { 34 | outline: 1px solid red; 35 | } 36 | 37 | .validation-message { 38 | color: red; 39 | } 40 | 41 | #blazor-error-ui { 42 | background: lightyellow; 43 | bottom: 0; 44 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 45 | display: none; 46 | left: 0; 47 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 48 | position: fixed; 49 | width: 100%; 50 | z-index: 1000; 51 | } 52 | 53 | #blazor-error-ui .dismiss { 54 | cursor: pointer; 55 | position: absolute; 56 | right: 0.75rem; 57 | top: 0.5rem; 58 | } 59 | 60 | .blazor-error-boundary { 61 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 62 | padding: 1rem 1rem 1rem 3.7rem; 63 | color: white; 64 | } 65 | 66 | .blazor-error-boundary::after { 67 | content: "An error has occurred." 68 | } 69 | -------------------------------------------------------------------------------- /BlazorServerCleanArchitecture.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{C1B0778C-B1C2-47EA-A903-4867AB6EAE9D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{21CB9554-7556-4F72-BA8A-4119941ABEA2}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentation", "{135A1223-CCDB-4C19-992A-7FA3F1DED578}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerCleanArchitecture.Domain", "BlazorServerCleanArchitecture.Domain\BlazorServerCleanArchitecture.Domain.csproj", "{10BAC793-E276-4B2B-98EE-1FE5194C7ABB}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerCleanArchitecture.Application", "BlazorServerCleanArchitecture.Application\BlazorServerCleanArchitecture.Application.csproj", "{B03062D4-78FD-4073-B39E-BDAB4536B7D7}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerCleanArchitecture.Persistence", "BlazorServerCleanArchitecture.Persistence\BlazorServerCleanArchitecture.Persistence.csproj", "{4E75FE72-3666-4B96-AFA0-9B98C17EB90E}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServerCleanArchitecture.WebUI", "BlazorServerCleanArchitecture.WebUI\BlazorServerCleanArchitecture.WebUI.csproj", "{065AA266-9D1F-4457-BEDF-F31B76779441}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {10BAC793-E276-4B2B-98EE-1FE5194C7ABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {10BAC793-E276-4B2B-98EE-1FE5194C7ABB}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {10BAC793-E276-4B2B-98EE-1FE5194C7ABB}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {10BAC793-E276-4B2B-98EE-1FE5194C7ABB}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {B03062D4-78FD-4073-B39E-BDAB4536B7D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {B03062D4-78FD-4073-B39E-BDAB4536B7D7}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {B03062D4-78FD-4073-B39E-BDAB4536B7D7}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {B03062D4-78FD-4073-B39E-BDAB4536B7D7}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {4E75FE72-3666-4B96-AFA0-9B98C17EB90E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {4E75FE72-3666-4B96-AFA0-9B98C17EB90E}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {4E75FE72-3666-4B96-AFA0-9B98C17EB90E}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {4E75FE72-3666-4B96-AFA0-9B98C17EB90E}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {065AA266-9D1F-4457-BEDF-F31B76779441}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {065AA266-9D1F-4457-BEDF-F31B76779441}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {065AA266-9D1F-4457-BEDF-F31B76779441}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {065AA266-9D1F-4457-BEDF-F31B76779441}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(NestedProjects) = preSolution 47 | {10BAC793-E276-4B2B-98EE-1FE5194C7ABB} = {C1B0778C-B1C2-47EA-A903-4867AB6EAE9D} 48 | {B03062D4-78FD-4073-B39E-BDAB4536B7D7} = {C1B0778C-B1C2-47EA-A903-4867AB6EAE9D} 49 | {4E75FE72-3666-4B96-AFA0-9B98C17EB90E} = {21CB9554-7556-4F72-BA8A-4119941ABEA2} 50 | {065AA266-9D1F-4457-BEDF-F31B76779441} = {135A1223-CCDB-4C19-992A-7FA3F1DED578} 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {A8527184-1F74-4699-9A46-69C2D26590BB} 54 | EndGlobalSection 55 | EndGlobal 56 | --------------------------------------------------------------------------------