Aplikacja C# od Zera Architektura,CQRS : Dobre Praktyki

Cezary Walenciuk

Aplikacja C# od Zera
Architektura, CQRS : Dobre Praktyki

@walenciukC

Speaker

Słuchaj przez te 10 lat programowałem

Dlaczego warto znać wzorce i style architektury?

ASP.NET Web Form : jak kiedyś się uczyłe tworzyć swoją pierwszą aplikacje

                    
                        <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                            ConnectionString="<%$ ConnectionStrings:SklepConnectionString1 %>" 
                            DeleteCommand="DELETE FROM [Products] WHERE [ProductId] = @ProductId" 
                            InsertCommand="INSERT INTO [Products] ([ProductName], [RecommendedRetailPrice],
                            [Selling Price]) VALUES (@ProductName, @RecommendedRetailPrice, @Selling_Price)" 
                            ProviderName="<%$ ConnectionStrings:SklepConnectionString1.ProviderName %>" 
                            SelectCommand="SELECT [ProductId], [ProductName], [RecommendedRetailPrice],
                            [Selling Price] AS Selling_Price FROM [Products]"
                            UpdateCommand="UPDATE [Products] SET [ProductName] = @ProductName, 
                            [RecommendedRetailPrice] = @RecommendedRetailPrice,
                            [Selling Price] = @Selling_Price WHERE [ProductId] = @ProductId">
                            <DeleteParameters>
                                <asp:Parameter Name="ProductId" Type="Int32" />
                            </DeleteParameters>
                            <InsertParameters>
                                <asp:Parameter Name="ProductName" Type="String" />
                                <asp:Parameter Name="RecommendedRetailPrice" Type="Decimal" />
                                <asp:Parameter Name="Selling_Price" Type="Decimal" />
                            </InsertParameters>
                            <UpdateParameters>
                                <asp:Parameter Name="ProductName" Type="String" />
                                <asp:Parameter Name="RecommendedRetailPrice" Type="Decimal" />
                                <asp:Parameter Name="Selling_Price" Type="Decimal" />
                                <asp:Parameter Name="ProductId" Type="Int32" />
                            </UpdateParameters>
                        </asp:SqlDataSource>
                    
                

blog
Copyright © 2021 Diagram
blog
Copyright © 2021 Diagram
UI nie powinno być zależne od dostępu do danych
blog
Copyright © 2021 Diagram
W roku 2021 ta książka ma już przestarzałe rady
Często zadaje sobie pytanie czy moja wiedza jest na czasie

Czy twoja wiedza jest na czasie.

Najlepsze prakyki?

Czy twoja wiedza jest na czasie.

Najlepsze prakyki?

Paczki NuGet?

Czy twoja wiedza jest na czasie.

Najlepsze prakyki?

Paczki NuGet?

Jasna architektura

Czy twoja wiedza jest na czasie.

Najlepsze prakyki?

Paczki NuGet?

Jasna architektura

Testowalna, Łatwa do zarządzania

Jak to napisać ?

Czy dla mnie są te "dobre praktyki" ?

ASP.NET CORE w pigułce
blog
Copyright © 2021 Diagram
Gdybyś miał napisać aplikacje od zera to od czego byś zaczął?
🤔
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
My nawet chcemy wydzielić warstwę UI tak aby mogło to być...
🤯
blog
Copyright © 2021 Cezary Walenciuk
Co budujemy ?
🙈🙉🙊
Poznaj
😲 EdoZbieracza 😲
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Diagram
blog
Copyright © 2021 Diagram
blog
Copyright © 2021 Diagram
blog
Copyright © 2021 Diagram

Czyli mamy

Poznaj
🧐 Podstawy architektury 🧐

Ważne zasady projektowe

Dependency Inversion

Ważne zasady projektowe

Dependency Inversion
Seperation of Concerns

Ważne zasady projektowe

Dependency Inversion
Seperation of Concerns
Single Responsibility

Ważne zasady projektowe

Dependency Inversion
Seperation of Concerns
Single Responsibility
DRY

Ważne zasady projektowe

Dependency Inversion
Seperation of Concerns
Single Responsibility
DRY
Persistence Ignorance
Wstrzykiwanie zależności
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
  1. Seperation of Concerns
  1. Single Responsibility
  1. DRY
  1. Persistence Ignorance
🧐 Typy architektury 🧐
blog
Copyright © 2021 Cezary Walenciuk

Typy architektury

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Czyli warstwy to foldery
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
To w czym jest problem ?

To w czym jest problem

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Typy architektury

  1. Wszystko w jednym
  2. "Warsztwy" z 3 albo 5

Mamy dwa style Clean Architecture

blog
Copyright © 2021 https://bulldogjob.pl/news/1145-architektura-heksagonalna-w-javie-w-3-minuty
blog
Copyright © 2021 https://bulldogjob.pl/news/1145-architektura-heksagonalna-w-javie-w-3-minuty
blog
Copyright © 2021 https://bulldogjob.pl/news/1145-architektura-heksagonalna-w-javie-w-3-minuty
Jak skoncentruje się na Cebuli ?
🧅
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk

Dwie najważnieszje zasady

Depedency Inversion i Injection

Dwie najważnieszje zasady

Depedency Inversion i Injection

Mediator i CQRS

...i CQRS

Command-Query
Responsibility
Segregation

...i CQRS

Command-Query
Responsibility
Segregation

Róźne modele
Command do aktualizacji danych
Query do odczytu

...i CQRS

Command-Query
Responsibility
Segregation

Róźne modele
Command do aktualizacji danych
Query do odczytu

Każdy
ma swoją odpowiedzialność
 

Co idzie gdzie ?
😵
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Nazewnictwo klas domain powinno być najkrótsze

Jak byś nazwał "kurs wideo" ?

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Post

                    
                        public class Post
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string Description { get; set; }
                    
                            public int CategoryId { get; set; }
                            public Category Category { get; set; }
                    
                            public string ImageUrl { get; set; }
                            public string Url { get; set; }
                            public int Rate { get; set; }
                        }
                    
                

Category

                    
                        public class Category
                        {
                            public int CategoryId { get; set; }
                            public string Name { get; set; }
                                           
                            public string DisplayName { get; set; }
                            public ICollection<Post> Posts { get; set; }
                        }
                    
                

Webinars

                    
                        public class Webinars
                        {
                            public int Id { get; set; }
                    
                            public string Title { get; set; }
                            public string Description { get; set; }
                    
                            public string ImageUrl { get; set; }
                            public string FacebookEventUrl { get; set; }
                            public string SlidesUrl { get; set; }
                    
                            public string WatchFacebookLink { get; set; }
                            public string WatchYoutubeLink { get; set; }

                            public DateTime Date { get; set; }
                            public bool AlreadyHappend { get; set; }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

AuditableEntity

                    
                        public class AuditableEntity
                        {
                            public string CreatedBy { get; set; }
                    
                            public DateTime CreatedDate { get; set; }
                    
                            public string LastModifiedBy { get; set; }

                            public DateTime? LastModifiedDate { get; set; }
                        }
                    
                

AuditableEntity

                    
                        public class Category : AuditableEntity
                        {
                            public int CategoryId { get; set; }
                        
                            public string Name { get; set; }
                        
                            public string DisplayName { get; set; }
                        
                            public ICollection<Post> Posts { get; set; }
                        }
                    
                

Mamy klasy domenowe ?
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
Skorzystamy z wzorca Repository ?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

IAsyncRepository

                    
                        public interface IAsyncRepository<T> where T : class
                        {
                            Task<T> GetByIdAsync(int id);
                        
                            Task<IReadOnlyList<T>> GetAllAsync();
                        
                            Task<T> AddAsync(T entity);
                        
                            Task UpdateAsync(T entity);
                        
                            Task DeleteAsync(T entity);
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

IPostRepository

                    
                        public interface IPostRepository 
                            : IAsyncRepository<Post>
                        {
                        }
                    
                

ICategoryRepository

                    
                        public interface ICategoryRepository 
                            : IAsyncRepository<Category>
                        {
                        }
                    
                

IWebinaryRepository

                    
                        public interface IWebinaryRepository 
                            : IAsyncRepository<Webinars>
                        {
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Wzorzec projektowy Mediator
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Przykład query

                    
                        public class GetVideoCoursesListQuery 
                        : IRequest<List<VideoCoursesVideModel>> 
                        { 


                        }
                    
                

Przykład obsługi zapytania

                    
                        public class GetVideoCoursesQueryHandler 
                        : IRequestHandler<GetVideoCoursesListQuery, List<VideoCoursesVideModel>>
                        {
                            public async Task<List<VideoCoursesVideModel>> Handle
                                (GetVideoCoursesListQuery request, 
                                CancellationToken cancellationToken)
                            {
                        
                            }
                        }
                    
                

Problem z mapowaniem obiektów ?
😨
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Przykład AutoMapper

                    
                        private User TranslateUser(UserViewModel userView)
                        {
                            return new User()
                            {
                                FirstName = userView.FirstName,
                                LastName = userView.LastName,
                                Login = userView.Login,
                                Role = userView.Role,
                                UserId = userView.UserId
                            };
                        }
                    
                

Przykład pisania swojego kodu

                    









                        public static T2 ConvertToTheSameProperties<T1,T2>(T1  employee) where T2 : new()
                        {
                            var prop = employee.GetType().GetProperties();
                            var prop2 = typeof(T2).GetProperties();
                        
                            T2 u = new T2();
                        
                            foreach (var propertyInfo in prop)
                            {
                        
                               bool propValue = propertyInfo.PropertyType == typeof (string) ||
                                             propertyInfo.PropertyType == typeof (int) ||
                                             propertyInfo.PropertyType == typeof (long) ||
                                             propertyInfo.PropertyType == typeof (Guid) ||
                                             propertyInfo.PropertyType == typeof (bool) ||
                                             propertyInfo.PropertyType == typeof (bool?);
                        
                                foreach (var info in prop2)
                                {
                        
                                    if (propertyInfo.Name == info.Name && 
                                        info.CanWrite && 
                                        propertyInfo.CanRead)
                                    {
                        
                                        if (propValue && propertyInfo.PropertyType == info.PropertyType)
                        
                                            info.SetValue(u, propertyInfo.GetValue(employee));
                                    }
                                }
                            }
                        
                            return u;
                        }
                    
                

Na pomoc przychodzi AutoMapper ?

Przykład AutoMapper

                    



                        [ApiController]
                        [Route("[controller]")]
                        public class EmployeeController : Controller
                        {
                            private readonly IMapper _mapper;
                        
                            public Employee Index()
                            {
                                EmployeeDto eDTO = new EmployeeDto()
                                {
                                    EmployeeId = 121,
                                    FirstName = "Damian",
                                    LastName = "Morfeus",
                                    Login = "frankodomanamiga",
                                    OrganizationCompanyName = "LichyDzwig",
                                    OrganizationId = 34,
                                    Role = "Sprzątacz podłogi pietra 34",
                                    RootOrganizationCompanyName = "Dobra Winda",
                                    RootOrganizationId = 12,
                                    Type = "Sprzątacz"
                                };
                        
                                return _mapper.Map<Employee>(eDTO);
                            }
                        }
                    
                

Przykład AutoMapper

                    
                        public class AutoMapperProfile : Profile
                        {
                            public AutoMapperProfile()
                            {
                                CreateMap<EmployeeDto, Employee>();
                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Projekt EduZbieracz.Domain

                    
                      <Project Sdk="Microsoft.NET.Sdk">

                        <PropertyGroup>
                          <TargetFramework>netstandard2.0</TargetFramework>
                        </PropertyGroup>
                      
                        <ItemGroup>
                          <ProjectReference Include="..\EduZbieracz.Domain\EduZbieracz.Domain.csproj" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <Folder Include="Functions\" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <PackageReference Include="AutoMapper" Version="10.1.1" />
                          <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
                          <PackageReference Include="MediatR" Version="9.0.0" />
                        </ItemGroup>
                      
                      </Project>
                    
                

blog
Copyright © 2021 Cezary Walenciuk
Stwórzmy Query dla wpisu który będzie wyświetlany na liście

PostInListViewModel

                    
                        public class PostInListViewModel
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public DateTime Date { get; set; }
                            public string ImageUrl { get; set; }
                            public int Rate { get; set; }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

GetPostsListQuery

                    
                        public class GetPostsListQuery 
                        : IRequest<List<PostInListViewModel>>
                        {

                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

GetPostsListQueryHandler

                    
                        public class GetPostsListQueryHandler :
                        IRequestHandler<GetPostsListQuery, List<PostInListViewModel>>
                        {
                            public Task<List<PostInListViewModel>>
                                Handle(GetPostsListQuery request,
                                CancellationToken cancellationToken)
                            {
                                throw new NotImplementedException();
                            }
                        }
                    
                

GetPostsListQueryHandler

                    
                        public class GetPostsListQueryHandler :
                        IRequestHandler<GetPostsListQuery, List<PostInListViewModel>>
                        {
                            private readonly IAsyncRepository<Post> _postRepository;
                            private readonly IMapper _mapper;
                        
                            public GetPostsListQueryHandler
                                (IMapper mapper,
                                IAsyncRepository<Post> postRepository)
                            {
                                _mapper = mapper;
                                _postRepository = postRepository;
                            }
                        
                            public async Task<List<PostInListViewModel>>
                                Handle(GetPostsListQuery request,
                                CancellationToken cancellationToken)
                            {
                                var all = await _postRepository.GetAllAsync();
                                var allordered = all.OrderBy(x => x.Date);
                        
                                return _mapper.Map<List<PostInListViewModel>>(all);
                            }
                        }
                    
                

GetPostsListQueryHandler ZOOM

                    
                        private readonly IAsyncRepository<Post> _postRepository;
                        private readonly IMapper _mapper;
                    
                        public GetPostsListQueryHandler
                            (IMapper mapper,
                            IAsyncRepository<Post> postRepository)
                        {
                            _mapper = mapper;
                            _postRepository = postRepository;
                        }
                    
                

GetPostsListQueryHandler ZOOM 2

                    
                        public async Task<List<PostInListViewModel>>
                        Handle(GetPostsListQuery request,
                        CancellationToken cancellationToken)
                        {
                            var all = await _postRepository.GetAllAsync();
                            var allordered = all.OrderBy(x => x.Date);
                    
                            return _mapper.Map<List<PostInListViewModel>>(all);
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

MappingProfile dla Warstwy API

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                            }
                        }
                    
                

Stwórzmy Query dla pobieranie dokładnych danych dla

PostDetailViewModel dla GetPostDetailQuery

                    
                        public class PostDetailViewModel
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string Description { get; set; }
                    
                            public CategoryDto Category { get; set; }
                    
                            public string ImageUrl { get; set; }
                            public string Url { get; set; }
                            public int Rate { get; set; }
                        }
                    
                

CategoryDto dla GetPostDetailQuery

                    
                        public class CategoryDto
                        {
                            public int CategoryId { get; set; }
                    
                            public string Name { get; set; }
                    
                            public string DisplayName { get; set; }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

GetPostDetailQuery dla GetPostDetailQuery

                    
                        public class GetPostDetailQuery 
                            : IRequest<PostDetailViewModel>
                        {

                            public int Id { get; set; }
                        }
                    
                

GetPostDetailQueryHandler dla GetPostDetailQuery

                    















                        public class GetPostDetailQueryHandler :
                        IRequestHandler<GetPostDetailQuery, PostDetailViewModel>
                        {
                            private readonly IAsyncRepository<Post> _postRepository;
                            private readonly IAsyncRepository<Category> _categoryRepository;
                            private readonly IMapper _mapper;
                        


                            public GetPostDetailQueryHandler(
                                IAsyncRepository<Post> postRepository,
                                IAsyncRepository<Category> categoryRepository,
                                IMapper mapper)
                            {
                                _postRepository = postRepository;
                                _categoryRepository = categoryRepository;
                                _mapper = mapper;
                            }
                        



                            public async Task<PostDetailViewModel> Handle
                                (GetPostDetailQuery request,
                                CancellationToken cancellationToken)
                            {
                                var post = await _postRepository.GetByIdAsync(request.Id);
                                var postdetail = _mapper.Map<PostDetailViewModel>(post);
                        
                                var category = await _categoryRepository.GetByIdAsync(post.CategoryId);
                        
                                postdetail.Category = _mapper.Map<CategoryDto>(category);
                        
                                return postdetail;
                            }
                        }
                    
                

MappingProfile Dla Appliaction : 2

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                            }
                        }
                    
                

Trzeba to wszystko zainstalować
🧐
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

GetPostsListQueryHandler

                    
                        public static class AddEduZbieraczApplication
                        {
                            public static IServiceCollection AddEduZbieraczApplication(this IServiceCollection services)
                            {
                                services.AddAutoMapper(Assembly.GetExecutingAssembly());
                                services.AddMediatR(Assembly.GetExecutingAssembly());
                            
                                return services;
                            }
                        }
                    
                

CQRS
😌
Dlaczego nie stworzyć klasę "Service" ?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Napiszmy Query dla pobierania kategori?

CategoryInListViewModel

                    
                        public class CategoryInListViewModel
                        {
                            public int CategoryId { get; set; }
                    
                            public string Name { get; set; }
                    
                            public string DisplayName { get; set; }
                        }
                    
                

GetCategoriesListQuery

                    
                        public class GetCategoriesListQuery
                        : IRequest<List<CategoryInListViewModel>>
                        {
                            
                        }
                    
                

CategoryInListViewModel

                    
                        public class GetCategoriesListQueryHandler :
                        IRequestHandler<GetCategoriesListQuery, List<CategoryInListViewModel>>
                        {
                            private readonly IAsyncRepository<Category> _categoryRepository;
                            private readonly IMapper _mapper;
                        
                            public GetCategoriesListQueryHandler(IMapper mapper,
                                IAsyncRepository<Category> categoryRepository)
                            {
                                _mapper = mapper;
                                _categoryRepository = categoryRepository;
                            }
                        
                        
                            public async Task<List<CategoryInListViewModel>> Handle
                                (GetCategoriesListQuery request, CancellationToken cancellationToken)
                            {
                                var all = await _categoryRepository.GetAllAsync();
                                var ordered = all.OrderBy(a => a.Name);
                        
                                return _mapper.Map<List<CategoryInListViewModel>>(ordered);
                            }
                        }
                        
                

Napiszmy Query dla pobierania kategori z wpisami?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

CategoryPostDto

                    
                        public class CategoryPostDto
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string ImageUrl { get; set; }
                            public int Rate { get; set; }

                            public int CategoryId { get; set; }
                        }
                    
                

CategoryPostListViewModel

                    
                        public class CategoryPostListViewModel
                        {
                            public int CategoryId { get; set; }
                        
                            public string Name { get; set; }
                        
                            public string DisplayName { get; set; }
                        
                            public ICollection<CategoryPostDto> Posts { get; set; }
                        }
                    
                

CategoryInListViewModel

                    
                        public class CategoryPostListViewModel
                        {
                            public int CategoryId { get; set; }
                        
                            public string Name { get; set; }
                        
                            public string DisplayName { get; set; }
                        
                            public ICollection<CategoryPostDto> Posts { get; set; }
                        }
                    
                

GetCategoriesWithPostListQuery

                    
                        public class GetCategoriesWithPostListQuery :
                        IRequest<List<CategoryPostListViewModel>>
                        {
                            public SearchCategoryOptions searchCategory { get; set; }
                        }
                    
                

SearchCategoryOptions

                    
                        public enum SearchCategoryOptions
                        {
                            All = 0,
                            FirstBestThisMonth = 2,
                            FirstBestAllTheTime = 3,
                        }
                    
                

CategoryInListViewModel

                    






                    






                        public class GetCategoriesWithPostListQueryHandler :
                        IRequestHandler<GetCategoriesWithPostListQuery, 
                        List<CategoryPostListViewModel>>
                        {
                            private readonly IAsyncRepository<Category> _categoryRepository;
                            private readonly IMapper _mapper;
                        


                            public GetCategoriesWithPostListQueryHandler(IMapper mapper,
                                IAsyncRepository<Category> categoryRepository)
                            {
                                _mapper = mapper;
                                _categoryRepository = categoryRepository;
                            }




                        
                            public Task<List<CategoryPostListViewModel>>
                                Handle(GetCategoriesWithPostListQuery request,
                                CancellationToken cancellationToken)
                            {
                                var list = await _categoryRepository.
                                    GetCategoriesWithPost(request.searchCategory);
                        
                                return _mapper.Map<List<CategoryPostListViewModel>>(list);
                            }
                        }
                    
                

Trzeba dodać taką metodę do repozytorium

CategoryInListViewModel

                    
                        public interface ICategoryRepository 
                            : IAsyncRepository<Category>
                        {
                            Task<List<Category>> 
                                GetCategoriesWithPost
                                (SearchCategoryOptions searchCategory);
                        }
                    
                

Kolejne mapowanie

CategoryInListViewModel

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                        
                                CreateMap<Category, CategoryInListViewModel>();
                                CreateMap<Category, CategoryPostDto>();
                                CreateMap<Category, CategoryPostListViewModel>();
                        
                            }
                        }
                    
                

Stwórzmy polecenie utworzenia wpisów
blog
Copyright © 2021 Onion Cezary Walenciuk

CreatedPostCommand

                    
                        public class CreatedPostCommand : IRequest<int>
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string Description { get; set; }
                        
                            public int CategoryId { get; set; }
                        
                            public string ImageUrl { get; set; }
                            public string Url { get; set; }
                        
                            public int Rate { get; set; }
                        }
                    
                

CreatedPostCommandHandler

                    










                        public class CreatedPostCommandHandler
                        : IRequestHandler<CreatedPostCommand, int>
                        {
                            private readonly IPostRepository _postRepository;
                            private readonly IMapper _mapper;
                        

                            public async Task<int> Handle(CreatedPostCommand request,
                                CancellationToken cancellationToken)
                            {
                                var post = _mapper.Map<Post>(request);
                        
                                post = await _postRepository.AddAsync(post);
                        
                                return post.PostId;
                            }
                        



                            
                            public CreatedPostCommandHandler(IPostRepository postRepository,
                                IMapper mapper)
                            {
                                _postRepository = postRepository;
                                _mapper = _mapper;
                            }
                        }
                    
                

MappingProfile

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                        
                                CreateMap<Category, CategoryInListViewModel>();
                                CreateMap<Category, CategoryPostDto>();
                                CreateMap<Category, CategoryPostListViewModel>();
                        
                                CreateMap<Post, CreatedPostCommand>().ReverseMap();
                            }
                        }
                    
                

blog
Copyright © 2021 Onion Cezary Walenciuk
Stwórzmy polecenie do aktualizacji i kasowania wpisów

UpdatePostCommand

                    
                        public class UpdatePostCommand : IRequest
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string Description { get; set; }
                    
                            public int CategoryId { get; set; }
                    
                            public string ImageUrl { get; set; }
                            public string Url { get; set; }
                    
                            public int Rate { get; set; }
                        }
                    
                

UpdatePostCommandHandler

                    
                        public class UpdatePostCommandHandler
                        : IRequestHandler<UpdatePostCommand>
                        {
                            private readonly IPostRepository _postRepository;
                            private readonly IMapper _mapper;
                        
                            public async Task<Unit> Handle(UpdatePostCommand request,
                                CancellationToken cancellationToken)
                            {
                                var post = _mapper.Map<Post>(request);
                        
                                await _postRepository.UpdateAsync(post);
                        
                                return Unit.Value;
                            }
                        
                            public UpdatePostCommandHandler(IPostRepository postRepository,
                                IMapper mapper)
                            {
                                _postRepository = postRepository;
                                _mapper = mapper;
                            }
                        }
                    
                

MappingProfile

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                        
                                CreateMap<Category, CategoryInListViewModel>();
                                CreateMap<Category, CategoryPostDto>();
                                CreateMap<Category, CategoryPostListViewModel>();
                        
                                CreateMap<Post, CreatedPostCommand>().ReverseMap();
                                CreateMap<Post, UpdatePostCommand>().ReverseMap();
                            }
                        }
                    
                

Teraz kasowanie

DeletePostCommand

                    
                        public class DeletePostCommand : IRequest
                        {
                            public int PostId { get; set; }
                        }
                    
                

DeletePostCommandHandler

                    
                        public class DeletePostCommandHandler : IRequestHandler<DeletePostCommand>
                        {
                            private readonly IPostRepository _postRepository;
                            private readonly IMapper _mapper;
                        
                            public async Task<Unit> Handle(DeletePostCommand request, 
                            CancellationToken cancellationToken)
                            {
                                var posttodelete = await _postRepository.GetByIdAsync(request.PostId);
                        
                                await _postRepository.DeleteAsync(posttodelete);
                        
                                return Unit.Value;
                            }
                        
                            public DeletePostCommandHandler(IPostRepository postRepository,
                        IMapper mapper)
                            {
                                _postRepository = postRepository;
                                _mapper = mapper;
                            }
                        }
                    
                

Sprawdzenie poprawności danych
🙃
blog
Copyright © 2021 Onion Cezary Walenciuk
Moglibyśmy logikę umieścić w klasacyh domenowych?

Czy to dobry pomysł?
blog
Copyright © 2021 Cezary Walenciuk

MappingProfile

                    
                        public class Post
                        {
                            public int PostId { get; set; }
                            
                            [Required]
                            [StringLenght(80)]
                            public string Title { get; set; }
                            
                            [StringLenght(40)]
                            public string Author { get; set; }

                            .....    

                            [Range(0, 100)]
                            public int Rate { get; set; }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
Potraktujmy jednak "Single Responsibility Principle" poważnie
W którym miejscu sprawdzać dane?
Mogę to zwalić na warstwę UI (Client albo Web API)
Mogę to zwalić na warstwę infrastruktury (EF Core)
blog
Copyright © 2021 Onion Cezary Walenciuk
Oczywiście, że walidacja danych powinna być w każdej warstwie
Na pomoc przychodzi paczka "Fluent Validation"
Dobry powód do dodania swoich wyjątków
??
blog
Copyright © 2021 Cezary Walenciuk

Projekt i nowe paczki

                    
                        <Project Sdk="Microsoft.NET.Sdk">

                        <PropertyGroup>
                          <TargetFramework>netstandard2.0</TargetFramework>
                        </PropertyGroup>
                      
                        <ItemGroup>
                          <ProjectReference Include="..\EduZbieracz.Domain\EduZbieracz.Domain.csproj" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <PackageReference Include="AutoMapper" Version="10.1.1" />
                          <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
                          <PackageReference Include="FluentValidation" Version="9.4.0" />
                          <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="9.4.0" />
                          <PackageReference Include="MediatR" Version="9.0.0" />
                          <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <Folder Include="Functions\Categories\Commands\CreateCategory\" />
                          <Folder Include="Functions\Webinars\Command\" />
                          <Folder Include="Functions\Webinars\Queries\GetWebinarList\" />
                        </ItemGroup>
                      
                        </Project>
                    
                

blog
Copyright © 2021 Cezary Walenciuk

FluentValidation do CreatedPostCommandValidator

                    
                        public class CreatedPostCommandValidator
                        : AbstractValidator<CreatedPostCommand>
                        {
                            public CreatedPostCommandValidator()
                            {
                                RuleFor(p => p.Title)
                                    .NotEmpty()
                                    .WithMessage("{PropertyName} is required")
                                    .NotNull()
                                    .MaximumLength(80)
                                    .WithMessage("{PropertyName} must not exceed 80 characters");
                        
                                RuleFor(p => p.Date)
                                    .NotEmpty()
                                    .WithMessage("{PropertyName} is required")
                                    .NotNull()
                                    .LessThan(DateTime.Now.AddDays(1));
                        
                                RuleFor(p => p.Rate)
                                    .InclusiveBetween(0, 100)
                                    .WithMessage("{PropertyName} is beetween 0 to 100");
                            }
                        }
                    
                

CreatedPostCommandHandler z FluentValidation

                    
                        public class CreatedPostCommandHandler
                        : IRequestHandler<CreatedPostCommand, int>
                        {
                        ......
                        
                            public async Task<int> Handle(CreatedPostCommand request,
                                CancellationToken cancellationToken)
                            {
                                var validator = new CreatedPostCommandValidator();
                                var validatorResult = await validator.ValidateAsync(request);
                        
                                //Co dalej......

                                var post = _mapper.Map<Post>(request);
                        
                                post = await _postRepository.AddAsync(post);
                        
                                return post.PostId;
                            }
                        .....
                        }
                    
                

ValidationException

                    
                        public class ValidationEduException : ApplicationException
                        {
                            public List<string> ErrorMessages { get; set; }
                        
                            public ValidationEduException(ValidationResult validationResult)
                            {
                                ErrorMessages = new List<String>();
                        
                                foreach (var item in validationResult.Errors)
                                {
                                    ErrorMessages.Add(item.ErrorMessage);
                                }
                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

ValidationException in action

                    
                        public async Task<int> Handle(CreatedPostCommand request,
                        CancellationToken cancellationToken)
                        {
                            var validator = new CreatedPostCommandValidator();
                            var validatorResult = await validator.ValidateAsync(request);
                        
                            if (!validatorResult.IsValid)
                                throw new ValidationEduException(validatorResult);
                        
                            var post = _mapper.Map<Post>(request);
                        
                            post = await _postRepository.AddAsync(post);
                        
                            return post.PostId;
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
Bardziej zaawansowane sprawdzenie poprawności danych
🧐

IsNameAndAuthorAlreadyExist

                    
                        public class CreatedPostCommandValidator
                        : AbstractValidator<CreatedPostCommand>
                        {
                            private readonly IPostRepository _postRepository;
                        
                            public CreatedPostCommandValidator(IPostRepository postRepository)
                            {
                                _postRepository = postRepository;
                                ......................
                                ......................
                                RuleFor(p => p).
                                    MustAsync(IsNameAndAuthorAlreadyExist)
                                    .WithMessage("Post with the same Title and Author already exist");
                            }
                        
                        
                            private async Task<bool> IsNameAndAuthorAlreadyExist
                                (CreatedPostCommand e, CancellationToken cancellationToken)
                            {
                                var check = await _postRepository.
                                    IsNameAndAuthorAlreadyExist(e.Title, e.Author);
                                return !check;
                            }
                        }
                    
                

IsNameAndAuthorAlreadyExist

                    
                        public interface IPostRepository 
                            : IAsyncRepository<Post>
                        {
                            Task<bool> 
                            IsNameAndAuthorAlreadyExist
                            (string title, string author);
                        }
                    
                

ValidationException

                    
                        public class CreatedPostCommandHandler
                        : IRequestHandler<CreatedPostCommand, int>
                        {
                            private readonly IPostRepository _postRepository;
                            private readonly IMapper _mapper;
                        
                            public async Task<int> Handle(CreatedPostCommand request,
                                CancellationToken cancellationToken)
                            {
                                var validator = new CreatedPostCommandValidator
                                (_postRepository);

                                //Co dalej ?
                                //Czy wyrzucanie wyjątków to dobry pomysł?
                    
                

A może pora na lepsze podjeście
🤨
Wyjątki są zbyt agresywne
Wyjątki są tylko dla frameworków
Dodajmy meta-informacje o odpowiedzi i naszych operacjach
Klasa BaseResponse
blog
Copyright © 2021 Cezary Walenciuk

BaseResponse

                    









                        public class BaseResponse
                        {
                            public bool Success { get; set; }
                            public string Message { get; set; }
                            public List<string> ValidationErrors { get; set; }
                        
                            public BaseResponse()
                            {
                                ValidationErrors = new List<string>();
                                Success = true;
                            }
                            public BaseResponse(string message = null)
                            {
                                ValidationErrors = new List<string>();
                                Success = true;
                                Message = message;
                            }
                        
                            public BaseResponse(string message, bool success)
                            {
                                ValidationErrors = new List<string>();
                                Success = success;
                                Message = message;
                            }
                        
                            public BaseResponse(ValidationResult validationResult)
                            {
                                ValidationErrors = new List<String>();
                                Success = validationResult.Errors.Count < 0;
                                foreach (var item in validationResult.Errors)
                                {
                                    ValidationErrors.Add(item.ErrorMessage);
                                }
                        
                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

CreatedPostCommandResponse

                    
                        public class CreatedPostCommandResponse : BaseResponse
                        {
                            public int? PostId { get; set; }
                        
                            public CreatedPostCommandResponse() : base()
                            { }
                        
                            public CreatedPostCommandResponse(ValidationResult validationResult)
                                : base(validationResult)
                            { }
                        
                            public CreatedPostCommandResponse(string message)
                            : base(message)
                            { }
                        
                            public CreatedPostCommandResponse(string message, bool success)
                                : base(message, success)
                            { }
                        
                            public CreatedPostCommandResponse(int postId)
                            {
                                PostId = postId;
                            }
                        }
                    
                

CreatedPostCommand

                    
                        public class CreatedPostCommand 
                        : IRequest<CreatedPostCommandResponse>
                        {
                            public int PostId { get; set; }
                            public string Title { get; set; }
                            public string Author { get; set; }
                            public DateTime Date { get; set; }
                            public string Description { get; set; }
                        
                            public int CategoryId { get; set; }
                        
                            public string ImageUrl { get; set; }
                            public string Url { get; set; }
                        
                            public int Rate { get; set; }
                        }
                    
                

CreatedPostCommandHandler

                    












                        public class CreatedPostCommandHandler
                        : IRequestHandler<CreatedPostCommand, CreatedPostCommandResponse>
                        {
                            private readonly IPostRepository _postRepository;
                            private readonly IMapper _mapper;
                        


                            public async Task<CreatedPostCommandResponse>
                                Handle(CreatedPostCommand request,
                                CancellationToken cancellationToken)
                            {
                                var validator = new CreatedPostCommandValidator(_postRepository);
                                var validatorResult = await validator.ValidateAsync(request);
                        
                                if (!validatorResult.IsValid)
                                    return new CreatedPostCommandResponse(validatorResult);
                        
                                var post = _mapper.Map<Post>(request);
                        
                                post = await _postRepository.AddAsync(post);
                        
                                return new CreatedPostCommandResponse(post.PostId);
                            }
                        


                            public CreatedPostCommandHandler(IPostRepository postRepository,
                                IMapper mapper)
                            {
                                _postRepository = postRepository;
                                _mapper = mapper;
                            }
                        }
                    
                

To jest ciekawy problem jak REST API który zwraca "Failed" z kodem HTTP 200
Ja jestem fanem tworzenia jeszcze typów wyliczeniowych które potrafią określić co dokładnie i gdzie się spier###

ResponseStatus

                    
                        public enum ResponseStatus
                        {
                            Success = 0,
                            NotFound = 1,
                            BadQueryRequest = 2,
                            ValidationError = 3,
                            Exception = 4,
                            DataBaseError = 5,
                            OtherClientApiError = 5,
                        }
                    
                

BaseResponse

                    
                        public class BaseResponse
                        {
                            public ResponseStatus Status { get; set; }
                            public bool Success { get; set; }
                            public string Message { get; set; }
                            public List<string> ValidationErrors { get; set; }
                    
                

Analogicznie zróbmy to dla Command dla kategorii
blog
Copyright © 2021 Cezary Walenciuk

CreatedCategoryCommand

                    
                        public class CreatedCategoryCommand 
                        : IRequest<CreatedCategoryCommandResponse>
                        {
                            public int CategoryId { get; set; }
                        
                            public string Name { get; set; }
                        
                            public string DisplayName { get; set; }
                        }
                    
                

CreatedCategoryCommandResponse

                    
                        public class CreatedCategoryCommandResponse : BaseResponse
                        {
                            public int? CategoryId { get; set; }
                        
                            public CreatedCategoryCommandResponse() : base()
                            { }
                        
                            public CreatedCategoryCommandResponse(ValidationResult validationResult)
                                : base(validationResult)
                            { }
                        
                            public CreatedCategoryCommandResponse(string message)
                            : base(message)
                            { }
                        
                            public CreatedCategoryCommandResponse(string message, bool success)
                                : base(message, success)
                            { }
                        
                            public CreatedCategoryCommandResponse(int categoryId)
                            {
                                CategoryId = categoryId;
                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

CreatedCategoryCommandValidator

                    
                        public class CreatedCategoryCommandValidator :
                        AbstractValidator<CreatedCategoryCommand>
                        {
                            public CreatedCategoryCommandValidator()
                            {
                                RuleFor(c => c.Name)
                                    .MinimumLength(2).MaximumLength(15)
                                    .WithMessage("{PropertName} Length is beewten 2 and 15");
                                RuleFor(c => c.DisplayName)
                                    .MinimumLength(2).MaximumLength(15)
                                    .WithMessage("{PropertName} Length is beewten 2 and 15");
                            }
                        }
                    
                

CreatedCategoryCommandHandler

                    

                        public class CreatedCategoryCommandHandler
                        : IRequestHandler<CreatedCategoryCommand, CreatedCategoryCommandResponse>
                        {
                            private readonly ICategoryRepository _categoryRepository;
                            private readonly IMapper _mapper;
                        
                            public async Task<CreatedCategoryCommandResponse> Handle(CreatedCategoryCommand request, CancellationToken cancellationToken)
                            {
                                var validator = new CreatedCategoryCommandValidator();
                                var validatorResult = await validator.ValidateAsync(request);
                        
                                if (!validatorResult.IsValid)
                                    return new CreatedCategoryCommandResponse(validatorResult);
                        
                                var category = _mapper.Map<Category>(request);
                        
                                category = await _categoryRepository.AddAsync(category);
                        
                                return new CreatedCategoryCommandResponse(category.CategoryId);
                            }
                        
                            public CreatedCategoryCommandHandler(ICategoryRepository categoryRepository,
                                IMapper mapper)
                            {
                                _categoryRepository = categoryRepository;
                                _mapper = mapper;
                            }
                        }
                    
                

MappingProfile

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                        
                                CreateMap<Category, CategoryInListViewModel>();
                                CreateMap<Category, CategoryPostDto>();
                                CreateMap<Category, CategoryPostListViewModel>();
                        
                                CreateMap<Post, CreatedPostCommand>().ReverseMap();
                                CreateMap<Post, UpdatePostCommand>().ReverseMap();
                        
                                CreateMap<Category, CreatedCategoryCommand>().ReverseMap();
                            }
                        }
                    
                

Pora na szybkie podsumowanie 🥳
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

GetWebinarsByDateQuery

                    
                        public class GetWebinarsByDateQuery 
                        : IRequest<PageWebinarByDateViewModel>
                        {
                            public DateTime? Date { get; set; }
                            public int Page { get; set; }
                            public int PageSize { get; set; }
                            public SearchOptionsWebinars Options { get; set; }
                        }
                    
                

SearchOptionsWebinars

                    
                        public enum SearchOptionsWebinars
                        {
                            Ignore = 0,
                            Month = 1,
                            Year = 2,
                            MonthAndYear = 3
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

WebinarsByDateViewModel

                    
                        public class WebinarsByDateViewModel
                        {
                            public int Id { get; set; }
                        
                            public string Title { get; set; }
                        
                            public string Description { get; set; }
                        
                            public string ImageUrl { get; set; }
                        
                            public string FacebookEventUrl { get; set; }
                        
                            public string SlidesUrl { get; set; }
                        
                            public string WatchFacebookLink { get; set; }
                        
                            public string WatchYoutubeLink { get; set; }
                        
                            public DateTime Date { get; set; }
                        }
                    
                

GetWebinarsByDateQueryHandler

                    












                        public class GetWebinarsByDateQueryHandler :
                        IRequestHandler<GetWebinarsByDateQuery, PageWebinarByDateViewModel>
                        {
                            private readonly IWebinaryRepository _webinarRepository;
                            private readonly IMapper _mapper;
                        


                            public GetWebinarsByDateQueryHandler(IWebinaryRepository webinarRepository,
                                IMapper mapper)
                            {
                                _webinarRepository = webinarRepository;
                                _mapper = mapper;
                            }
                        

                            
                            public async Task<PageWebinarByDateViewModel> Handle
                            (GetWebinarsByDateQuery request, CancellationToken cancellationToken)
                            {
                                var list = await _webinarRepository.GetPagedWebinarsForDate
                                (request.Options, request.Page, request.PageSize, request.Date);

                                var webinars = _mapper.Map<List<WebinarsByDateViewModel>>(list);
                        
                                var count = await _webinarRepository.GetTotalCountOfWebinarsForDate(request.Options, request.Date);
                                return new PageWebinarByDateViewModel()
                                {
                                    AllCount = count,
                                    Webinars = webinars,
                                    Page = request.Page,
                                    PageSize = request.PageSize
                                };
                            }
                        }
                    
                

IWebinaryRepository

                    
                        public interface IWebinaryRepository : IAsyncRepository<Webinar>
                        {
                            Task<int> GetTotalCountOfWebinarsForDate
                            (SearchOptionsWebinars options, DateTime? date);

                            Task<List<Webinar>> GetPagedWebinarsForDate
                            (SearchOptionsWebinars options, int page, int pageSize, 
                            DateTime? date);
                        }
                    
                

MappingProfile

                    
                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<Post, PostInListViewModel>()
                                    .ReverseMap();
                                CreateMap<Post, PostDetailViewModel>()
                                    .ReverseMap();
                                CreateMap<Category, CategoryDto>();
                        
                                CreateMap<Category, CategoryInListViewModel>();
                                CreateMap<Category, CategoryPostDto>();
                                CreateMap<Category, CategoryPostListViewModel>();
                        
                                CreateMap<Post, CreatedPostCommand>().ReverseMap();
                                CreateMap<Post, UpdatePostCommand>().ReverseMap();
                        
                                CreateMap<Category, CreatedCategoryCommand>().ReverseMap();
                        
                                CreateMap<Webinar, WebinarsByDateViewModel>().ReverseMap();
                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

CreatedWebinarCommand

                    
                        public class CreatedWebinarCommand :
                        IRequest<CreatedWebinarCommandResponse>
                        {
                            public int Id { get; set; }
                        
                            public string Title { get; set; }
                        
                            public string Description { get; set; }
                        
                            public string ImageUrl { get; set; }
                        
                            public string FacebookEventUrl { get; set; }
                        
                            public string SlidesUrl { get; set; }
                        
                            public string WatchFacebookLink { get; set; }
                        
                            public string WatchYoutubeLink { get; set; }
                        
                            public DateTime Date { get; set; }
                        
                            public bool AlreadyHappend { get; set; }
                        }
                    
                

CreatedWebinarCommandResponse

                    
                        public class CreatedWebinarCommandResponse : BaseResponse
                        {
                            public int? Id { get; set; }
                        
                            public CreatedWebinarCommandResponse() : base()
                            { }
                        
                            public CreatedWebinarCommandResponse(ValidationResult validationResult)
                                : base(validationResult)
                            { }
                        
                            public CreatedWebinarCommandResponse(string message)
                            : base(message)
                            { }
                        
                            public CreatedWebinarCommandResponse(string message, bool success)
                                : base(message, success)
                            { }
                        
                            public CreatedWebinarCommandResponse(int webinarId)
                            {
                                Id = webinarId;
                            }
                        }
                    
                

CreatedWebinarCommandValidator

                    
                        public class CreatedWebinarCommandValidator :
                        AbstractValidator<CreatedWebinarCommand>
                        {
                            public CreatedWebinarCommandValidator()
                            {
                                RuleFor(w => w.ImageUrl).NotEmpty().NotNull();
                                RuleFor(w => w.Title).NotEmpty().NotNull()
                                    .MinimumLength(5).MaximumLength(80);
                                RuleFor(w => w.FacebookEventUrl).NotEmpty().NotNull();
                        
                                RuleFor(w => w.Date).
                                    GreaterThan
                                    (DateTime.Now.AddYears(-1));
                            }
                        }
                    
                

CreatedWebinarCommandHandler

                    








                        public class CreatedWebinarCommandHandler
                        : IRequestHandler<CreatedWebinarCommand, CreatedWebinarCommandResponse>
                        {
                            public async Task<CreatedWebinarCommandResponse>
                            Handle(CreatedWebinarCommand request,
                            CancellationToken cancellationToken)
                            {
                                var validator = new CreatedWebinarCommandValidator();
                                var validatorResult = await validator.ValidateAsync(request);
                        
                                if (!validatorResult.IsValid)
                                    return new CreatedWebinarCommandResponse(validatorResult);
                        
                                var webinar = _mapper.Map<Webinar>(request);
                        
                                webinar = await _webinaryRepository.AddAsync(webinar);
                        
                                return new CreatedWebinarCommandResponse(webinar.Id);
                            }
                        


                            public CreatedWebinarCommandHandler(IWebinaryRepository webinaryRepository,
                                IMapper mapper)
                            {
                                _webinaryRepository = webinaryRepository;
                                _mapper = mapper;
                            }
                        


                            private readonly IWebinaryRepository _webinaryRepository;
                            private readonly IMapper _mapper;
                        }
                    
                

Oto ostatnia forma MappingProfile

MappingProfile

                    


                        

                        public class MappingProfile : Profile
                        {
                                public MappingProfile()
                                {
                                    CreateMap<Post, PostInListViewModel>()
                                        .ReverseMap();
                                    CreateMap<Post, PostDetailViewModel>()
                                        .ReverseMap();
                                    CreateMap<Category, CategoryDto>();
                                
                                    CreateMap<Category, CategoryInListViewModel>();
                                    CreateMap<Category, CategoryPostDto>();
                                    CreateMap<Category, CategoryPostListViewModel>();
                                    CreateMap<Post, CategoryPostDto>();
                                
                                    CreateMap<Post, CreatedPostCommand>().ReverseMap();
                                    CreateMap<Post, UpdatePostCommand>().ReverseMap();
                                
                                    CreateMap<Category, CreatedCategoryCommand>().ReverseMap();
                                
                                    CreateMap<Webinar, WebinarsByDateViewModel>().ReverseMap();
                                
                                    CreateMap<Webinar, CreatedWebinarCommand>().ReverseMap();
                                    CreateMap<Webinar, WebinarViewModel>().ReverseMap();
                                
                                    CreateMap<Webinar, UpdateWebinarCommand>().ReverseMap();
                                
                                }
                            
                        }
                    
                

Analogicznie dodałem tak mnóstwo klas w stylu CQRS
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Przechodzimy do
😏 Testów 😏
Czy stworzyliśmy na tym etapie testowalny kod?

Typy Testów

Integracyjne
Integration Test

Typy Testów

Integracyjne
Integration Test

Funkcjonalne
Functional Test

Typy Testów

Integracyjne
Integration Test

Funkcjonalne
Functional Test

Wydajnościowe
Performance Test

Typy Testów

Integracyjne
Integration Test

Funkcjonalne
Functional Test

Wydajnościowe
Performance Test

Jednostkowe
Unit Test

Testy jednostkowy jest to zautomatyzowany sposób wywołania kodu.

Sprawdź czy dane założenie jest spełnione

Testy jednostkowe

Uruchamianie
w izolacji

Testy jednostkowe

Uruchamianie
w izolacji

Stałe rezultaty

Testy jednostkowe

Uruchamianie
w izolacji

Stałe rezultaty

Zautomatyzowane

Testy jednostkowe

Uruchamianie
w izolacji

Stałe rezultaty

Zautomatyzowane

Szybkie

Potrzebujemy testów jednostkowych?

Aby znaleźć
błędy

Potrzebujemy testów jednostkowych?

Aby znaleźć
błędy

Dokonywać zmiań
bez strachu

Potrzebujemy testów jednostkowych?

Aby znaleźć
błędy

Dokonywać zmiań
bez strachu

Dokumentowanie
kodu

Potrzebujemy testów jednostkowych?

Aby znaleźć
błędy

Dokonywać zmiań
bez strachu

Dokumentowanie
kodu

Sprawdzanie kodu
w każdym momencie

Jak stworzyć test jednostkowy

Projekt xUnit

blog
Copyright © 2021 Onion Cezary Walenciuk

Jak stworzyć test jednostkowy

Projekt xUnit

Mock

Jak stworzyć test jednostkowy

Projekt xUnit

Mock

Wstrzykiwanie zależnosci

blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
Jak testy pomogły mi znaleźć błędy

😬
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
Przecież "<" czy ">" to bez róźnicy

😅
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
Trzeba napisać fałszywe obiekty dla Repozytoriów

👺

Mock ICategoryRepository

                    






























                        public static Mock<ICategoryRepository> GetCategoryRepository()
                        {
                            var categories = GetCategories();
                        
                            var mockCategoryRepository = new Mock<ICategoryRepository>();
                            mockCategoryRepository.
                            Setup(repo => repo.GetAllAsync()).ReturnsAsync(categories);
                        



                            mockCategoryRepository.
                            Setup(repo => repo.GetByIdAsync(It.IsAny<int>())).ReturnsAsync(
                            (int id) =>
                            {
                                var cat = categories.FirstOrDefault(c => c.CategoryId == id);
                                return cat;
                            });
                        




                            mockCategoryRepository.
                            Setup(repo => repo.AddAsync(It.IsAny<Category>())).ReturnsAsync(
                                (Category category) =>
                                {
                                    categories.Add(category);
                                    return category;
                                });

                        



                            mockCategoryRepository.
                            Setup(repo => repo.DeleteAsync(It.IsAny<Category>())).Callback
                                <Category>((entity) => categories.Remove(entity));
                        



                            mockCategoryRepository.
                            Setup(repo => repo.UpdateAsync(It.IsAny<Category>())).Callback
                                <Category>((entity) => { categories.Remove(entity); categories.Add(entity); });
                        



                            var categorieswithpost = GetCategoriesWithPosts();
                            mockCategoryRepository.Setup(repo => repo.GetCategoriesWithPost
                                (It.IsAny<SearchCategoryOptions>()))
                                .ReturnsAsync(categorieswithpost);
                        
                            return mockCategoryRepository;
                        }
                    
                

Mock IPostRepository

                    
                        








                        




















                        public static Mock<IPostRepository> GetPostRepository()
                        {
                            var posts = GetPosts();
                            var mockpostRepository = new Mock<IPostRepository>();
                        
                            mockpostRepository.Setup(repo => repo.GetAllAsync()).ReturnsAsync(posts);
                        



                            mockpostRepository.Setup(repo => repo.GetByIdAsync(It.IsAny<int>())).ReturnsAsync(
                            (int id) =>
                            {
                                var pos = posts.FirstOrDefault(c => c.PostId == id);
                                return pos;
                            });
                        



                            mockpostRepository.Setup(repo => repo.AddAsync(It.IsAny<Post>())).ReturnsAsync(
                            (Post post) =>
                            {
                                posts.Add(post);
                                return post;
                            });
                        



                            mockpostRepository.Setup(repo => repo.DeleteAsync(It.IsAny<Post>())).Callback
                                <Post>((entity) => posts.Remove(entity));
                        



                            mockpostRepository.Setup(repo => repo.UpdateAsync(It.IsAny<Post>())).Callback
                                <Post>((entity) => { posts.Remove(entity); posts.Add(entity); });
                        



                            mockpostRepository.Setup(repo => repo.IsNameAndAuthorAlreadyExist
                                (It.IsAny<string>(), It.IsAny<string>()))
                                .ReturnsAsync((string title, string author) =>
                                {
                                    var matches = posts.
                                    Any(a => a.Title.Equals(title) && a.Author.Equals(author));
                                    return matches;
                                });
                        

                            return mockpostRepository;
                        }
                    
                

Mock Webinary

                    
                                                








                        


































                        public static Mock<IWebinaryRepository> GetWebinarRepository()
                        {
                            var webinars = GetWebinars();
                            var mockWebinarRepository = new Mock<IWebinaryRepository>();
                        
                            mockWebinarRepository.Setup(repo => repo.GetAllAsync()).ReturnsAsync(webinars);
                        



                            mockWebinarRepository.Setup(repo => repo.GetByIdAsync(It.IsAny<int>())).ReturnsAsync(
                            (int id) =>
                            {
                                var pos = webinars.FirstOrDefault(c => c.Id == id);
                                return pos;
                            });
                        



                            mockWebinarRepository.Setup(repo => repo.AddAsync(It.IsAny<Webinar>())).ReturnsAsync(
                            (Webinar webinar) =>
                            {
                                webinars.Add(webinar);
                                return webinar;
                            });
                        


                            
                            mockWebinarRepository.Setup(repo => repo.DeleteAsync(It.IsAny<Webinar>())).Callback
                                <Webinar>((entity) => webinars.Remove(entity));
                        



                            mockWebinarRepository.Setup(repo => repo.UpdateAsync(It.IsAny<Webinar>())).Callback
                                <Webinar>((entity) => { webinars.Remove(entity); webinars.Add(entity); });
                        



                                mockWebinarRepository.Setup(repo => repo.GetPagedWebinarsForDate
                                (It.IsAny<SearchOptionsWebinars>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<DateTime?>()))
                                .ReturnsAsync((SearchOptionsWebinars s, int page, int pageSize, DateTime date) =>
                                {
                                    var matches = webinars.Where(x => x.Date.Month == date.Month && x.Date.Year == date.Year)
                                    .Skip((page - 1) * pageSize).Take(pageSize).ToList();
                    
                                    return matches;
                                });
                    

                                
                                mockWebinarRepository.Setup(repo => repo.GetTotalCountOfWebinarsForDate
                                (It.IsAny<SearchOptionsWebinars>(), It.IsAny<DateTime?>()))
                                .ReturnsAsync((SearchOptionsWebinars s, DateTime date) =>
                                {
                                    var matches = webinars.Count
                                    (x => x.Date.Month == date.Month && x.Date.Year == date.Year);
                    
                                    return matches;
                                });
                        
                            return mockWebinarRepository;
                        }
                    
                

blog
Copyright © 2021 Onion Cezary Walenciuk

CreatePostTest

                    
                        public class CreatePostTest
                        {
                            private readonly IMapper _mapper;
                            private readonly Mock<IPostRepository> _mockPostRepository;
                        
                            public CreatePostTest()
                            {
                                _mockPostRepository = RepositoryMocks.GetPostRepository();
                        
                                var configurationProvider = new MapperConfiguration(cfg =>
                                    {
                                        cfg.AddProfile<MappingProfile>();
                                    }
                                );
                        
                                _mapper = configurationProvider.CreateMapper();
                            }
                    
                

Handle_ValidPost_AddedToPostRepo()

                    
                        [Fact]
                        public async Task Handle_ValidPost_AddedToPostRepo()
                        {
                            var handler = new CreatedPostCommandHandler
                                (_mockPostRepository.Object, _mapper);
                        
                            var allPostsBeforeCount = (await _mockPostRepository.Object.GetAllAsync()).Count;
                        
                            var command = new CreatedPostCommand()
                            {
                                Title = "TestTest",
                                Date = DateTime.Now.AddDays(-14),
                                Rate = 9,
                                Author = "AAAA"
                            };
                        
                            var response = await handler.Handle(command, CancellationToken.None);
                        
                            var allPosts = await _mockPostRepository.Object.GetAllAsync();
                        
                            response.Success.ShouldBe(true);
                            response.ValidationErrors.Count.ShouldBe(0);
                            allPosts.Count.ShouldBe(allPostsBeforeCount + 1);
                            response.PostId.ShouldNotBeNull();
                        }
                    
                

Handle_Not_ValidPost_TooLongTitle_81Characters_NotAddedToPostRepo

                    
                        [Fact]
                        public async Task Handle_Not_ValidPost_TooLongTitle_81Characters_NotAddedToPostRepo()
                        {
                            var handler = new CreatedPostCommandHandler
                                (_mockPostRepository.Object, _mapper);
                        
                            var allPostsBeforeCount = (await _mockPostRepository.Object.GetAllAsync()).Count;
                        
                            var command = new CreatedPostCommand()
                            {
                                Title = new string('*', 81),
                                Date = DateTime.Now.AddDays(-14),
                                Rate = 9,
                                Author = "AAAA"
                            };
                        
                            var response = await handler.Handle(command, CancellationToken.None);
                        
                            var allPosts = await _mockPostRepository.Object.GetAllAsync();
                        
                            response.Success.ShouldBe(false);
                            response.ValidationErrors.Count.ShouldBe(1);
                            allPosts.Count.ShouldBe(allPostsBeforeCount);
                            response.PostId.ShouldBeNull();
                        }
                    
                

Handle_Not_ValidPost_FutureDate_2DayIntoTheFuture_NotAddedToPostRepo()

                    
                        [Fact]
                        public async Task Handle_Not_ValidPost_FutureDate_2DayIntoTheFuture_NotAddedToPostRepo()
                        {
                            var handler = new CreatedPostCommandHandler
                                (_mockPostRepository.Object, _mapper);
                        
                            var allPostsBeforeCount = (await _mockPostRepository.Object.GetAllAsync()).Count;
                        
                            var command = new CreatedPostCommand()
                            {
                                Title = new string('*', 80),
                                Date = DateTime.Now.AddDays(2),
                                Rate = 9,
                                Author = "AAAA"
                            };
                        
                            var response = await handler.Handle(command, CancellationToken.None);
                        
                            var allPosts = await _mockPostRepository.Object.GetAllAsync();
                        
                            response.Success.ShouldBe(false);
                            response.ValidationErrors.Count.ShouldBe(1);
                            allPosts.Count.ShouldBe(allPostsBeforeCount);
                            response.PostId.ShouldBeNull();
                        }
                    
                

Handle_Not_ValidPost_RateToBig_NotAddedToPostRepo()

                    
                        [Fact]
                        public async Task Handle_Not_ValidPost_RateToBig_NotAddedToPostRepo()
                        {
                            var handler = new CreatedPostCommandHandler
                                (_mockPostRepository.Object, _mapper);
                        
                            var allPostsBeforeCount = (await _mockPostRepository.Object.GetAllAsync()).Count;
                        
                            var command = new CreatedPostCommand()
                            {
                                Title = new string('*', 80),
                                Date = DateTime.Now.AddDays(-12),
                                Rate = 101,
                                Author = "AAAA"
                            };
                        
                            var response = await handler.Handle(command, CancellationToken.None);
                        
                            var allPosts = await _mockPostRepository.Object.GetAllAsync();
                        
                            response.Success.ShouldBe(false);
                            response.ValidationErrors.Count.ShouldBe(1);
                            allPosts.Count.ShouldBe(allPostsBeforeCount);
                            response.PostId.ShouldBeNull();
                        }
                    
                

CreateWebinarTest

                    
























                        public class CreateWebinarTest
                        {
                            private readonly IMapper _mapper;
                            private readonly Mock<IWebinaryRepository> _mockWebinarRepository;
                        


                            public CreateWebinarTest()
                            {
                                _mockWebinarRepository = RepositoryMocks.GetWebinarRepository();
                        
                                var configurationProvider = new MapperConfiguration(cfg =>
                                {
                                    cfg.AddProfile<MappingProfile>();
                                }
                                );
                        
                                _mapper = configurationProvider.CreateMapper();
                            }
                        



                            [Fact]
                            public async Task Handle_ValidWebinar_AddedToWebinarRepo()
                            {
                                var handler = new CreatedWebinarCommandHandler
                                    (_mockWebinarRepository.Object, _mapper);
                        
                                var allWebinarsBeforeCount = (await _mockWebinarRepository.Object.GetAllAsync()).Count;
                        
                                var command = new CreatedWebinarCommand()
                                {
                                    ImageUrl = "TestTest",
                                    Title = new string('*', 80),
                                    FacebookEventUrl = "TestTest",
                                    Date = DateTime.Now.AddDays(-14),
                                };
                        
                                var response = await handler.Handle(command, CancellationToken.None);
                        
                                var allWebinars = await _mockWebinarRepository.Object.GetAllAsync();
                        
                                response.Success.ShouldBe(true);
                                response.ValidationErrors.Count.ShouldBe(0);
                                allWebinars.Count.ShouldBe(allWebinarsBeforeCount + 1);
                                response.Id.ShouldNotBeNull();
                            }
                        }
                    
                

CreateCategoryTests

                    


















                        public class CreateCategoryTests
                        {
                            private readonly IMapper _mapper;
                            private readonly Mock<ICategoryRepository> _mockCategoryRepository;
                        



                            public CreateCategoryTests()
                            {
                                _mockCategoryRepository = RepositoryMocks.GetCategoryRepository();
                                var configurationProvider = new MapperConfiguration(cfg =>
                                {
                                    cfg.AddProfile<MappingProfile>();
                                });
                        
                                _mapper = configurationProvider.CreateMapper();
                            }



                        
                            [Fact]
                            public async Task Handle_ValidCategory_AddedToCategoriesRepo()
                            {
                                var handler = new CreatedCategoryCommandHandler
                                (_mockCategoryRepository.Object, _mapper);
                        
                                var allCategoriesBeforeCount = 
                                (await _mockCategoryRepository.Object.GetAllAsync()).Count;
                        
                                var response = await handler.Handle(new CreatedCategoryCommand()
                                { Name = "Test", DisplayName = "Test" }
                                , CancellationToken.None);
                        
                                var allCategories = await _mockCategoryRepository.Object.GetAllAsync();
                        
                                response.Success.ShouldBe(true);
                                response.ValidationErrors.Count.ShouldBe(0);
                                allCategories.Count.ShouldBe(allCategoriesBeforeCount + 1);
                                response.CategoryId.ShouldNotBeNull();
                        
                            }
                        }
                    
                

blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Onion Cezary Walenciuk
Przechodzimy do
🏗️ Infrastruktury 🏗️
Na razie wszystko było interfejsem
blog
Copyright © 2021 Onion Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

EduZbieracz.Persistence.EF

                    
                        <Project Sdk="Microsoft.NET.Sdk">

                        <PropertyGroup>
                          <TargetFramework>netstandard2.0</TargetFramework>
                          <AssemblyName>EduZbieracz.Persistence.EF</AssemblyName>
                          <RootNamespace>EduZbieracz.Persistence.EF</RootNamespace>
                        </PropertyGroup>
                      
                      
                        <ItemGroup>
                          <ProjectReference Include="..\EduZbieracz.Application\EduZbieracz.Application.csproj" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.11" />
                          <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
                        </ItemGroup>
                      
                      </Project>
                      
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

DummySeed

                    
                        public class DummySeed
                        {
                            public static int Csharp = 1;
                            public static int Aspnet = 2;
                            public static int TrikiZWindows = 3;
                            public static int Docker = 4;
                            public static int Filzofia = 5;
                        }
                    
                

DummyCategories

                    

























                        public class DummyCategories
                        {
                            public static List<Category> Get()
                            {
                                Category c1 = new Category()
                                {
                                    CategoryId = DummySeed.Csharp,
                                    Name = "CSharp",
                                    DisplayName = "C#"
                                };
                        
                                Category c2 = new Category()
                                {
                                    CategoryId = DummySeed.Aspnet,
                                    Name = "aspnet",
                                    DisplayName = "ASP.NET"
                                };
                        
                                Category c3 = new Category()
                                {
                                    CategoryId = DummySeed.TrikiZWindows,
                                    Name = "triki-z-windows",
                                    DisplayName = "Triki z Windows"
                                };
                        
                                Category c4 = new Category()
                                {
                                    CategoryId = DummySeed.Docker,
                                    Name = "docker",
                                    DisplayName = "Docker"
                                };
                        
                                Category c5 = new Category()
                                {
                                    CategoryId = DummySeed.Filzofia,
                                    Name = "filozofia",
                                    DisplayName = "Filozofia"
                                };
                        
                        
                                List<Category> p = new List<Category>();
                                p.Add(c1); p.Add(c3);
                                p.Add(c2); p.Add(c4);
                                p.Add(c5);
                        
                                return p;
                        
                            }
                        }
                    
                

DummyCategories

                    

















































































































                        public static class DummyPosts
                        {
                            public static List<Post> Get()
                            {
                                var cat = DummyCategories.Get();
                        
                                Post p1 = new Post()
                                {
                                    Author = "Damian",
                                    Date = DateTime.Now.AddMonths(-6),
                                    Description = @"Nasze aplikacje ASP.NET CORE coraz częściej są tylko aplikacją REST. To oczywiście wymaga Walidacji po stronie klienta i po stronie serwera
                        Jak taką walidację jak najszybciej zrobić.Może przecież sam napisać takie warunki,
                                    ale przy dużej ilości klas,
                                    które występują jako parametry mija się to z celem.
                        
                        Możesz też skorzystać z atrybutów i oznaczyć reguły do każdej właściwości.",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 1,
                                    Rate = 8,
                                    CategoryId = DummySeed.Aspnet,
                                    Title = "Walidacja z FluentValidation w ASP.NET Core + Swagger",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/walidacja-z-fluentvalidation-waspnet-core--swagger"
                                };
                        
                                Post p2 = new Post()
                                {
                                    Author = "Damian",
                                    Date = DateTime.Now.AddMonths(-6),
                                    Description = @"Programiści codziennie tworzą jakąś aplikację sieciową typu REST. Teraz nastaje pytanie, jak najlepiej zrozumieć jak dane API działa. Do tego mamy dokumentacje, ale jeśli pracujesz w szybkich, zamkniętych projektach to takiej dokumentacji może nie być.",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 2,
                        
                                    CategoryId = DummySeed.Aspnet,
                                    Rate = 7,
                                    Title = "Swagger UI : Dokumentowanie API w ASP.NET CORE",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/swagger-ui--dokumentowanie-api-w-aspnet-core"
                                };
                        
                                Post p3 = new Post()
                                {
                                    Author = "Stefan",
                                    Date = DateTime.Now.AddMonths(-12),
                                    Description = @"W pod koniec roku 2017 zacząłem ćwiczyć. Proste ćwiczenia rzeczywiście robią różnice, gdy masz siedzący tryb życia. A co z bieganiem ?
                        Pamiętam jak pierwszy raz na bieżni nie byłem w stanie wytrzymać 5 minut normalnego spaceru. Powoli z tygodnia na dzień zacząłem sobie stawiać wyższe progi i tak odkryłem, że o ile jest to na początku bolesne to jak twoje ciało da Ci te endorfiny to już...aż chce się biegać więcej. ",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 3,
                                    CategoryId = DummySeed.Filzofia,
                                    Rate = 5,
                                    Title = "Bieganie jak się do tego zmotywować : Zdrowie Programisty",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/bieganie-jak-sie-do-tego-zmotywowac--zdrowie-programisty"
                                };
                        
                                Post p4 = new Post()
                                {
                                    Author = "Damian",
                                    Date = DateTime.Now.AddMonths(-12),
                                    Description = @"Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 4,
                                    CategoryId = DummySeed.Aspnet,
                                    Rate = 5,
                                    Title = "NLog z ASP.NET Core : Logowanie błędów w aplikacji",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji"
                                };
                        
                                Post p5 = new Post()
                                {
                                    Author = "Damian",
                                    Date = DateTime.Now.AddMonths(-12),
                                    Description = @"Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 5,
                                    CategoryId = DummySeed.Aspnet,
                                    Rate = 5,
                                    Title = "NLog z ASP.NET Core : Logowanie błędów w aplikacji",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji"
                                };
                        
                                Post p6 = new Post()
                                {
                                    Author = "Damian",
                                    Date = DateTime.Now.AddMonths(-8),
                                    Description = @"W tym  artykule zobaczymy jak zintegrować AutoMapper  z ASP.NET CORE dla .NET 5, chociaż bądźmy szczerzy możesz skorzystać z tej biblioteki w każdym projekcie w C#.",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 6,
                                    CategoryId = DummySeed.Aspnet,
                                    Rate = 9,
                                    Title = "AutoMapper z ASP.NET CORE czyli mapowanie klas",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core"
                                };
                        
                                Post p7 = new Post()
                                {
                                    Author = "Adrian",
                                    Date = DateTime.Now.AddMonths(-14),
                                    Description = @"Nagrywanie Gif - ów ? Robienie obrazków na bloga ? Jak to robić jeszcze szybciej ? ",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 7,
                                    CategoryId = DummySeed.TrikiZWindows,
                                    Rate = 4,
                                    Title = "ShareX : Lepszy PrintScreen oraz robienie Gif-ów twojego pulpitu?",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow"
                                };
                        
                                Post p8 = new Post()
                                {
                                    Author = "Adrian",
                                    Date = DateTime.Now.AddMonths(-15),
                                    Description = @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 8,
                                    CategoryId = DummySeed.TrikiZWindows,
                                    Rate = 5,
                                    Title = "QuickLook, TeraCopy, ProcessExplorer czy to potrzebne jest ?",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow"
                                };
                        
                                Post p9 = new Post()
                                {
                                    Author = "Adrian",
                                    Date = DateTime.Now.AddMonths(-15),
                                    Description = @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.",
                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                    PostId = 9,
                                    CategoryId = DummySeed.Docker,
                                    Rate = 9,
                                    Title = "Docker File dla Go, ASP.NET Core, .NET 5, Java Spring, NodeJS, Python",
                                    Url = "https://cezarywalenciuk.pl/blog/programing/docker-file-dla-go-aspnet-core-net-5-java-spring-nodejs-python"
                                };
                        
                                List<Post> p = new List<Post>();
                                p.Add(p1); p.Add(p3);
                                p.Add(p2); p.Add(p4);
                                p.Add(p5); p.Add(p6);
                                p.Add(p8); p.Add(p7);
                                p.Add(p9);
                        
                                return p;
                            }
                        }
                    
                

DummyWebinars

                    

























\



























                        public class DummyWebinars
                        {
                            public static List<Webinar> Get()
                            {
                                List<Webinar> w = new List<Webinar>();
                        
                                var w1 = new Webinar()
                                {
                                    Title = "Aplikacja C# od Zera Architektura, CQRS, Dobre praktyki",
                                    AlreadyHappend = false,
                                    Date = DateTime.Now.AddDays(10),
                                    Description = @"Ustalenie architektury nie jest prostym zadaniem. Każda decyzja może mieć wielkie komplikacje potem.",
                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                    Id = 1,
                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg",
                                    SlidesUrl = "",
                                    WatchFacebookLink = "",
                                    WatchYoutubeLink = "",
                                };
                                w.Add(w1);
                        
                                var w2 = new Webinar()
                                {
                                    Title = "Kubernetes i Docker : Wytłumacz mi i pokaż",
                                    AlreadyHappend = false,
                                    Date = DateTime.Now.AddDays(-40),
                                    Description = @"Kontenery są tutaj. Kubernetes jest de facto platformą do ich uruchamiania i zarządzania.",
                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                    Id = 2,
                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg",
                                    SlidesUrl = "https://panniebieski.github.io/webinar-Kubernetes-Docker-Wytlumacz-mi-i-pokaz/",
                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2775230679405348&ref=watch_permalink",
                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=7g00wOg9Jto",
                                };
                                w.Add(w2);
                        
                        
                                var w3 = new Webinar()
                                {
                                    Title = "C# 9, Rekordy i duże zmiany w .NET 5",
                                    AlreadyHappend = false,
                                    Date = DateTime.Now.AddDays(-60),
                                    Description = @"Jak utworzyć projekt w .NET 5?",
                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                    Id = 3,
                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg",
                                };
                                w.Add(w3);
                        
                                var w4 = new Webinar()
                                {
                                    Title = "Szybki Trening Sql Server 2",
                                    AlreadyHappend = false,
                                    Date = DateTime.Now.AddDays(-70),
                                    Description = @"Czasami jedyne czego potrzebujemy to dobrego przykładu.",
                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                    Id = 4,
                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg",
                                };
                                w.Add(w4);
                        
                                var w5 = new Webinar()
                                {
                                    Title = "Pytania rekrutacyjne czyli dalsza kariera",
                                    AlreadyHappend = false,
                                    Date = DateTime.Now.AddDays(-90),
                                    Description = @"Jak wygląda szukanie pracy jako programista w 2020 roku? Czy jest lepiej, czy jest gorzej?",
                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                    Id = 5,
                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg",
                                };
                                w.Add(w5);
                        
                                return w;
                            }
                        }
                    
                

EduZbieraczContext

EduZbieraczContext

                    


                        public class EduZbieraczContext : DbContext
                        {
                            public EduZbieraczContext(DbContextOptions<EduZbieraczContext> options)
                                : base(options)
                            {
                            }
                        
                            public DbSet<Post> Posts { get; set; }
                            public DbSet<Category> Categories { get; set; }
                            public DbSet<Webinar> Webinars { get; set; }
                        
                            public override Task<int> SaveChangesAsync
                            (CancellationToken cancellationToken = new CancellationToken())
                            {
                                foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
                                {
                                    switch (entry.State)
                                    {
                                        case EntityState.Added:
                                            entry.Entity.CreatedDate = DateTime.Now;
                                            break;
                                        case EntityState.Modified:
                                            entry.Entity.LastModifiedDate = DateTime.Now;
                                            break;
                                    }
                                }
                                return base.SaveChangesAsync(cancellationToken);
                            }
                        
                    
                

OnModelCreating w EduZbieraczContext

                    
                        protected override void OnModelCreating(ModelBuilder modelBuilder)
                        {
                            modelBuilder.
                                ApplyConfigurationsFromAssembly
                                (typeof(EduZbieraczContext).Assembly);
                        
                        
                            foreach (var item in DummyCategories.Get())
                            {
                                modelBuilder.Entity<Category>().HasData(item);
                            }
                        
                            foreach (var item in DummyPosts.Get())
                            {
                                modelBuilder.Entity<Post>(b =>
                                {
                                    b.HasData(item);
                                });
                        
                            }
                        
                            foreach (var item in DummyWebinars.Get())
                            {
                                modelBuilder.Entity<Webinar>().HasData(item);
                            }
                        }
                    
                

Gdzie dodać walidacje po stronie EF?
blog
Copyright © 2021 Cezary Walenciuk

PostConfiguration

                    
                        public class PostConfiguration 
                            : IEntityTypeConfiguration<Post>
                        {
                            public void Configure(EntityTypeBuilder<Post> builder)
                            {
                                builder.Property(e => e.Title)
                                    .IsRequired()
                                    .HasMaxLength(80);
                            }
                        }
                    
                

OnModelCreating PostConfiguration

                    
                        protected override void OnModelCreating(ModelBuilder modelBuilder)
                        {
                            modelBuilder.
                                ApplyConfigurationsFromAssembly
                                (typeof(EduZbieraczContext).Assembly);
                    
                

Pora stworzyć repozytoria

BaseRepository

                    




















                        public class BaseRepository<T> : IAsyncRepository<T> where T : class
                        {
                            protected readonly EduZbieraczContext _dbContext;
                        
                            public BaseRepository(EduZbieraczContext dbContext)
                            {
                                _dbContext = dbContext;
                            }
                        
                            public async Task<T> AddAsync(T entity)
                            {
                                await _dbContext.Set<T>().AddAsync(entity);
                                await _dbContext.SaveChangesAsync();
                        
                                return entity;
                            }
                        
                            public async Task DeleteAsync(T entity)
                            {
                                _dbContext.Set<T>().Remove(entity);
                                await _dbContext.SaveChangesAsync();
                            }
                        
                            public async Task<IReadOnlyList<T>> GetAllAsync()
                            {
                                return await _dbContext.Set<T>().ToListAsync();
                        
                            }
                        
                            public async Task<T> GetByIdAsync(int id)
                            {
                                return await _dbContext.Set<T>().FindAsync(id);
                            }
                        
                            public async Task UpdateAsync(T entity)
                            {
                                _dbContext.Entry(entity).State = EntityState.Modified;
                                await _dbContext.SaveChangesAsync();
                            }
                        }
                    
                

PostRepository

                    
                        public class PostRepository : BaseRepository<Post>, IPostRepository
                        {
                            public PostRepository(EduZbieraczContext dbContext) : base(dbContext)
                            { }
                        
                            public Task<bool> IsNameAndAuthorAlreadyExist(string title, string author)
                            {
                                var matches = _dbContext.Posts.
                                    Any(a => a.Title.Equals(title) && a.Author.Equals(author));
                        
                                return Task.FromResult(matches);
                            }
                        }
                    
                

CategoryRepository

                    



























                        public class CategoryRepository : BaseRepository<Category>, ICategoryRepository
                        {
                            public CategoryRepository(EduZbieraczContext dbContext) : base(dbContext)
                            { }
                        
                            public async Task<List<Category>> GetCategoriesWithPost
                            (SearchCategoryOptions searchCategory)
                            {
                                var allCategories = await _dbContext.Categories.Include(p => p.Posts).ToListAsync();
                        
                                if (searchCategory == SearchCategoryOptions.FirstBestAllTheTime)
                                {
                                    return GetOneMaxPost(allCategories);
                                }
                                else if (searchCategory == SearchCategoryOptions.FirstBestThisMonth)
                                {
                                    DateTime d = DateTime.Now;
                        
                                    allCategories = allCategories.Where(c =>
                                    c.Posts.Any(p => (p.Date.Month == d.Month && d.Year == p.Date.Year)))
                                        .ToList();
                        
                                    return GetOneMaxPost(allCategories);
                                }
                        
                                return allCategories;
                            }
                        
                            private List<Category> GetOneMaxPost(List<Category> allCategories)
                            {
                                foreach (var c in allCategories)
                                {
                                    Post max = null;
                                    foreach (var p in c.Posts)
                                    {
                                        if (max == null)
                                        {
                                            max = p;
                                            break;
                                        }
                        
                                        if (max.Rate < p.Rate)
                                            max = p;
                        
                                    }
                                    c.Posts = new List<Post>();
                                    if (max != null)
                                        c.Posts.Add(max);
                                }
                        
                                return allCategories;
                            }
                        }
                    
                

WebinaryRepository

                    



































                        public class WebinaryRepository : BaseRepository<Webinar>, IWebinaryRepository
                        {
                            public WebinaryRepository(EduZbieraczContext dbContext) : base(dbContext)
                            { }
                        
                            public async Task<List<Webinar>> GetPagedWebinarsForDate(SearchOptionsWebinars options,
                             int page, int pageSize, DateTime? date)
                            {


                                if (options == SearchOptionsWebinars.MonthAndYear && date.HasValue)
                                {
                                    return await _dbContext.Webinars.Where(x => x.Date.Month == date.Value.Month 
                                    && x.Date.Year == date.Value.Year)
                                        .Skip((page - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
                                }
                                if (options == SearchOptionsWebinars.Year && date.HasValue)
                                {
                                    return await _dbContext.Webinars.Where(x => x.Date.Year == date.Value.Year)
                                        .Skip((page - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
                                }
                                if (options == SearchOptionsWebinars.Month && date.HasValue)
                                {
                                    return await _dbContext.Webinars.Where(x => x.Date.Month == date.Value.Month)
                                        .Skip((page - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
                                }
                        
                        
                                return await _dbContext.Webinars
                                    .Skip((page - 1) * pageSize).Take(pageSize).AsNoTracking().ToListAsync();
                        
                        
                            }



                            public async Task<int> GetTotalCountOfWebinarsForDate(SearchOptionsWebinars options,
                             DateTime? date)
                            {
                                if (options == SearchOptionsWebinars.MonthAndYear && date.HasValue)
                                {
                                    return await _dbContext.Webinars.CountAsync
                                                    (x => x.Date.Month == date.Value.Month 
                                                    && x.Date.Year == date.Value.Year);
                                }
                                if (options == SearchOptionsWebinars.Year && date.HasValue)
                                {
                                    return await _dbContext.Webinars.CountAsync
                                       (x => x.Date.Year == date.Value.Year);
                                }
                                if (options == SearchOptionsWebinars.Month && date.HasValue)
                                {
                                    return await _dbContext.Webinars.CountAsync
                                      (x => x.Date.Year == date.Value.Year);
                                }

                                return await _dbContext.Webinars.CountAsync();

                            }
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Pora napisać instalator dla tej warstwy
blog
Copyright © 2021 Cezary Walenciuk

PersistenceWithEFRegistration

                    
                        public static class PersistenceWithEFRegistration
                        {
                            public static IServiceCollection AddPersistenceServices(this IServiceCollection services,
                             IConfiguration configuration)
                            {
                                services.AddDbContext<EduZbieraczContext>(options =>
                                    options.UseSqlServer(configuration.
                                    GetConnectionString("EduZbieraczConnectionString")));
                        
                                services.AddScoped(typeof(IAsyncRepository<>), typeof(BaseRepository<>));
                        
                                services.AddScoped<ICategoryRepository, CategoryRepository>();
                                services.AddScoped<IWebinaryRepository, WebinaryRepository>();
                                services.AddScoped<IPostRepository, IPostRepository>();
                        
                                return services;
                            }
                        }
                    
                

Przechodzimy do
🗣️ API 🗣️
To ta część w której w końcu tworzenia aplikacje
🐱‍👤 ASP.NET CORE 🐱‍👤
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Zajrzmi do serca ASP.NET CORE
czyli klasy Startup 🔍
blog
Copyright © 2021 Cezary Walenciuk

GetPostsListQueryHandler

                    
                        public class Startup
                        {
                            public IConfiguration Configuration { get; }
                        
                            public Startup(IConfiguration configuration)
                            {
                                Configuration = configuration;
                            }
                        
                            public void ConfigureServices(IServiceCollection services)
                            {
                                services.AddEduZbieraczApplication();
                                services.AddEduZbieraczPersistenceEFServices(Configuration);
                            }
                    
                

GetPostsListQueryHandler

                    
                        public void ConfigureServices(IServiceCollection services)
                        {
                            services.AddEduZbieraczApplication();
                            services.AddEduZbieraczPersistenceEFServices(Configuration);
                        
                            services.AddControllers();
                        
                            services.AddCors(options =>
                            {
                                options.AddPolicy("Open",
                                    builder => builder.AllowAnyOrigin()
                                    .AllowAnyHeader().AllowAnyMethod());
                            });
                        }
                    
                

GetPostsListQueryHandler

                    
                        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
                        {
                            if (env.IsDevelopment())
                            {
                                app.UseDeveloperExceptionPage();
                            }
                
                            app.UseHttpsRedirection();
                            app.UseRouting();
                            app.UseCors("Open");
                
                            app.UseEndpoints(endpoints =>
                            {
                                endpoints.MapControllers();
                            });
                        }
                    
                

Zajrzmi do konfiguracji
Gdzie mają być ConnectionStringi 🔍
blog
Copyright © 2021 Cezary Walenciuk

GetPostsListQueryHandler

                    
                        {
                            "ConnectionStrings": {
                              "EduZbieraczConnectionString": "Data Source=CEZMSI\SQLEXPRESS;
                              Initial Catalog=EduZbieracz;Integrated Security=True;Pooling=False"
                            }
                        }
                    
                

Pora utworzyć baze danych
Przy pomocy EntityFrrameworkCore.Tools
😎
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Init Migration

                    



















































































































                        public partial class Init : Migration
                        {
                            protected override void Up(MigrationBuilder migrationBuilder)
                            {
                                migrationBuilder.CreateTable(
                                    name: "Categories",
                                    columns: table => new
                                    {
                                        CategoryId = table.Column<int>(nullable: false)
                                            .Annotation("SqlServer:Identity", "1, 1"),
                                        CreatedBy = table.Column<string>(nullable: true),
                                        CreatedDate = table.Column<DateTime>(nullable: false),
                                        LastModifiedBy = table.Column<string>(nullable: true),
                                        LastModifiedDate = table.Column<DateTime>(nullable: true),
                                        Name = table.Column<string>(nullable: true),
                                        DisplayName = table.Column<string>(nullable: true)
                                    },
                                    constraints: table =>
                                    {
                                        table.PrimaryKey("PK_Categories", x => x.CategoryId);
                                    });
                        
                                migrationBuilder.CreateTable(
                                    name: "Webinars",
                                    columns: table => new
                                    {
                                        Id = table.Column<int>(nullable: false)
                                            .Annotation("SqlServer:Identity", "1, 1"),
                                        Title = table.Column<string>(nullable: true),
                                        Description = table.Column<string>(nullable: true),
                                        ImageUrl = table.Column<string>(nullable: true),
                                        FacebookEventUrl = table.Column<string>(nullable: true),
                                        SlidesUrl = table.Column<string>(nullable: true),
                                        WatchFacebookLink = table.Column<string>(nullable: true),
                                        WatchYoutubeLink = table.Column<string>(nullable: true),
                                        Date = table.Column<DateTime>(nullable: false),
                                        AlreadyHappend = table.Column<bool>(nullable: false)
                                    },
                                    constraints: table =>
                                    {
                                        table.PrimaryKey("PK_Webinars", x => x.Id);
                                    });
                        
                                migrationBuilder.CreateTable(
                                    name: "Posts",
                                    columns: table => new
                                    {
                                        PostId = table.Column<int>(nullable: false)
                                            .Annotation("SqlServer:Identity", "1, 1"),
                                        Title = table.Column<string>(maxLength: 80, nullable: false),
                                        Author = table.Column<string>(nullable: true),
                                        Date = table.Column<DateTime>(nullable: false),
                                        Description = table.Column<string>(nullable: true),
                                        CategoryId = table.Column<int>(nullable: false),
                                        ImageUrl = table.Column<string>(nullable: true),
                                        Url = table.Column<string>(nullable: true),
                                        Rate = table.Column<int>(nullable: false)
                                    },
                                    constraints: table =>
                                    {
                                        table.PrimaryKey("PK_Posts", x => x.PostId);
                                        table.ForeignKey(
                                            name: "FK_Posts_Categories_CategoryId",
                                            column: x => x.CategoryId,
                                            principalTable: "Categories",
                                            principalColumn: "CategoryId",
                                            onDelete: ReferentialAction.Cascade);
                                    });
                        
                                migrationBuilder.InsertData(
                                    table: "Categories",
                                    columns: new[] { "CategoryId", "CreatedBy", "CreatedDate", "DisplayName", "LastModifiedBy", "LastModifiedDate", "Name" },
                                    values: new object[,]
                                    {
                                        { 1, null, new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), "C#", null, null, "CSharp" },
                                        { 3, null, new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), "Triki z Windows", null, null, "triki-z-windows" },
                                        { 2, null, new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), "ASP.NET", null, null, "aspnet" },
                                        { 4, null, new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), "Docker", null, null, "docker" },
                                        { 5, null, new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), "Filozofia", null, null, "filozofia" }
                                    });
                        
                                migrationBuilder.InsertData(
                                    table: "Webinars",
                                    columns: new[] { "Id", "AlreadyHappend", "Date", "Description", "FacebookEventUrl", "ImageUrl", "SlidesUrl", "Title", "WatchFacebookLink", "WatchYoutubeLink" },
                                    values: new object[,]
                                    {
                                        { 1, false, new DateTime(2021, 1, 28, 10, 3, 58, 620, DateTimeKind.Local).AddTicks(5513), "Ustalenie architektury nie jest prostym zadaniem. Każda decyzja może mieć wielkie komplikacje potem.", "https://www.facebook.com/events/407358067213893/", "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg", "", "Aplikacja C# od Zera Architektura, CQRS, Dobre praktyki", "", "" },
                                        { 2, false, new DateTime(2020, 12, 9, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7359), "Kontenery są tutaj. Kubernetes jest de facto platformą do ich uruchamiania i zarządzania.", "https://www.facebook.com/events/407358067213893/", "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg", "https://panniebieski.github.io/webinar-Kubernetes-Docker-Wytlumacz-mi-i-pokaz/", "Kubernetes i Docker : Wytłumacz mi i pokaż", "https://www.facebook.com/watch/live/?v=2775230679405348&ref=watch_permalink", "https://www.youtube.com/watch?v=7g00wOg9Jto" },
                                        { 3, false, new DateTime(2020, 11, 19, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7537), "Jak utworzyć projekt w .NET 5?", "https://www.facebook.com/events/407358067213893/", "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg", "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/", "C# 9, Rekordy i duże zmiany w .NET 5", "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink", "https://www.youtube.com/watch?v=ATbLEyd_1Kg" },
                                        { 4, false, new DateTime(2020, 11, 9, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7547), "Czasami jedyne czego potrzebujemy to dobrego przykładu.", "https://www.facebook.com/events/407358067213893/", "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg", "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/", "Szybki Trening Sql Server 2", "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink", "https://www.youtube.com/watch?v=ATbLEyd_1Kg" },
                                        { 5, false, new DateTime(2020, 10, 20, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7554), "Jak wygląda szukanie pracy jako programista w 2020 roku? Czy jest lepiej, czy jest gorzej?", "https://www.facebook.com/events/407358067213893/", "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg", "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/", "Pytania rekrutacyjne czyli dalsza kariera", "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink", "https://www.youtube.com/watch?v=ATbLEyd_1Kg" }
                                    });
                        
                                migrationBuilder.InsertData(
                                    table: "Posts",
                                    columns: new[] { "PostId", "Author", "CategoryId", "Date", "Description", "ImageUrl", "Rate", "Title", "Url" },
                                    values: new object[,]
                                    {
                                        { 8, "Adrian", 3, new DateTime(2019, 10, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7507), @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                                    Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                                    Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 5, "QuickLook, TeraCopy, ProcessExplorer czy to potrzebne jest ?", "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow" },
                                        { 7, "Adrian", 3, new DateTime(2019, 11, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7503), "Nagrywanie Gif - ów ? Robienie obrazków na bloga ? Jak to robić jeszcze szybciej ? ", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 4, "ShareX : Lepszy PrintScreen oraz robienie Gif-ów twojego pulpitu?", "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow" },
                                        { 1, "Damian", 2, new DateTime(2020, 7, 18, 10, 3, 58, 614, DateTimeKind.Local).AddTicks(6597), @"Nasze aplikacje ASP.NET CORE coraz częściej są tylko aplikacją REST. To oczywiście wymaga Walidacji po stronie klienta i po stronie serwera
                                    Jak taką walidację jak najszybciej zrobić.Może przecież sam napisać takie warunki,
                                                    ale przy dużej ilości klas,
                                                    które występują jako parametry mija się to z celem.
                        
                                    Możesz też skorzystać z atrybutów i oznaczyć reguły do każdej właściwości.", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 8, "Walidacja z FluentValidation w ASP.NET Core + Swagger", "https://cezarywalenciuk.pl/blog/programing/walidacja-z-fluentvalidation-waspnet-core--swagger" },
                                        { 2, "Damian", 2, new DateTime(2020, 7, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7347), "Programiści codziennie tworzą jakąś aplikację sieciową typu REST. Teraz nastaje pytanie, jak najlepiej zrozumieć jak dane API działa. Do tego mamy dokumentacje, ale jeśli pracujesz w szybkich, zamkniętych projektach to takiej dokumentacji może nie być.", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 7, "Swagger UI : Dokumentowanie API w ASP.NET CORE", "https://cezarywalenciuk.pl/blog/programing/swagger-ui--dokumentowanie-api-w-aspnet-core" },
                                        { 4, "Damian", 2, new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7491), "Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 5, "NLog z ASP.NET Core : Logowanie błędów w aplikacji", "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji" },
                                        { 5, "Damian", 2, new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7495), "Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 5, "NLog z ASP.NET Core : Logowanie błędów w aplikacji", "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji" },
                                        { 6, "Damian", 2, new DateTime(2020, 5, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7499), "W tym  artykule zobaczymy jak zintegrować AutoMapper  z ASP.NET CORE dla .NET 5, chociaż bądźmy szczerzy możesz skorzystać z tej biblioteki w każdym projekcie w C#.", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 9, "AutoMapper z ASP.NET CORE czyli mapowanie klas", "https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core" },
                                        { 9, "Adrian", 4, new DateTime(2019, 10, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7512), @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                                    Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                                    Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 9, "Docker File dla Go, ASP.NET Core, .NET 5, Java Spring, NodeJS, Python", "https://cezarywalenciuk.pl/blog/programing/docker-file-dla-go-aspnet-core-net-5-java-spring-nodejs-python" },
                                        { 3, "Stefan", 5, new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7480), @"W pod koniec roku 2017 zacząłem ćwiczyć. Proste ćwiczenia rzeczywiście robią różnice, gdy masz siedzący tryb życia. A co z bieganiem ?
                                    Pamiętam jak pierwszy raz na bieżni nie byłem w stanie wytrzymać 5 minut normalnego spaceru. Powoli z tygodnia na dzień zacząłem sobie stawiać wyższe progi i tak odkryłem, że o ile jest to na początku bolesne to jak twoje ciało da Ci te endorfiny to już...aż chce się biegać więcej. ", "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png", 5, "Bieganie jak się do tego zmotywować : Zdrowie Programisty", "https://cezarywalenciuk.pl/blog/programing/bieganie-jak-sie-do-tego-zmotywowac--zdrowie-programisty" }
                                    });
                        
                                migrationBuilder.CreateIndex(
                                    name: "IX_Posts_CategoryId",
                                    table: "Posts",
                                    column: "CategoryId");
                            }
                        
                            protected override void Down(MigrationBuilder migrationBuilder)
                            {
                                migrationBuilder.DropTable(
                                    name: "Posts");
                        
                                migrationBuilder.DropTable(
                                    name: "Webinars");
                        
                                migrationBuilder.DropTable(
                                    name: "Categories");
                            }
                        }
                    
                

EduZbieraczContextModelSnapshot : ModelSnapshot {

                    














































































































































































































































































































































                        // <auto-generated />
                        using System;
                        using EduZbieracz.Persistence;
                        using Microsoft.EntityFrameworkCore;
                        using Microsoft.EntityFrameworkCore.Infrastructure;
                        using Microsoft.EntityFrameworkCore.Metadata;
                        using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
                        
                        namespace EduZbieracz.Persistence.EF.Migrations
                        {
                            [DbContext(typeof(EduZbieraczContext))]
                            partial class EduZbieraczContextModelSnapshot : ModelSnapshot
                            {
                                protected override void BuildModel(ModelBuilder modelBuilder)
                                {
                        #pragma warning disable 612, 618
                                    modelBuilder
                                        .HasAnnotation("ProductVersion", "3.1.11")
                                        .HasAnnotation("Relational:MaxIdentifierLength", 128)
                                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
                        
                                    modelBuilder.Entity("EduZbieracz.Domain.Entities.Category", b =>
                                        {
                                            b.Property<int>("CategoryId")
                                                .ValueGeneratedOnAdd()
                                                .HasColumnType("int")
                                                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
                        
                                            b.Property<string>("CreatedBy")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<DateTime>("CreatedDate")
                                                .HasColumnType("datetime2");
                        
                                            b.Property<string>("DisplayName")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("LastModifiedBy")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<DateTime?>("LastModifiedDate")
                                                .HasColumnType("datetime2");
                        
                                            b.Property<string>("Name")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.HasKey("CategoryId");
                        
                                            b.ToTable("Categories");
                        
                                            b.HasData(
                                                new
                                                {
                                                    CategoryId = 1,
                                                    CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
                                                    DisplayName = "C#",
                                                    Name = "CSharp"
                                                },
                                                new
                                                {
                                                    CategoryId = 3,
                                                    CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
                                                    DisplayName = "Triki z Windows",
                                                    Name = "triki-z-windows"
                                                },
                                                new
                                                {
                                                    CategoryId = 2,
                                                    CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
                                                    DisplayName = "ASP.NET",
                                                    Name = "aspnet"
                                                },
                                                new
                                                {
                                                    CategoryId = 4,
                                                    CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
                                                    DisplayName = "Docker",
                                                    Name = "docker"
                                                },
                                                new
                                                {
                                                    CategoryId = 5,
                                                    CreatedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
                                                    DisplayName = "Filozofia",
                                                    Name = "filozofia"
                                                });
                                        });
                        
                                    modelBuilder.Entity("EduZbieracz.Domain.Entities.Post", b =>
                                        {
                                            b.Property<int>("PostId")
                                                .ValueGeneratedOnAdd()
                                                .HasColumnType("int")
                                                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
                        
                                            b.Property<string>("Author")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<int>("CategoryId")
                                                .HasColumnType("int");
                        
                                            b.Property<DateTime>("Date")
                                                .HasColumnType("datetime2");
                        
                                            b.Property<string>("Description")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("ImageUrl")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<int>("Rate")
                                                .HasColumnType("int");
                        
                                            b.Property<string>("Title")
                                                .IsRequired()
                                                .HasColumnType("nvarchar(80)")
                                                .HasMaxLength(80);
                        
                                            b.Property<string>("Url")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.HasKey("PostId");
                        
                                            b.HasIndex("CategoryId");
                        
                                            b.ToTable("Posts");
                        
                                            b.HasData(
                                                new
                                                {
                                                    PostId = 1,
                                                    Author = "Damian",
                                                    CategoryId = 2,
                                                    Date = new DateTime(2020, 7, 18, 10, 3, 58, 614, DateTimeKind.Local).AddTicks(6597),
                                                    Description = @"Nasze aplikacje ASP.NET CORE coraz częściej są tylko aplikacją REST. To oczywiście wymaga Walidacji po stronie klienta i po stronie serwera
                        Jak taką walidację jak najszybciej zrobić.Może przecież sam napisać takie warunki,
                                        ale przy dużej ilości klas,
                                        które występują jako parametry mija się to z celem.
                        
                        Możesz też skorzystać z atrybutów i oznaczyć reguły do każdej właściwości.",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 8,
                                                    Title = "Walidacja z FluentValidation w ASP.NET Core + Swagger",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/walidacja-z-fluentvalidation-waspnet-core--swagger"
                                                },
                                                new
                                                {
                                                    PostId = 3,
                                                    Author = "Stefan",
                                                    CategoryId = 5,
                                                    Date = new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7480),
                                                    Description = @"W pod koniec roku 2017 zacząłem ćwiczyć. Proste ćwiczenia rzeczywiście robią różnice, gdy masz siedzący tryb życia. A co z bieganiem ?
                        Pamiętam jak pierwszy raz na bieżni nie byłem w stanie wytrzymać 5 minut normalnego spaceru. Powoli z tygodnia na dzień zacząłem sobie stawiać wyższe progi i tak odkryłem, że o ile jest to na początku bolesne to jak twoje ciało da Ci te endorfiny to już...aż chce się biegać więcej. ",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 5,
                                                    Title = "Bieganie jak się do tego zmotywować : Zdrowie Programisty",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/bieganie-jak-sie-do-tego-zmotywowac--zdrowie-programisty"
                                                },
                                                new
                                                {
                                                    PostId = 2,
                                                    Author = "Damian",
                                                    CategoryId = 2,
                                                    Date = new DateTime(2020, 7, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7347),
                                                    Description = "Programiści codziennie tworzą jakąś aplikację sieciową typu REST. Teraz nastaje pytanie, jak najlepiej zrozumieć jak dane API działa. Do tego mamy dokumentacje, ale jeśli pracujesz w szybkich, zamkniętych projektach to takiej dokumentacji może nie być.",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 7,
                                                    Title = "Swagger UI : Dokumentowanie API w ASP.NET CORE",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/swagger-ui--dokumentowanie-api-w-aspnet-core"
                                                },
                                                new
                                                {
                                                    PostId = 4,
                                                    Author = "Damian",
                                                    CategoryId = 2,
                                                    Date = new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7491),
                                                    Description = "Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 5,
                                                    Title = "NLog z ASP.NET Core : Logowanie błędów w aplikacji",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji"
                                                },
                                                new
                                                {
                                                    PostId = 5,
                                                    Author = "Damian",
                                                    CategoryId = 2,
                                                    Date = new DateTime(2020, 1, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7495),
                                                    Description = "Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 5,
                                                    Title = "NLog z ASP.NET Core : Logowanie błędów w aplikacji",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji"
                                                },
                                                new
                                                {
                                                    PostId = 6,
                                                    Author = "Damian",
                                                    CategoryId = 2,
                                                    Date = new DateTime(2020, 5, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7499),
                                                    Description = "W tym  artykule zobaczymy jak zintegrować AutoMapper  z ASP.NET CORE dla .NET 5, chociaż bądźmy szczerzy możesz skorzystać z tej biblioteki w każdym projekcie w C#.",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 9,
                                                    Title = "AutoMapper z ASP.NET CORE czyli mapowanie klas",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core"
                                                },
                                                new
                                                {
                                                    PostId = 8,
                                                    Author = "Adrian",
                                                    CategoryId = 3,
                                                    Date = new DateTime(2019, 10, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7507),
                                                    Description = @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 5,
                                                    Title = "QuickLook, TeraCopy, ProcessExplorer czy to potrzebne jest ?",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow"
                                                },
                                                new
                                                {
                                                    PostId = 7,
                                                    Author = "Adrian",
                                                    CategoryId = 3,
                                                    Date = new DateTime(2019, 11, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7503),
                                                    Description = "Nagrywanie Gif - ów ? Robienie obrazków na bloga ? Jak to robić jeszcze szybciej ? ",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 4,
                                                    Title = "ShareX : Lepszy PrintScreen oraz robienie Gif-ów twojego pulpitu?",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow"
                                                },
                                                new
                                                {
                                                    PostId = 9,
                                                    Author = "Adrian",
                                                    CategoryId = 4,
                                                    Date = new DateTime(2019, 10, 18, 10, 3, 58, 617, DateTimeKind.Local).AddTicks(7512),
                                                    Description = @"Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.",
                                                    ImageUrl = "https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png",
                                                    Rate = 9,
                                                    Title = "Docker File dla Go, ASP.NET Core, .NET 5, Java Spring, NodeJS, Python",
                                                    Url = "https://cezarywalenciuk.pl/blog/programing/docker-file-dla-go-aspnet-core-net-5-java-spring-nodejs-python"
                                                });
                                        });
                        
                                    modelBuilder.Entity("EduZbieracz.Domain.Entities.Webinar", b =>
                                        {
                                            b.Property<int>("Id")
                                                .ValueGeneratedOnAdd()
                                                .HasColumnType("int")
                                                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
                        
                                            b.Property<bool>("AlreadyHappend")
                                                .HasColumnType("bit");
                        
                                            b.Property<DateTime>("Date")
                                                .HasColumnType("datetime2");
                        
                                            b.Property<string>("Description")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("FacebookEventUrl")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("ImageUrl")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("SlidesUrl")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("Title")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("WatchFacebookLink")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.Property<string>("WatchYoutubeLink")
                                                .HasColumnType("nvarchar(max)");
                        
                                            b.HasKey("Id");
                        
                                            b.ToTable("Webinars");
                        
                                            b.HasData(
                                                new
                                                {
                                                    Id = 1,
                                                    AlreadyHappend = false,
                                                    Date = new DateTime(2021, 1, 28, 10, 3, 58, 620, DateTimeKind.Local).AddTicks(5513),
                                                    Description = "Ustalenie architektury nie jest prostym zadaniem. Każda decyzja może mieć wielkie komplikacje potem.",
                                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg",
                                                    SlidesUrl = "",
                                                    Title = "Aplikacja C# od Zera Architektura, CQRS, Dobre praktyki",
                                                    WatchFacebookLink = "",
                                                    WatchYoutubeLink = ""
                                                },
                                                new
                                                {
                                                    Id = 2,
                                                    AlreadyHappend = false,
                                                    Date = new DateTime(2020, 12, 9, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7359),
                                                    Description = "Kontenery są tutaj. Kubernetes jest de facto platformą do ich uruchamiania i zarządzania.",
                                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg",
                                                    SlidesUrl = "https://panniebieski.github.io/webinar-Kubernetes-Docker-Wytlumacz-mi-i-pokaz/",
                                                    Title = "Kubernetes i Docker : Wytłumacz mi i pokaż",
                                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2775230679405348&ref=watch_permalink",
                                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=7g00wOg9Jto"
                                                },
                                                new
                                                {
                                                    Id = 3,
                                                    AlreadyHappend = false,
                                                    Date = new DateTime(2020, 11, 19, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7537),
                                                    Description = "Jak utworzyć projekt w .NET 5?",
                                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                                    Title = "C# 9, Rekordy i duże zmiany w .NET 5",
                                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg"
                                                },
                                                new
                                                {
                                                    Id = 4,
                                                    AlreadyHappend = false,
                                                    Date = new DateTime(2020, 11, 9, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7547),
                                                    Description = "Czasami jedyne czego potrzebujemy to dobrego przykładu.",
                                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                                    Title = "Szybki Trening Sql Server 2",
                                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg"
                                                },
                                                new
                                                {
                                                    Id = 5,
                                                    AlreadyHappend = false,
                                                    Date = new DateTime(2020, 10, 20, 10, 3, 58, 621, DateTimeKind.Local).AddTicks(7554),
                                                    Description = "Jak wygląda szukanie pracy jako programista w 2020 roku? Czy jest lepiej, czy jest gorzej?",
                                                    FacebookEventUrl = "https://www.facebook.com/events/407358067213893/",
                                                    ImageUrl = "https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg",
                                                    SlidesUrl = "https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/",
                                                    Title = "Pytania rekrutacyjne czyli dalsza kariera",
                                                    WatchFacebookLink = "https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink",
                                                    WatchYoutubeLink = "https://www.youtube.com/watch?v=ATbLEyd_1Kg"
                                                });
                                        });
                        
                                    modelBuilder.Entity("EduZbieracz.Domain.Entities.Post", b =>
                                        {
                                            b.HasOne("EduZbieracz.Domain.Entities.Category", "Category")
                                                .WithMany("Posts")
                                                .HasForeignKey("CategoryId")
                                                .OnDelete(DeleteBehavior.Cascade)
                                                .IsRequired();
                                        });
                        #pragma warning restore 612, 618
                                }
                            }
                        }
                        
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

CREATE TABLE

                    



























































































                        IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
                        BEGIN
                            CREATE TABLE [__EFMigrationsHistory] (
                                [MigrationId] nvarchar(150) NOT NULL,
                                [ProductVersion] nvarchar(32) NOT NULL,
                                CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
                            );
                        END;
                        
                        GO
                        
                        CREATE TABLE [Categories] (
                            [CategoryId] int NOT NULL IDENTITY,
                            [CreatedBy] nvarchar(max) NULL,
                            [CreatedDate] datetime2 NOT NULL,
                            [LastModifiedBy] nvarchar(max) NULL,
                            [LastModifiedDate] datetime2 NULL,
                            [Name] nvarchar(max) NULL,
                            [DisplayName] nvarchar(max) NULL,
                            CONSTRAINT [PK_Categories] PRIMARY KEY ([CategoryId])
                        );
                        
                        GO
                        
                        CREATE TABLE [Webinars] (
                            [Id] int NOT NULL IDENTITY,
                            [Title] nvarchar(max) NULL,
                            [Description] nvarchar(max) NULL,
                            [ImageUrl] nvarchar(max) NULL,
                            [FacebookEventUrl] nvarchar(max) NULL,
                            [SlidesUrl] nvarchar(max) NULL,
                            [WatchFacebookLink] nvarchar(max) NULL,
                            [WatchYoutubeLink] nvarchar(max) NULL,
                            [Date] datetime2 NOT NULL,
                            [AlreadyHappend] bit NOT NULL,
                            CONSTRAINT [PK_Webinars] PRIMARY KEY ([Id])
                        );
                        
                        GO
                        
                        CREATE TABLE [Posts] (
                            [PostId] int NOT NULL IDENTITY,
                            [Title] nvarchar(80) NOT NULL,
                            [Author] nvarchar(max) NULL,
                            [Date] datetime2 NOT NULL,
                            [Description] nvarchar(max) NULL,
                            [CategoryId] int NOT NULL,
                            [ImageUrl] nvarchar(max) NULL,
                            [Url] nvarchar(max) NULL,
                            [Rate] int NOT NULL,
                            CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId]),
                            CONSTRAINT [FK_Posts_Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [Categories] ([CategoryId]) ON DELETE CASCADE
                        );
                        
                        GO
                        
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'CategoryId', N'CreatedBy', N'CreatedDate', N'DisplayName', N'LastModifiedBy', N'LastModifiedDate', N'Name') AND [object_id] = OBJECT_ID(N'[Categories]'))
                            SET IDENTITY_INSERT [Categories] ON;
                        INSERT INTO [Categories] ([CategoryId], [CreatedBy], [CreatedDate], [DisplayName], [LastModifiedBy], [LastModifiedDate], [Name])
                        VALUES (1, NULL, '0001-01-01T00:00:00.0000000', N'C#', NULL, NULL, N'CSharp'),
                        (3, NULL, '0001-01-01T00:00:00.0000000', N'Triki z Windows', NULL, NULL, N'triki-z-windows'),
                        (2, NULL, '0001-01-01T00:00:00.0000000', N'ASP.NET', NULL, NULL, N'aspnet'),
                        (4, NULL, '0001-01-01T00:00:00.0000000', N'Docker', NULL, NULL, N'docker'),
                        (5, NULL, '0001-01-01T00:00:00.0000000', N'Filozofia', NULL, NULL, N'filozofia');
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'CategoryId', N'CreatedBy', N'CreatedDate', N'DisplayName', N'LastModifiedBy', N'LastModifiedDate', N'Name') AND [object_id] = OBJECT_ID(N'[Categories]'))
                            SET IDENTITY_INSERT [Categories] OFF;
                        
                        GO
                        
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'AlreadyHappend', N'Date', N'Description', N'FacebookEventUrl', N'ImageUrl', N'SlidesUrl', N'Title', N'WatchFacebookLink', N'WatchYoutubeLink') AND [object_id] = OBJECT_ID(N'[Webinars]'))
                            SET IDENTITY_INSERT [Webinars] ON;
                        INSERT INTO [Webinars] ([Id], [AlreadyHappend], [Date], [Description], [FacebookEventUrl], [ImageUrl], [SlidesUrl], [Title], [WatchFacebookLink], [WatchYoutubeLink])
                        VALUES (1, CAST(0 AS bit), '2021-01-28T10:03:58.6205513+01:00', N'Ustalenie architektury nie jest prostym zadaniem. Każda decyzja może mieć wielkie komplikacje potem.', N'https://www.facebook.com/events/407358067213893/', N'https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg', N'', N'Aplikacja C# od Zera Architektura, CQRS, Dobre praktyki', N'', N''),
                        (2, CAST(0 AS bit), '2020-12-09T10:03:58.6217359+01:00', N'Kontenery są tutaj. Kubernetes jest de facto platformą do ich uruchamiania i zarządzania.', N'https://www.facebook.com/events/407358067213893/', N'https://cezarywalenciuk.pl/posts/fileswebinars/17_apliacjacsharpodzeraarchitekturacqrs.jpg', N'https://panniebieski.github.io/webinar-Kubernetes-Docker-Wytlumacz-mi-i-pokaz/', N'Kubernetes i Docker : Wytłumacz mi i pokaż', N'https://www.facebook.com/watch/live/?v=2775230679405348&ref=watch_permalink', N'https://www.youtube.com/watch?v=7g00wOg9Jto'),
                        (3, CAST(0 AS bit), '2020-11-19T10:03:58.6217537+01:00', N'Jak utworzyć projekt w .NET 5?', N'https://www.facebook.com/events/407358067213893/', N'https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg', N'https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/', N'C# 9, Rekordy i duże zmiany w .NET 5', N'https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink', N'https://www.youtube.com/watch?v=ATbLEyd_1Kg'),
                        (4, CAST(0 AS bit), '2020-11-09T10:03:58.6217547+01:00', N'Czasami jedyne czego potrzebujemy to dobrego przykładu.', N'https://www.facebook.com/events/407358067213893/', N'https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg', N'https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/', N'Szybki Trening Sql Server 2', N'https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink', N'https://www.youtube.com/watch?v=ATbLEyd_1Kg'),
                        (5, CAST(0 AS bit), '2020-10-20T10:03:58.6217554+01:00', N'Jak wygląda szukanie pracy jako programista w 2020 roku? Czy jest lepiej, czy jest gorzej?', N'https://www.facebook.com/events/407358067213893/', N'https://cezarywalenciuk.pl/posts/fileswebinars/15_csharpirekordy.jpg', N'https://panniebieski.github.io/webinar_CSharp9-Rekordy-i-duze-zamiany-w-net-5/', N'Pytania rekrutacyjne czyli dalsza kariera', N'https://www.facebook.com/watch/live/?v=2835303250091399&ref=watch_permalink', N'https://www.youtube.com/watch?v=ATbLEyd_1Kg');
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'AlreadyHappend', N'Date', N'Description', N'FacebookEventUrl', N'ImageUrl', N'SlidesUrl', N'Title', N'WatchFacebookLink', N'WatchYoutubeLink') AND [object_id] = OBJECT_ID(N'[Webinars]'))
                            SET IDENTITY_INSERT [Webinars] OFF;
                        
                        GO
                        
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'PostId', N'Author', N'CategoryId', N'Date', N'Description', N'ImageUrl', N'Rate', N'Title', N'Url') AND [object_id] = OBJECT_ID(N'[Posts]'))
                            SET IDENTITY_INSERT [Posts] ON;
                        INSERT INTO [Posts] ([PostId], [Author], [CategoryId], [Date], [Description], [ImageUrl], [Rate], [Title], [Url])
                        VALUES (8, N'Adrian', 3, '2019-10-18T10:03:58.6177507+01:00', N'Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 5, N'QuickLook, TeraCopy, ProcessExplorer czy to potrzebne jest ?', N'https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow'),
                        (7, N'Adrian', 3, '2019-11-18T10:03:58.6177503+01:00', N'Nagrywanie Gif - ów ? Robienie obrazków na bloga ? Jak to robić jeszcze szybciej ? ', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 4, N'ShareX : Lepszy PrintScreen oraz robienie Gif-ów twojego pulpitu?', N'https://cezarywalenciuk.pl/blog/programing/sharex-lepszy-printscreen-oraz-robienie-gif-ow'),
                        (1, N'Damian', 2, '2020-07-18T10:03:58.6146597+01:00', N'Nasze aplikacje ASP.NET CORE coraz częściej są tylko aplikacją REST. To oczywiście wymaga Walidacji po stronie klienta i po stronie serwera
                                        Jak taką walidację jak najszybciej zrobić.Może przecież sam napisać takie warunki,
                                                        ale przy dużej ilości klas,
                                                        które występują jako parametry mija się to z celem.
                        
                                        Możesz też skorzystać z atrybutów i oznaczyć reguły do każdej właściwości.', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 8, N'Walidacja z FluentValidation w ASP.NET Core + Swagger', N'https://cezarywalenciuk.pl/blog/programing/walidacja-z-fluentvalidation-waspnet-core--swagger'),
                        (2, N'Damian', 2, '2020-07-18T10:03:58.6177347+01:00', N'Programiści codziennie tworzą jakąś aplikację sieciową typu REST. Teraz nastaje pytanie, jak najlepiej zrozumieć jak dane API działa. Do tego mamy dokumentacje, ale jeśli pracujesz w szybkich, zamkniętych projektach to takiej dokumentacji może nie być.', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 7, N'Swagger UI : Dokumentowanie API w ASP.NET CORE', N'https://cezarywalenciuk.pl/blog/programing/swagger-ui--dokumentowanie-api-w-aspnet-core'),
                        (4, N'Damian', 2, '2020-01-18T10:03:58.6177491+01:00', N'Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 5, N'NLog z ASP.NET Core : Logowanie błędów w aplikacji', N'https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji'),
                        (5, N'Damian', 2, '2020-01-18T10:03:58.6177495+01:00', N'Logowanie działania aplikacji. Jak wiedzieć w końcu, gdy coś nie działa. Mój blog jest napisany w C# i działa po ASP.NET CORE. Jak to jednak bywa z napisaną przez siebie aplikacją pojawiają się błędy więc do bloga dodałem mechanizm logowania błędów. W taki sposób znalazłem wiele dziwnych przypadków uszkodzonych wpisów w formacie XML, które rozwalały Parser. Znalazłem też złe zbudowane przez ze mnie lista kursów. ', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 5, N'NLog z ASP.NET Core : Logowanie błędów w aplikacji', N'https://cezarywalenciuk.pl/blog/programing/nlog-z-aspnet-core--logowanie-b%C5%82edow-w-aplikacji'),
                        (6, N'Damian', 2, '2020-05-18T10:03:58.6177499+01:00', N'W tym  artykule zobaczymy jak zintegrować AutoMapper  z ASP.NET CORE dla .NET 5, chociaż bądźmy szczerzy możesz skorzystać z tej biblioteki w każdym projekcie w C#.', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 9, N'AutoMapper z ASP.NET CORE czyli mapowanie klas', N'https://cezarywalenciuk.pl/blog/programing/automapper-z-aspnet-core'),
                        (9, N'Adrian', 4, '2019-10-18T10:03:58.6177512+01:00', N'Jak jeszcze lepiej ulepszyć system operacyjny Windows.
                        
                                        Czy być może programy tobie, które za chwilę to śmieci, które nie będą ci potrzebne?
                        
                                        Zazwyczaj w tym cyklu pokazuje programy, z które moim bardzo zmieniają przepływ mojej pracy.', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 9, N'Docker File dla Go, ASP.NET Core, .NET 5, Java Spring, NodeJS, Python', N'https://cezarywalenciuk.pl/blog/programing/docker-file-dla-go-aspnet-core-net-5-java-spring-nodejs-python'),
                        (3, N'Stefan', 5, '2020-01-18T10:03:58.6177480+01:00', N'W pod koniec roku 2017 zacząłem ćwiczyć. Proste ćwiczenia rzeczywiście robią różnice, gdy masz siedzący tryb życia. A co z bieganiem ?
                                        Pamiętam jak pierwszy raz na bieżni nie byłem w stanie wytrzymać 5 minut normalnego spaceru. Powoli z tygodnia na dzień zacząłem sobie stawiać wyższe progi i tak odkryłem, że o ile jest to na początku bolesne to jak twoje ciało da Ci te endorfiny to już...aż chce się biegać więcej. ', N'https://cezarywalenciuk.pl/Posts/programing/icons/_withbackground/R2/656_walidacja-z-fluentvalidation-waspnet-core--swagger.png', 5, N'Bieganie jak się do tego zmotywować : Zdrowie Programisty', N'https://cezarywalenciuk.pl/blog/programing/bieganie-jak-sie-do-tego-zmotywowac--zdrowie-programisty');
                        IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'PostId', N'Author', N'CategoryId', N'Date', N'Description', N'ImageUrl', N'Rate', N'Title', N'Url') AND [object_id] = OBJECT_ID(N'[Posts]'))
                            SET IDENTITY_INSERT [Posts] OFF;
                        
                        GO
                        
                        CREATE INDEX [IX_Posts_CategoryId] ON [Posts] ([CategoryId]);
                        
                        GO
                        
                        INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
                        VALUES (N'20210118090359_Init', N'3.1.11');
                        
                        GO
                        
                        
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Teraz gdy mamy to z głowy. Pora na stworzenie API
To co tworzymy klasy "Service" i wydzielamy to?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

PostController

                    
                        [Route("api/[controller]")]
                        [ApiController]
                        public class PostController : ControllerBase
                        {
                            private readonly IMediator _mediator;
                        
                            public PostController(IMediator mediator)
                            {
                                _mediator = mediator;
                            }
                        
                            [HttpGet(Name = "GetAllPosts")]
                            public async Task<ActionResult<List<PostInListViewModel>>> GetAllPosts()
                            {
                                var list = await _mediator.Send(new GetPostsListQuery());
                                return Ok(list);
                            }
                    
                

PostController

                    
                        [HttpGet("{id}", Name = "GetPostById")]
                        [ProducesResponseType(StatusCodes.Status200OK)]
                        [ProducesDefaultResponseType]
                        public async Task<ActionResult<PostDetailViewModel>> GetPostById(int id)
                        {
                            var detailViewModel = await _mediator.Send
                                (new GetPostDetailQuery() { Id = id });
                            return Ok(detailViewModel);
                        }
                        
                        [HttpPost(Name = "AddPost")]
                        public async Task<ActionResult<int>> Create([FromBody] CreatedPostCommand createPostCommand)
                        {
                            var result = await _mediator.Send(createPostCommand);
                            return Ok(result.PostId);
                        }
                    
                

PostController

                    
                        [HttpPut(Name = "UpdatePost")]
                        [ProducesResponseType(StatusCodes.Status204NoContent)]
                        [ProducesResponseType(StatusCodes.Status404NotFound)]
                        [ProducesDefaultResponseType]
                        public async Task<ActionResult> Update([FromBody] UpdatePostCommand updatePostCommand)
                        {
                            await _mediator.Send(updatePostCommand);
                            return NoContent();
                        }
                        
                        [HttpDelete("{id}", Name = "DeletePost")]
                        [ProducesResponseType(StatusCodes.Status204NoContent)]
                        [ProducesResponseType(StatusCodes.Status404NotFound)]
                        [ProducesDefaultResponseType]
                        public async Task<ActionResult> Delete(int id)
                        {
                            var deletepostCommand = new DeletePostCommand() { PostId = id };
                            await _mediator.Send(deletepostCommand);
                            return NoContent();
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk

WebinarController

                    






































                        


                        
                        [Route("api/[controller]")]
                        [ApiController]
                        public class WebinarController : Controller
                        {
                            private readonly IMediator _mediator;
                        
                            public WebinarController(IMediator mediator)
                            {
                                _mediator = mediator;
                            }
                        
                            [HttpGet("/getwebinarfordate", Name = "GetPagedWebinarsForDate")]
                            [ProducesResponseType(StatusCodes.Status200OK)]
                            [ProducesDefaultResponseType]
                            public async Task<ActionResult<PageWebinarByDateViewModel>> GetPagedWebinarsForMonth(SearchOptionsWebinars searchOptionsWebinars, int page, int pagesize, DateTime? date)
                            {
                                var getWebinarForMonthQuery = new GetWebinarsByDateQuery()
                                { Date = date, Page = page, PageSize = pagesize, Options = searchOptionsWebinars };
                                var pageWebinarsByDateViewModel = await _mediator.Send(getWebinarForMonthQuery);
                        
                                return Ok(pageWebinarsByDateViewModel);
                            }
                        
                        
                            [HttpPost(Name = "AddWebinar")]
                            public async Task<ActionResult<int>> Create([FromBody] CreatedWebinarCommand createWebinarCommand)
                            {
                                var result = await _mediator.Send(createWebinarCommand);
                                return Ok(result.Id);
                            }
                        
                            [HttpGet("{id}", Name = "GetWebinar")]
                            [ProducesResponseType(StatusCodes.Status200OK)]
                            [ProducesDefaultResponseType]
                            public async Task<ActionResult<WebinarViewModel>> GetWebinarById(int id)
                            {
                                var result = await _mediator.Send((new GetWebinarQuery() { Id = id }));
                                return Ok(result);
                            }
                        
                            [HttpPut(Name = "UpdateWebinar")]
                            [ProducesResponseType(StatusCodes.Status204NoContent)]
                            [ProducesResponseType(StatusCodes.Status404NotFound)]
                            [ProducesDefaultResponseType]
                            public async Task<ActionResult> Update([FromBody] UpdateWebinarCommand updatePostCommand)
                            {
                                await _mediator.Send(updatePostCommand);
                        
                        
                                return NoContent();
                            }
                        
                            [HttpDelete("{id}", Name = "DeleteWebinar")]
                            [ProducesResponseType(StatusCodes.Status204NoContent)]
                            [ProducesResponseType(StatusCodes.Status404NotFound)]
                            [ProducesDefaultResponseType]
                            public async Task<ActionResult> Delete(int id)
                            {
                                var deletepostCommand = new DeleteWebinarCommand() { WebinarId = id };
                                await _mediator.Send(deletepostCommand);
                                return NoContent();
                            }
                        }
                    
                

CategoriesController

                    



















                        [Route("api/[controller]")]
                        [ApiController]
                        public class CategoriesController : Controller
                        {
                            private readonly IMediator _mediator;
                        
                            public CategoriesController(IMediator mediator)
                            {
                                _mediator = mediator;
                            }
                        


                            [HttpGet("all", Name = "GetAllCategories")]
                            [ProducesResponseType(StatusCodes.Status200OK)]
                            public async Task<ActionResult<List<CategoryInListViewModel>>> GetAllCategories()
                            {
                                var categoryInListViewModel = await _mediator.Send(new GetCategoriesListQuery());
                                return Ok(categoryInListViewModel);
                            }
                        



                            [HttpGet("allwithposts", Name = "GetCategoriesWithPosts")]
                            [ProducesDefaultResponseType]
                            [ProducesResponseType(StatusCodes.Status200OK)]
                            public async Task<ActionResult<List<CategoryPostListViewModel>>> GetCategoriesWithPosts
                                (SearchCategoryOptions searchOptions)
                            {
                                GetCategoriesWithPostListQuery getCategoriesListWithPostsQuery =
                                    new GetCategoriesWithPostListQuery() { searchCategory = searchOptions };
                        
                                var dtos = await _mediator.Send(getCategoriesListWithPostsQuery);
                                return Ok(dtos);
                            }
                        
                            [HttpPost(Name = "addCategory")]
                            public async Task<ActionResult<CreatedCategoryCommandResponse>> Create
                                ([FromBody] CreatedCategoryCommand createCategoryCommand)
                            {
                                var response = await _mediator.Send(createCategoryCommand);
                                return Ok(response);
                            }
                        }
                    
                

Pozostało jeszcze to ?
blog
Copyright © 2021 Cezary Walenciuk
Dla ułatwienia ten problem zostawie
Już prawie mamy wszystko 😎
Jak testować REST API ?
blog
Copyright © 2021 Cezary Walenciuk
Dodajmy Swagger UI 😎
blog
Copyright © 2021 Cezary Walenciuk

Projekt API

                    
                        <Project Sdk="Microsoft.NET.Sdk.Web">

                        <PropertyGroup>
                          <TargetFramework>net5.0</TargetFramework>
                        </PropertyGroup>
                      
                        <ItemGroup>
                          <ProjectReference Include="..\EduZbieracz.Application\EduZbieracz.Application.csproj" />
                          <ProjectReference Include="..\EduZbieracz.Persistence\EduZbieracz.Persistence.EF.csproj" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.11">
                            <PrivateAssets>all</PrivateAssets>
                            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
                          </PackageReference>
                          <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
                          <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
                        </ItemGroup>
                      
                      </Project>
                      
                    
                

ConfigureServices

                    
                        public void ConfigureServices(IServiceCollection services)
                        {
                            services.AddSwaggerGen(c =>
                            {
                                c.SwaggerDoc("v1", new OpenApiInfo
                                {
                                    Version = "v1",
                                    Title = "Edu Zbieracz API",
                                });
                        
                            });
                        
                            services.AddEduZbieraczApplication();
                            services.AddEduZbieraczPersistenceEFServices(Configuration);
                        
                            services.AddControllers();
                        
                            services.AddCors(options =>
                            {
                                options.AddPolicy("Open",
                                    builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
                            });
                        }
                    
                

Configure

                    
                        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
                        {
                            if (env.IsDevelopment())
                            {
                                app.UseDeveloperExceptionPage();
                            }
                        
                            app.UseHttpsRedirection();
                            app.UseRouting();
                        
                            app.UseSwagger();
                            app.UseSwaggerUI(c =>
                            {
                                c.SwaggerEndpoint("swagger/v1/swagger.json", "Edu Zbieracz API");
                            });
                        
                        
                            app.UseCors("Open");
                        
                            app.UseEndpoints(endpoints =>
                            {
                                endpoints.MapControllers();
                            });
                        }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Przechodzimy do
📊 UI 📊
Blazor pozwala stworzyć Tobie aplikację SPA w C#.
WebAssembly

Zalety :

Wady :

blog
Copyright © 2021 Cezary Walenciuk

Problemy :

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Jak wygląda projekt na start?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Jakie paczki NuGet?
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

Nuget

                    
                        <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

                        <PropertyGroup>
                          <TargetFramework>net5.0</TargetFramework>
                        </PropertyGroup>
                      
                        <ItemGroup>
                          <Compile Remove="Pages\CategoryList.razor.cs" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <Content Remove="Pages\CategoryList.razor" />
                        </ItemGroup>
                      
                        <ItemGroup>
                          <PackageReference Include="AutoMapper" Version="10.1.1" />
                          <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
                          <PackageReference Include="Blazored.LocalStorage" Version="3.0.0" />
                          <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.2" />
                          <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="5.0.2" />
                          <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.2" />
                          <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.2" PrivateAssets="all" />
                          <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
                          <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
                          <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
                        </ItemGroup>
                      
                      </Project>
                      
                    
                

Czy pisać swój kod?
blog
Copyright © 2021 Cezary Walenciuk
Inny problem to mozolne tworzenie nowych stron
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Nie mam ochoty pisać kodu HTTPClinet do mojego API w ASP.NET Core

Na pomoc przychodzi NSwag

NSwag

Generator kodu dla API

NSwag

Generator kodu dla API

Bazuje on na specyfikacji OpenAPI

NSwag

Generator kodu dla API

Bazuje on na specyfikacji OpenAPI

Działa dla TypeScript
i .NET CORE

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Wygenerowaną klasę warto przerobić

👿

Edycja wygenerowanej klasy

                    
                        public partial interface IClient
                        {
                            System.Net.Http.HttpClient HttpClient { get; }

                            ......
                    
                

Edycja wygenerowanej klasy PRZED

                    
                        public partial class Client : IClient
                        {
                            private string _baseUrl = "";
                            private System.Net.Http.HttpClient _httpClient;
                            private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
                            
                            public Client(string baseUrl, System.Net.Http.HttpClient httpClient)
                            {
                                BaseUrl = baseUrl; 
                                _httpClient = httpClient; 
                                _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
                            }
                    
                

Edycja wygenerowanej klasy PO

                    
                        public partial class Client : IClient
                        {
                            private string _baseUrl = "";
                            private System.Net.Http.HttpClient _httpClient;
                            private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
                            
                            public Client(System.Net.Http.HttpClient httpClient)
                            {
                            
                                _httpClient = httpClient;
                                _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
                            }
                    
                

Klasa Main

                    
                        public static async Task Main(string[] args)
                        {
                            var builder = WebAssemblyHostBuilder.CreateDefault(args);
                            builder.RootComponents.Add<App>("#app");
                        
                            builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
                            //builder.Services.AddBlazoredLocalStorage();
                        
                            string a = "https://localhost:5001/";
                            builder.Services.AddSingleton(new HttpClient
                            {
                                BaseAddress = new Uri("https://localhost:5001")
                            });
                        
                            builder.Services.AddHttpClient<IClient, Client>
                                (client => client.BaseAddress = new Uri("https://localhost:5001"));
                        
                        
                            builder.Services.AddScoped<ICategoryService, CategoryService>();
                            builder.Services.AddScoped<IWebinarService, WebinarService>();
                            builder.Services.AddScoped<IPostServices, PostServices>();
                        
                            await builder.Build().RunAsync();
                        }
                    
                

Skąd te

"https://localhost:5001"
blog
Copyright © 2021 Cezary Walenciuk

launchSettings.json

                    











                        {
                            "iisSettings": {
                              "windowsAuthentication": false,
                              "anonymousAuthentication": true,
                              "iis": {
                                "applicationUrl": "http://localhost/EduZbieracz.Api",
                                "sslPort": 0
                              },
                              "iisExpress": {
                                "applicationUrl": "http://localhost:5005",
                                "sslPort": 44366
                              }
                            },
                            "profiles": {
                              "IIS Express": {
                                "commandName": "IISExpress",
                                "launchBrowser": true,
                                "launchUrl": "swagger",
                                "environmentVariables": {
                                  "ASPNETCORE_ENVIRONMENT": "Development"
                                }
                              },
                              "EduZbieracz.Api": {
                                "commandName": "Project",
                                "launchBrowser": true,
                                "launchUrl": "swagger",
                                "environmentVariables": {
                                  "ASPNETCORE_ENVIRONMENT": "Development"
                                },
                                "applicationUrl": "https://localhost:5001;http://localhost:5000"
                              },
                              "Localhost": {
                                "commandName": "IIS"
                              }
                            }
                          }
                    
                

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk
Jak nazwać ViewModele w projekcie Blazor
blog
Copyright © 2021 Cezary Walenciuk

MappingProfile W Blazor

                    

                        public class MappingProfile : Profile
                        {
                            public MappingProfile()
                            {
                                CreateMap<PostInListViewModel, PostInListBlazorVM>().ReverseMap();
                                CreateMap<PostDetailViewModel, PostDetailBlazorVM>().ReverseMap();
                        
                                CreateMap<PostDetailBlazorVM, CreatedPostCommand>().ReverseMap();
                                CreateMap<PostDetailBlazorVM, UpdatePostCommand>().ReverseMap();
                        
                                CreateMap<CategoryPostDto, PostInsideCategoryBlazorVM>().ReverseMap();
                        
                                CreateMap<CategoryDto, CategoryBlazorVM>().ReverseMap();
                        
                                CreateMap<CategoryInListViewModel, CategoryBlazorVM>().ReverseMap();
                        
                                CreateMap<CategoryPostListViewModel, CategoryWithPostsBlazorVM>().ReverseMap();
                                CreateMap<CreatedCategoryCommand, CategoryBlazorVM>().ReverseMap();
                        
                                CreateMap<WebinarsByDateViewModel, WebinarForDateListBlazorVM>().ReverseMap();
                                CreateMap<PageWebinarByDateViewModel, WebinarPagedForDateBlazorVM>().ReverseMap();
                                CreateMap<WebinarViewModel, WebinarBlazorVM>().ReverseMap();
                        
                                CreateMap<CreatedWebinarCommand, WebinarBlazorVM>().ReverseMap();
                                CreateMap<UpdateWebinarCommand, WebinarBlazorVM>().ReverseMap();
                        
                            }
                        }
                    
                

DEMO
Co jeszcze można dodać
🤯 dodać 🤯
JSON Web Tokens

Co jeszcze można dodać

blog
Copyright © 2021 Cezary Walenciuk
blog
Copyright © 2021 Cezary Walenciuk

LoginController

                    
                        [Route("api/[controller]")]
                        [ApiController]
                        public class LoginController : ControllerBase
                        {
                            private readonly IAuthenticationService _authenticationService;
                            public LoginController(IAuthenticationService authenticationService)
                            {
                                _authenticationService = authenticationService;
                            }
                        
                            [HttpPost("authenticate", Name = "Authenticate")]
                            public async Task<ActionResult<AuthenticationResponse>> AuthenticateAsync(AuthenticationRequest request)
                            {
                                return Ok(await _authenticationService.AuthenticateAsync(request));
                            }
                        
                            [HttpPost("register", Name = "Register")]
                            public async Task<ActionResult<RegistrationResponse>> RegisterAsync(RegistrationRequest request)
                            {
                                return Ok(await _authenticationService.RegisterAsync(request));
                            }
                        }
                    
                

Co jeszcze można dodać

Podsumowanie