Skip to content

Instantly share code, notes, and snippets.

@gozilla-paradise
Last active September 21, 2021 16:07
Show Gist options
  • Save gozilla-paradise/d15be9aea60f73441f4b572d1a1af025 to your computer and use it in GitHub Desktop.
Save gozilla-paradise/d15be9aea60f73441f4b572d1a1af025 to your computer and use it in GitHub Desktop.
[How to create new entity using Blazor Hero Template] How to create new entity using Blazor Hero Template #BlazorHero
1. At Core/Domain/Entities:
Create Entity
2. At Infrastructure/Context/BlazorHeroContext.cs
Add DbSet
3. At Application/Interface/Repositories
Add Repository Interface
4. At Infrastructure/Repository
Add Repository Implement Interface Above
5. At Application/Features
Add CQRS
(Optional) At Application/Features/Dashboard
Add additional dashboard data
6.At Application/Mapping
Configure Automapper for CQRS <-> Object
7. At Application/Specifications
Add Entity Filters Criteria
8. At Infrastructure/Extensions/ServiceCollectionExtensions.cs
Add Service Repository to Dependency Injection
// Register Server Side
9. At Web/Server/Controllers
Add Entity Controller
// Register Client Side
10. At Web/Client/Pages
Add Page for Entity
using Ruaybet.Backoffice.Domain.Contracts;
using System.ComponentModel.DataAnnotations.Schema;
namespace Ruaybet.Backoffice.Domain.Entities.Catalog
{
public class Product : AuditableEntity<int>
{
public string Name { get; set; }
public string Barcode { get; set; }
[Column(TypeName = "text")]
public string ImageDataURL { get; set; }
public string Description { get; set; }
public decimal Rate { get; set; }
public int BrandId { get; set; }
public virtual Brand Brand { get; set; }
}
}
public DbSet<Product> Products { get; set; }
using System.Threading.Tasks;
namespace Ruaybet.Backoffice.Application.Interfaces.Repositories
{
public interface IProductRepository
{
Task<bool> IsBrandUsed(int brandId);
}
}
using Ruaybet.Backoffice.Application.Interfaces.Repositories;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
namespace Ruaybet.Backoffice.Infrastructure.Repositories
{
public class ProductRepository : IProductRepository
{
private readonly IRepositoryAsync<Product, int> _repository;
public ProductRepository(IRepositoryAsync<Product, int> repository)
{
_repository = repository;
}
public async Task<bool> IsBrandUsed(int brandId)
{
return await _repository.Entities.AnyAsync(b => b.BrandId == brandId);
}
}
}
using System.ComponentModel.DataAnnotations;
using AutoMapper;
using Ruaybet.Backoffice.Application.Interfaces.Repositories;
using Ruaybet.Backoffice.Application.Interfaces.Services;
using Ruaybet.Backoffice.Application.Requests;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
using Ruaybet.Backoffice.Shared.Wrapper;
using MediatR;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace Ruaybet.Backoffice.Application.Features.Products.Commands.AddEdit
{
public partial class AddEditProductCommand : IRequest<Result<int>>
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Barcode { get; set; }
[Required]
public string Description { get; set; }
public string ImageDataURL { get; set; }
[Required]
public decimal Rate { get; set; }
[Required]
public int BrandId { get; set; }
public UploadRequest UploadRequest { get; set; }
}
internal class AddEditProductCommandHandler : IRequestHandler<AddEditProductCommand, Result<int>>
{
private readonly IMapper _mapper;
private readonly IUnitOfWork<int> _unitOfWork;
private readonly IUploadService _uploadService;
private readonly IStringLocalizer<AddEditProductCommandHandler> _localizer;
public AddEditProductCommandHandler(IUnitOfWork<int> unitOfWork, IMapper mapper, IUploadService uploadService, IStringLocalizer<AddEditProductCommandHandler> localizer)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
_uploadService = uploadService;
_localizer = localizer;
}
public async Task<Result<int>> Handle(AddEditProductCommand command, CancellationToken cancellationToken)
{
if (await _unitOfWork.Repository<Product>().Entities.Where(p => p.Id != command.Id)
.AnyAsync(p => p.Barcode == command.Barcode, cancellationToken))
{
return await Result<int>.FailAsync(_localizer["Barcode already exists."]);
}
var uploadRequest = command.UploadRequest;
if (uploadRequest != null)
{
uploadRequest.FileName = $"P-{command.Barcode}{uploadRequest.Extension}";
}
if (command.Id == 0)
{
var product = _mapper.Map<Product>(command);
if (uploadRequest != null)
{
product.ImageDataURL = _uploadService.UploadAsync(uploadRequest);
}
await _unitOfWork.Repository<Product>().AddAsync(product);
await _unitOfWork.Commit(cancellationToken);
return await Result<int>.SuccessAsync(product.Id, _localizer["Product Saved"]);
}
else
{
var product = await _unitOfWork.Repository<Product>().GetByIdAsync(command.Id);
if (product != null)
{
product.Name = command.Name ?? product.Name;
product.Description = command.Description ?? product.Description;
if (uploadRequest != null)
{
product.ImageDataURL = _uploadService.UploadAsync(uploadRequest);
}
product.Rate = (command.Rate == 0) ? product.Rate : command.Rate;
product.BrandId = (command.BrandId == 0) ? product.BrandId : command.BrandId;
await _unitOfWork.Repository<Product>().UpdateAsync(product);
await _unitOfWork.Commit(cancellationToken);
return await Result<int>.SuccessAsync(product.Id, _localizer["Product Updated"]);
}
else
{
return await Result<int>.FailAsync(_localizer["Product Not Found!"]);
}
}
}
}
}
using Ruaybet.Backoffice.Application.Interfaces.Repositories;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
using Ruaybet.Backoffice.Shared.Wrapper;
using MediatR;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
namespace Ruaybet.Backoffice.Application.Features.Products.Commands.Delete
{
public class DeleteProductCommand : IRequest<Result<int>>
{
public int Id { get; set; }
}
internal class DeleteProductCommandHandler : IRequestHandler<DeleteProductCommand, Result<int>>
{
private readonly IUnitOfWork<int> _unitOfWork;
private readonly IStringLocalizer<DeleteProductCommandHandler> _localizer;
public DeleteProductCommandHandler(IUnitOfWork<int> unitOfWork, IStringLocalizer<DeleteProductCommandHandler> localizer)
{
_unitOfWork = unitOfWork;
_localizer = localizer;
}
public async Task<Result<int>> Handle(DeleteProductCommand command, CancellationToken cancellationToken)
{
var product = await _unitOfWork.Repository<Product>().GetByIdAsync(command.Id);
if (product != null)
{
await _unitOfWork.Repository<Product>().DeleteAsync(product);
await _unitOfWork.Commit(cancellationToken);
return await Result<int>.SuccessAsync(product.Id, _localizer["Product Deleted"]);
}
else
{
return await Result<int>.FailAsync(_localizer["Product Not Found!"]);
}
}
}
}
using Ruaybet.Backoffice.Application.Extensions;
using Ruaybet.Backoffice.Application.Interfaces.Repositories;
using Ruaybet.Backoffice.Application.Specifications.Catalog;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
using Ruaybet.Backoffice.Shared.Wrapper;
using MediatR;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
namespace Ruaybet.Backoffice.Application.Features.Products.Queries.GetAllPaged
{
public class GetAllProductsQuery : IRequest<PaginatedResult<GetAllPagedProductsResponse>>
{
public int PageNumber { get; set; }
public int PageSize { get; set; }
public string SearchString { get; set; }
public string[] OrderBy { get; set; } // of the form fieldname [ascending|descending],fieldname [ascending|descending]...
public GetAllProductsQuery(int pageNumber, int pageSize, string searchString, string orderBy)
{
PageNumber = pageNumber;
PageSize = pageSize;
SearchString = searchString;
if (!string.IsNullOrWhiteSpace(orderBy))
{
OrderBy = orderBy.Split(',');
}
}
}
internal class GetAllProductsQueryHandler : IRequestHandler<GetAllProductsQuery, PaginatedResult<GetAllPagedProductsResponse>>
{
private readonly IUnitOfWork<int> _unitOfWork;
public GetAllProductsQueryHandler(IUnitOfWork<int> unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<PaginatedResult<GetAllPagedProductsResponse>> Handle(GetAllProductsQuery request, CancellationToken cancellationToken)
{
Expression<Func<Product, GetAllPagedProductsResponse>> expression = e => new GetAllPagedProductsResponse
{
Id = e.Id,
Name = e.Name,
Description = e.Description,
Rate = e.Rate,
Barcode = e.Barcode,
Brand = e.Brand.Name,
BrandId = e.BrandId
};
var productFilterSpec = new ProductFilterSpecification(request.SearchString);
if (request.OrderBy?.Any() != true)
{
var data = await _unitOfWork.Repository<Product>().Entities
.Specify(productFilterSpec)
.Select(expression)
.ToPaginatedListAsync(request.PageNumber, request.PageSize);
return data;
}
else
{
var ordering = string.Join(",", request.OrderBy); // of the form fieldname [ascending|descending], ...
var data = await _unitOfWork.Repository<Product>().Entities
.Specify(productFilterSpec)
.OrderBy(ordering) // require system.linq.dynamic.core
.Select(expression)
.ToPaginatedListAsync(request.PageNumber, request.PageSize);
return data;
}
}
}
}
using AutoMapper;
using Ruaybet.Backoffice.Application.Features.Products.Commands.AddEdit;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
namespace Ruaybet.Backoffice.Application.Mappings
{
public class ProductProfile : Profile
{
public ProductProfile()
{
CreateMap<AddEditProductCommand, Product>().ReverseMap();
}
}
}
using Ruaybet.Backoffice.Application.Specifications.Base;
using Ruaybet.Backoffice.Domain.Entities.Catalog;
namespace Ruaybet.Backoffice.Application.Specifications.Catalog
{
public class ProductFilterSpecification : HeroSpecification<Product>
{
public ProductFilterSpecification(string searchString)
{
Includes.Add(a => a.Brand);
if (!string.IsNullOrEmpty(searchString))
{
Criteria = p => p.Barcode != null && (p.Name.Contains(searchString) || p.Description.Contains(searchString) || p.Barcode.Contains(searchString) || p.Brand.Name.Contains(searchString));
}
else
{
Criteria = p => p.Barcode != null;
}
}
}
}
public static IServiceCollection AddRepositories(this IServiceCollection services)
{
return services
.AddTransient(typeof(IRepositoryAsync<,>), typeof(RepositoryAsync<,>))
.AddTransient<IProductRepository, ProductRepository>()
.AddTransient<IBrandRepository, BrandRepository>()
.AddTransient<IDocumentRepository, DocumentRepository>()
.AddTransient<IDocumentTypeRepository, DocumentTypeRepository>()
.AddTransient<IBankingScbRepository, BankingScbRepository>()
.AddTransient(typeof(IUnitOfWork<>), typeof(UnitOfWork<>));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment