diff --git a/SampleWebApp.Core/Adapters/GenericServicesAdapter.cs b/SampleWebApp.Core/Adapters/GenericServicesAdapter.cs new file mode 100644 index 0000000..8a96905 --- /dev/null +++ b/SampleWebApp.Core/Adapters/GenericServicesAdapter.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Handlers.Posts.Commands; +using SampleWebApp.Core.Handlers.Posts.Queries; + +namespace SampleWebApp.Core.Adapters +{ + public class GenericServicesAdapter + { + private readonly IMediator _mediator; + private readonly IMapper _mapper; + + public GenericServicesAdapter(IMediator mediator, IMapper mapper) + { + _mediator = mediator; + _mapper = mapper; + } + + public async Task> GetAllPostsAsync() + { + var query = new GetPostsQuery(); + var result = await _mediator.Send(query); + return result.AsQueryable(); + } + + public async Task> GetPostsByBlogIdAsync(int blogId) + { + var query = new GetPostsQuery { BlogId = blogId }; + var result = await _mediator.Send(query); + return result.AsQueryable(); + } + + public async Task> GetPostDetailAsync(int id) + { + try + { + var query = new GetPostByIdQuery(id); + var result = await _mediator.Send(query); + + if (result == null) + { + return SuccessOrErrors.FailSingleError("Post not found"); + } + + return SuccessOrErrors.Success(result); + } + catch (Exception ex) + { + return SuccessOrErrors.FailSingleError(ex.Message); + } + } + + public async Task> GetPostForEditAsync(int id) + { + try + { + var query = new GetPostForEditQuery(id); + var result = await _mediator.Send(query); + + if (result == null) + { + return SuccessOrErrors.FailSingleError("Post not found"); + } + + return SuccessOrErrors.Success(result); + } + catch (Exception ex) + { + return SuccessOrErrors.FailSingleError(ex.Message); + } + } + + public async Task GetPostForCreateAsync() + { + var query = new GetPostForCreateQuery(); + return await _mediator.Send(query); + } + + public async Task CreatePostAsync(PostDto dto) + { + try + { + var command = new CreatePostCommand(dto); + var result = await _mediator.Send(command); + + if (result.IsValid) + { + return SuccessOrErrors.Success(result.SuccessMessage); + } + + var errors = SuccessOrErrors.FailSingleError(result.Errors.FirstOrDefault()?.ErrorMessage ?? "Unknown error"); + return errors; + } + catch (Exception ex) + { + return SuccessOrErrors.FailSingleError(ex.Message); + } + } + + public async Task UpdatePostAsync(PostDto dto) + { + try + { + var command = new UpdatePostCommand(dto); + var result = await _mediator.Send(command); + + if (result.IsValid) + { + return SuccessOrErrors.Success(result.SuccessMessage); + } + + var errors = SuccessOrErrors.FailSingleError(result.Errors.FirstOrDefault()?.ErrorMessage ?? "Unknown error"); + return errors; + } + catch (Exception ex) + { + return SuccessOrErrors.FailSingleError(ex.Message); + } + } + + public async Task DeletePostAsync(int id) + { + try + { + var command = new DeletePostCommand(id); + var result = await _mediator.Send(command); + + if (result.IsValid) + { + return SuccessOrErrors.Success(result.SuccessMessage); + } + + var errors = SuccessOrErrors.FailSingleError(result.Errors.FirstOrDefault()?.ErrorMessage ?? "Unknown error"); + return errors; + } + catch (Exception ex) + { + return SuccessOrErrors.FailSingleError(ex.Message); + } + } + } +} diff --git a/SampleWebApp.Core/Common/BaseDto.cs b/SampleWebApp.Core/Common/BaseDto.cs new file mode 100644 index 0000000..e38b895 --- /dev/null +++ b/SampleWebApp.Core/Common/BaseDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace SampleWebApp.Core.Common +{ + public abstract class BaseDto + { + public int Id { get; set; } + } + + public abstract class BaseDto : BaseDto where TEntity : class + { + } +} diff --git a/SampleWebApp.Core/Common/Commands/BaseCommand.cs b/SampleWebApp.Core/Common/Commands/BaseCommand.cs new file mode 100644 index 0000000..d45f6a3 --- /dev/null +++ b/SampleWebApp.Core/Common/Commands/BaseCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace SampleWebApp.Core.Common.Commands +{ + public abstract class BaseCommand : IRequest where TResponse : class + { + } +} diff --git a/SampleWebApp.Core/Common/Queries/BaseQuery.cs b/SampleWebApp.Core/Common/Queries/BaseQuery.cs new file mode 100644 index 0000000..d15f9a7 --- /dev/null +++ b/SampleWebApp.Core/Common/Queries/BaseQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace SampleWebApp.Core.Common.Queries +{ + public abstract class BaseQuery : IRequest where TResponse : class + { + } +} diff --git a/SampleWebApp.Core/Common/Results/CreateResult.cs b/SampleWebApp.Core/Common/Results/CreateResult.cs new file mode 100644 index 0000000..e82d897 --- /dev/null +++ b/SampleWebApp.Core/Common/Results/CreateResult.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace SampleWebApp.Core.Common.Results +{ + public class CreateResult : OperationResult + { + public int CreatedId { get; private set; } + + private CreateResult() { } + + public static CreateResult Success(string message, int createdId = 0) + { + return new CreateResult + { + SuccessMessage = message, + CreatedId = createdId + }; + } + + public new static CreateResult Failure(string errorMessage, string propertyName = null) + { + var result = new CreateResult(); + result.Errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public new static CreateResult Failure(IEnumerable errors) + { + var result = new CreateResult(); + result.Errors.AddRange(errors); + return result; + } + } +} diff --git a/SampleWebApp.Core/Common/Results/DeleteResult.cs b/SampleWebApp.Core/Common/Results/DeleteResult.cs new file mode 100644 index 0000000..06bfd4d --- /dev/null +++ b/SampleWebApp.Core/Common/Results/DeleteResult.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace SampleWebApp.Core.Common.Results +{ + public class DeleteResult : OperationResult + { + private DeleteResult() { } + + public new static DeleteResult Success(string message = null) + { + return new DeleteResult { SuccessMessage = message }; + } + + public new static DeleteResult Failure(string errorMessage, string propertyName = null) + { + var result = new DeleteResult(); + result.Errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public new static DeleteResult Failure(IEnumerable errors) + { + var result = new DeleteResult(); + result.Errors.AddRange(errors); + return result; + } + } +} diff --git a/SampleWebApp.Core/Common/Results/OperationResult.cs b/SampleWebApp.Core/Common/Results/OperationResult.cs new file mode 100644 index 0000000..3c1ea8d --- /dev/null +++ b/SampleWebApp.Core/Common/Results/OperationResult.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SampleWebApp.Core.Common.Results +{ + public class OperationResult + { + public bool IsValid => !Errors.Any(); + public string SuccessMessage { get; protected set; } + public List Errors { get; protected set; } = new List(); + + protected OperationResult() { } + + public static OperationResult Success(string message = null) + { + return new OperationResult { SuccessMessage = message }; + } + + public static OperationResult Failure(string errorMessage, string propertyName = null) + { + var result = new OperationResult(); + result.Errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public static OperationResult Failure(IEnumerable errors) + { + var result = new OperationResult(); + result.Errors.AddRange(errors); + return result; + } + + public void AddError(string errorMessage, string propertyName = null) + { + Errors.Add(new ValidationError(propertyName, errorMessage)); + } + + public string ErrorsAsHtml() + { + return string.Join("
", Errors.Select(e => e.ErrorMessage)); + } + } + + public class ValidationError + { + public string PropertyName { get; } + public string ErrorMessage { get; } + + public ValidationError(string propertyName, string errorMessage) + { + PropertyName = propertyName; + ErrorMessage = errorMessage; + } + } +} diff --git a/SampleWebApp.Core/Common/Results/PagedResult.cs b/SampleWebApp.Core/Common/Results/PagedResult.cs new file mode 100644 index 0000000..d40a0f8 --- /dev/null +++ b/SampleWebApp.Core/Common/Results/PagedResult.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace SampleWebApp.Core.Common.Results +{ + public class PagedResult where T : class + { + public IEnumerable Items { get; } + public int TotalCount { get; } + public int Page { get; } + public int PageSize { get; } + public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize); + public bool HasPreviousPage => Page > 1; + public bool HasNextPage => Page < TotalPages; + + public PagedResult(IEnumerable items, int totalCount, int page, int pageSize) + { + Items = items; + TotalCount = totalCount; + Page = page; + PageSize = pageSize; + } + } +} diff --git a/SampleWebApp.Core/Common/Results/SuccessOrErrors.cs b/SampleWebApp.Core/Common/Results/SuccessOrErrors.cs new file mode 100644 index 0000000..d434030 --- /dev/null +++ b/SampleWebApp.Core/Common/Results/SuccessOrErrors.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SampleWebApp.Core.Common.Results +{ + public interface ISuccessOrErrors + { + bool IsValid { get; } + string SuccessMessage { get; } + IReadOnlyList Errors { get; } + void AddNamedParameterError(string parameterName, string errorMessage); + string ErrorsAsHtml(); + } + + public interface ISuccessOrErrors : ISuccessOrErrors + { + T Result { get; } + } + + public class SuccessOrErrors : ISuccessOrErrors + { + private readonly List _errors = new List(); + + public bool IsValid => !_errors.Any(); + public string SuccessMessage { get; private set; } + public IReadOnlyList Errors => _errors.AsReadOnly(); + + protected SuccessOrErrors() { } + + public static SuccessOrErrors Success(string message) + { + return new SuccessOrErrors { SuccessMessage = message }; + } + + public static SuccessOrErrors FailSingleError(string errorMessage, string propertyName = null) + { + var result = new SuccessOrErrors(); + result._errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public void AddNamedParameterError(string parameterName, string errorMessage) + { + _errors.Add(new ValidationError(parameterName, errorMessage)); + } + + public string ErrorsAsHtml() + { + return string.Join("
", _errors.Select(e => e.ErrorMessage)); + } + } + + public class SuccessOrErrors : ISuccessOrErrors + { + private readonly List _errors = new List(); + + public bool IsValid => !_errors.Any(); + public string SuccessMessage { get; private set; } + public T Result { get; private set; } + public IReadOnlyList Errors => _errors.AsReadOnly(); + + private SuccessOrErrors() { } + + public static SuccessOrErrors Success(T result, string message = null) + { + return new SuccessOrErrors + { + Result = result, + SuccessMessage = message + }; + } + + public static SuccessOrErrors FailSingleError(string errorMessage, string propertyName = null) + { + var result = new SuccessOrErrors(); + result._errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public static SuccessOrErrors ConvertNonResultStatus(ISuccessOrErrors status) + { + var result = new SuccessOrErrors(); + foreach (var error in status.Errors) + { + result._errors.Add(error); + } + return result; + } + + public void AddNamedParameterError(string parameterName, string errorMessage) + { + _errors.Add(new ValidationError(parameterName, errorMessage)); + } + + public string ErrorsAsHtml() + { + return string.Join("
", _errors.Select(e => e.ErrorMessage)); + } + } +} diff --git a/SampleWebApp.Core/Common/Results/UpdateResult.cs b/SampleWebApp.Core/Common/Results/UpdateResult.cs new file mode 100644 index 0000000..f0794f8 --- /dev/null +++ b/SampleWebApp.Core/Common/Results/UpdateResult.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace SampleWebApp.Core.Common.Results +{ + public class UpdateResult : OperationResult + { + private UpdateResult() { } + + public new static UpdateResult Success(string message = null) + { + return new UpdateResult { SuccessMessage = message }; + } + + public new static UpdateResult Failure(string errorMessage, string propertyName = null) + { + var result = new UpdateResult(); + result.Errors.Add(new ValidationError(propertyName, errorMessage)); + return result; + } + + public new static UpdateResult Failure(IEnumerable errors) + { + var result = new UpdateResult(); + result.Errors.AddRange(errors); + return result; + } + } +} diff --git a/SampleWebApp.Core/DTOs/BlogDto.cs b/SampleWebApp.Core/DTOs/BlogDto.cs new file mode 100644 index 0000000..88f16be --- /dev/null +++ b/SampleWebApp.Core/DTOs/BlogDto.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using SampleWebApp.Core.Common; + +namespace SampleWebApp.Core.DTOs +{ + public class BlogDto : BaseDto + { + public int BlogId { get; set; } + + [Required] + [MinLength(2)] + [MaxLength(64)] + public string Name { get; set; } + + [Required] + [MaxLength(256)] + [EmailAddress] + public string EmailAddress { get; set; } + + public int PostsCount { get; set; } + } +} diff --git a/SampleWebApp.Core/DTOs/PostDto.cs b/SampleWebApp.Core/DTOs/PostDto.cs new file mode 100644 index 0000000..15ad7b0 --- /dev/null +++ b/SampleWebApp.Core/DTOs/PostDto.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using SampleWebApp.Core.Common; +using SampleWebApp.Core.UiClasses; + +namespace SampleWebApp.Core.DTOs +{ + public class PostDto : BaseDto + { + public int PostId { get; set; } + + [Required] + [MinLength(2), MaxLength(128)] + public string Title { get; set; } + + [Required] + [DataType(DataType.MultilineText)] + public string Content { get; set; } + + public string BloggerName { get; set; } + + public int BlogId { get; set; } + + public ICollection Tags { get; set; } + + public DateTime LastUpdated { get; set; } + + public DropDownListType Bloggers { get; set; } + + public MultiSelectListType UserChosenTags { get; set; } + + public DateTime LastUpdatedUtc => DateTime.SpecifyKind(LastUpdated, DateTimeKind.Utc); + + public string TagNames => Tags != null ? string.Join(", ", Tags.Select(x => x.Name)) : string.Empty; + + public PostDto() + { + Bloggers = new DropDownListType(); + UserChosenTags = new MultiSelectListType(); + Tags = new List(); + } + } +} diff --git a/SampleWebApp.Core/DTOs/SimplePostDto.cs b/SampleWebApp.Core/DTOs/SimplePostDto.cs new file mode 100644 index 0000000..54d5f1c --- /dev/null +++ b/SampleWebApp.Core/DTOs/SimplePostDto.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using SampleWebApp.Core.Common; + +namespace SampleWebApp.Core.DTOs +{ + public class SimplePostDto : BaseDto + { + public int PostId { get; set; } + + public int BlogId { get; set; } + + public string BloggerName { get; set; } + + [MinLength(2), MaxLength(128)] + public string Title { get; set; } + + public ICollection Tags { get; set; } + + public DateTime LastUpdated { get; set; } + + public DateTime LastUpdatedUtc => DateTime.SpecifyKind(LastUpdated, DateTimeKind.Utc); + + public string TagNames => Tags != null ? string.Join(", ", Tags.Select(x => x.Name)) : string.Empty; + } +} diff --git a/SampleWebApp.Core/DTOs/TagDto.cs b/SampleWebApp.Core/DTOs/TagDto.cs new file mode 100644 index 0000000..fdbb9cf --- /dev/null +++ b/SampleWebApp.Core/DTOs/TagDto.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using SampleWebApp.Core.Common; + +namespace SampleWebApp.Core.DTOs +{ + public class TagDto : BaseDto + { + public int TagId { get; set; } + + [Required] + [MaxLength(64)] + [RegularExpression(@"\w*", ErrorMessage = "The slug must not contain spaces or non-alphanumeric characters.")] + public string Slug { get; set; } + + [Required] + [MaxLength(128)] + public string Name { get; set; } + + public int PostsCount { get; set; } + } +} diff --git a/SampleWebApp.Core/DependencyInjection/ServiceCollectionExtensions.cs b/SampleWebApp.Core/DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..1d984d2 --- /dev/null +++ b/SampleWebApp.Core/DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,46 @@ +using System.Reflection; +using FluentValidation; +using MediatR; +using Microsoft.Extensions.DependencyInjection; +using SampleWebApp.Core.Adapters; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddCoreServices(this IServiceCollection services) + { + var assembly = typeof(ServiceCollectionExtensions).Assembly; + + services.AddMediatR(assembly); + + services.AddAutoMapper(assembly); + + services.AddValidatorsFromAssembly(assembly); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + + return services; + } + + public static IServiceCollection AddCoreServicesWithRepositories( + this IServiceCollection services) + where TPostRepository : class, IPostRepository + where TTagRepository : class, ITagRepository + where TBlogRepository : class, IBlogRepository + { + services.AddCoreServices(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogCommand.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogCommand.cs new file mode 100644 index 0000000..d9cd92a --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class CreateBlogCommand : BaseCommand + { + public BlogDto Blog { get; set; } + + public CreateBlogCommand() { } + + public CreateBlogCommand(BlogDto blog) + { + Blog = blog; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogHandler.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogHandler.cs new file mode 100644 index 0000000..e1831a3 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/CreateBlogHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class CreateBlogHandler : IRequestHandler + { + private readonly IBlogService _blogService; + + public CreateBlogHandler(IBlogService blogService) + { + _blogService = blogService; + } + + public async Task Handle(CreateBlogCommand request, CancellationToken cancellationToken) + { + return await _blogService.CreateAsync(request.Blog); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogCommand.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogCommand.cs new file mode 100644 index 0000000..7406ed7 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogCommand.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class DeleteBlogCommand : BaseCommand + { + public int Id { get; set; } + + public DeleteBlogCommand() { } + + public DeleteBlogCommand(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogHandler.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogHandler.cs new file mode 100644 index 0000000..a1ec15a --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/DeleteBlogHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class DeleteBlogHandler : IRequestHandler + { + private readonly IBlogService _blogService; + + public DeleteBlogHandler(IBlogService blogService) + { + _blogService = blogService; + } + + public async Task Handle(DeleteBlogCommand request, CancellationToken cancellationToken) + { + return await _blogService.DeleteAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogCommand.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogCommand.cs new file mode 100644 index 0000000..11ec667 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class UpdateBlogCommand : BaseCommand + { + public BlogDto Blog { get; set; } + + public UpdateBlogCommand() { } + + public UpdateBlogCommand(BlogDto blog) + { + Blog = blog; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogHandler.cs b/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogHandler.cs new file mode 100644 index 0000000..ad36665 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Commands/UpdateBlogHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Blogs.Commands +{ + public class UpdateBlogHandler : IRequestHandler + { + private readonly IBlogService _blogService; + + public UpdateBlogHandler(IBlogService blogService) + { + _blogService = blogService; + } + + public async Task Handle(UpdateBlogCommand request, CancellationToken cancellationToken) + { + return await _blogService.UpdateAsync(request.Blog); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdHandler.cs b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdHandler.cs new file mode 100644 index 0000000..5cd1b4a --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Blogs.Queries +{ + public class GetBlogByIdHandler : IRequestHandler + { + private readonly IBlogService _blogService; + + public GetBlogByIdHandler(IBlogService blogService) + { + _blogService = blogService; + } + + public async Task Handle(GetBlogByIdQuery request, CancellationToken cancellationToken) + { + return await _blogService.GetByIdAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdQuery.cs b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdQuery.cs new file mode 100644 index 0000000..82382d3 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogByIdQuery.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Blogs.Queries +{ + public class GetBlogByIdQuery : BaseQuery + { + public int Id { get; set; } + + public GetBlogByIdQuery() { } + + public GetBlogByIdQuery(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsHandler.cs b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsHandler.cs new file mode 100644 index 0000000..bbb543d --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsHandler.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Blogs.Queries +{ + public class GetBlogsHandler : IRequestHandler> + { + private readonly IBlogService _blogService; + + public GetBlogsHandler(IBlogService blogService) + { + _blogService = blogService; + } + + public async Task> Handle(GetBlogsQuery request, CancellationToken cancellationToken) + { + return await _blogService.GetAllAsync(); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsQuery.cs b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsQuery.cs new file mode 100644 index 0000000..a812a0d --- /dev/null +++ b/SampleWebApp.Core/Handlers/Blogs/Queries/GetBlogsQuery.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Blogs.Queries +{ + public class GetBlogsQuery : BaseQuery> + { + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostCommand.cs b/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostCommand.cs new file mode 100644 index 0000000..6376acc --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class CreatePostCommand : BaseCommand + { + public PostDto Post { get; set; } + + public CreatePostCommand() { } + + public CreatePostCommand(PostDto post) + { + Post = post; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostHandler.cs b/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostHandler.cs new file mode 100644 index 0000000..9bd15c7 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/CreatePostHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class CreatePostHandler : IRequestHandler + { + private readonly IPostService _postService; + + public CreatePostHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(CreatePostCommand request, CancellationToken cancellationToken) + { + return await _postService.CreateAsync(request.Post); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostCommand.cs b/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostCommand.cs new file mode 100644 index 0000000..a74d04b --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostCommand.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class DeletePostCommand : BaseCommand + { + public int Id { get; set; } + + public DeletePostCommand() { } + + public DeletePostCommand(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostHandler.cs b/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostHandler.cs new file mode 100644 index 0000000..d6ffa51 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/DeletePostHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class DeletePostHandler : IRequestHandler + { + private readonly IPostService _postService; + + public DeletePostHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(DeletePostCommand request, CancellationToken cancellationToken) + { + return await _postService.DeleteAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostCommand.cs b/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostCommand.cs new file mode 100644 index 0000000..d0833f6 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class UpdatePostCommand : BaseCommand + { + public PostDto Post { get; set; } + + public UpdatePostCommand() { } + + public UpdatePostCommand(PostDto post) + { + Post = post; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostHandler.cs b/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostHandler.cs new file mode 100644 index 0000000..543bd38 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Commands/UpdatePostHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Commands +{ + public class UpdatePostHandler : IRequestHandler + { + private readonly IPostService _postService; + + public UpdatePostHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(UpdatePostCommand request, CancellationToken cancellationToken) + { + return await _postService.UpdateAsync(request.Post); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdHandler.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdHandler.cs new file mode 100644 index 0000000..fee3c62 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostByIdHandler : IRequestHandler + { + private readonly IPostService _postService; + + public GetPostByIdHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(GetPostByIdQuery request, CancellationToken cancellationToken) + { + return await _postService.GetByIdAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdQuery.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdQuery.cs new file mode 100644 index 0000000..06c1ef8 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostByIdQuery.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostByIdQuery : BaseQuery + { + public int Id { get; set; } + + public GetPostByIdQuery() { } + + public GetPostByIdQuery(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateHandler.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateHandler.cs new file mode 100644 index 0000000..b1e34f7 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostForCreateHandler : IRequestHandler + { + private readonly IPostService _postService; + + public GetPostForCreateHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(GetPostForCreateQuery request, CancellationToken cancellationToken) + { + return await _postService.GetCreateDtoAsync(); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateQuery.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateQuery.cs new file mode 100644 index 0000000..7866676 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForCreateQuery.cs @@ -0,0 +1,9 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostForCreateQuery : BaseQuery + { + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditHandler.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditHandler.cs new file mode 100644 index 0000000..de5d658 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostForEditHandler : IRequestHandler + { + private readonly IPostService _postService; + + public GetPostForEditHandler(IPostService postService) + { + _postService = postService; + } + + public async Task Handle(GetPostForEditQuery request, CancellationToken cancellationToken) + { + return await _postService.GetUpdateDtoAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditQuery.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditQuery.cs new file mode 100644 index 0000000..fb565ec --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostForEditQuery.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostForEditQuery : BaseQuery + { + public int Id { get; set; } + + public GetPostForEditQuery() { } + + public GetPostForEditQuery(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsHandler.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsHandler.cs new file mode 100644 index 0000000..f6e3c0c --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsHandler.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostsHandler : IRequestHandler> + { + private readonly IPostService _postService; + + public GetPostsHandler(IPostService postService) + { + _postService = postService; + } + + public async Task> Handle(GetPostsQuery request, CancellationToken cancellationToken) + { + if (request.BlogId.HasValue) + { + return await _postService.GetSimpleByBlogIdAsync(request.BlogId.Value); + } + + return await _postService.GetAllSimpleAsync(); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsQuery.cs b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsQuery.cs new file mode 100644 index 0000000..2fb76dc --- /dev/null +++ b/SampleWebApp.Core/Handlers/Posts/Queries/GetPostsQuery.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Posts.Queries +{ + public class GetPostsQuery : BaseQuery> + { + public int? BlogId { get; set; } + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagCommand.cs b/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagCommand.cs new file mode 100644 index 0000000..acac01c --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class CreateTagCommand : BaseCommand + { + public TagDto Tag { get; set; } + + public CreateTagCommand() { } + + public CreateTagCommand(TagDto tag) + { + Tag = tag; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagHandler.cs b/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagHandler.cs new file mode 100644 index 0000000..6c5b674 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/CreateTagHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class CreateTagHandler : IRequestHandler + { + private readonly ITagService _tagService; + + public CreateTagHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task Handle(CreateTagCommand request, CancellationToken cancellationToken) + { + return await _tagService.CreateAsync(request.Tag); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagCommand.cs b/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagCommand.cs new file mode 100644 index 0000000..5fcd387 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagCommand.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class DeleteTagCommand : BaseCommand + { + public int Id { get; set; } + + public DeleteTagCommand() { } + + public DeleteTagCommand(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagHandler.cs b/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagHandler.cs new file mode 100644 index 0000000..6cef5a4 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/DeleteTagHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class DeleteTagHandler : IRequestHandler + { + private readonly ITagService _tagService; + + public DeleteTagHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task Handle(DeleteTagCommand request, CancellationToken cancellationToken) + { + return await _tagService.DeleteAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagCommand.cs b/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagCommand.cs new file mode 100644 index 0000000..867579e --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagCommand.cs @@ -0,0 +1,18 @@ +using SampleWebApp.Core.Common.Commands; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class UpdateTagCommand : BaseCommand + { + public TagDto Tag { get; set; } + + public UpdateTagCommand() { } + + public UpdateTagCommand(TagDto tag) + { + Tag = tag; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagHandler.cs b/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagHandler.cs new file mode 100644 index 0000000..86e1ed8 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Commands/UpdateTagHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Commands +{ + public class UpdateTagHandler : IRequestHandler + { + private readonly ITagService _tagService; + + public UpdateTagHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task Handle(UpdateTagCommand request, CancellationToken cancellationToken) + { + return await _tagService.UpdateAsync(request.Tag); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdHandler.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdHandler.cs new file mode 100644 index 0000000..ac7719d --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagByIdHandler : IRequestHandler + { + private readonly ITagService _tagService; + + public GetTagByIdHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task Handle(GetTagByIdQuery request, CancellationToken cancellationToken) + { + return await _tagService.GetByIdAsync(request.Id); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdQuery.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdQuery.cs new file mode 100644 index 0000000..a936918 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagByIdQuery.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagByIdQuery : BaseQuery + { + public int Id { get; set; } + + public GetTagByIdQuery() { } + + public GetTagByIdQuery(int id) + { + Id = id; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugHandler.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugHandler.cs new file mode 100644 index 0000000..277f378 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagBySlugHandler : IRequestHandler + { + private readonly ITagService _tagService; + + public GetTagBySlugHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task Handle(GetTagBySlugQuery request, CancellationToken cancellationToken) + { + return await _tagService.GetBySlugAsync(request.Slug); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugQuery.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugQuery.cs new file mode 100644 index 0000000..f0093ce --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagBySlugQuery.cs @@ -0,0 +1,17 @@ +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagBySlugQuery : BaseQuery + { + public string Slug { get; set; } + + public GetTagBySlugQuery() { } + + public GetTagBySlugQuery(string slug) + { + Slug = slug; + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsHandler.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsHandler.cs new file mode 100644 index 0000000..777e9c7 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsHandler.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using SampleWebApp.Core.DTOs; +using SampleWebApp.Core.Services; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagsHandler : IRequestHandler> + { + private readonly ITagService _tagService; + + public GetTagsHandler(ITagService tagService) + { + _tagService = tagService; + } + + public async Task> Handle(GetTagsQuery request, CancellationToken cancellationToken) + { + return await _tagService.GetAllAsync(); + } + } +} diff --git a/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsQuery.cs b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsQuery.cs new file mode 100644 index 0000000..f7b1b53 --- /dev/null +++ b/SampleWebApp.Core/Handlers/Tags/Queries/GetTagsQuery.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using SampleWebApp.Core.Common.Queries; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Handlers.Tags.Queries +{ + public class GetTagsQuery : BaseQuery> + { + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + } +} diff --git a/SampleWebApp.Core/Mapping/MappingProfile.cs b/SampleWebApp.Core/Mapping/MappingProfile.cs new file mode 100644 index 0000000..3da347e --- /dev/null +++ b/SampleWebApp.Core/Mapping/MappingProfile.cs @@ -0,0 +1,33 @@ +using AutoMapper; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Mapping +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + } + } + + public class PostMappingProfile : Profile + { + public PostMappingProfile() + { + } + } + + public class TagMappingProfile : Profile + { + public TagMappingProfile() + { + } + } + + public class BlogMappingProfile : Profile + { + public BlogMappingProfile() + { + } + } +} diff --git a/SampleWebApp.Core/SampleWebApp.Core.csproj b/SampleWebApp.Core/SampleWebApp.Core.csproj new file mode 100644 index 0000000..d464dd5 --- /dev/null +++ b/SampleWebApp.Core/SampleWebApp.Core.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + 8.0 + SampleWebApp.Core + SampleWebApp.Core + + + + + + + + + + + + + diff --git a/SampleWebApp.Core/Services/BlogService.cs b/SampleWebApp.Core/Services/BlogService.cs new file mode 100644 index 0000000..a45f716 --- /dev/null +++ b/SampleWebApp.Core/Services/BlogService.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AutoMapper; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public class BlogService : IBlogService + { + private readonly IMapper _mapper; + private readonly IBlogRepository _repository; + + public BlogService(IMapper mapper, IBlogRepository repository) + { + _mapper = mapper; + _repository = repository; + } + + public async Task> GetAllAsync() + { + var blogs = await _repository.GetAllBlogsAsync(); + return _mapper.Map>(blogs); + } + + public async Task> GetFilteredAsync(Expression> filter) + { + var blogs = await _repository.GetAllBlogsAsync(); + var dtos = _mapper.Map>(blogs); + return dtos.AsQueryable().Where(filter); + } + + public async Task> GetPagedAsync(int page, int pageSize) + { + var (blogs, totalCount) = await _repository.GetPagedBlogsAsync(page, pageSize); + var dtos = _mapper.Map>(blogs); + return new PagedResult(dtos, totalCount, page, pageSize); + } + + public async Task GetByIdAsync(int id) + { + var blog = await _repository.GetBlogByIdAsync(id); + return _mapper.Map(blog); + } + + public Task GetBySlugAsync(string slug) + { + throw new NotImplementedException("Blogs do not have slugs"); + } + + public async Task CreateAsync(BlogDto dto) + { + try + { + var id = await _repository.CreateBlogAsync(dto); + return CreateResult.Success($"Blog '{dto.Name}' created successfully", id); + } + catch (Exception ex) + { + return CreateResult.Failure(ex.Message); + } + } + + public async Task GetCreateDtoAsync() + { + return await Task.FromResult(new BlogDto()); + } + + public async Task UpdateAsync(BlogDto dto) + { + try + { + await _repository.UpdateBlogAsync(dto); + return UpdateResult.Success($"Blog '{dto.Name}' updated successfully"); + } + catch (Exception ex) + { + return UpdateResult.Failure(ex.Message); + } + } + + public async Task GetUpdateDtoAsync(int id) + { + return await GetByIdAsync(id); + } + + public async Task ResetDtoAsync(BlogDto dto) + { + return await Task.FromResult(dto); + } + + public async Task DeleteAsync(int id) + { + try + { + await _repository.DeleteBlogAsync(id); + return DeleteResult.Success("Blog deleted successfully"); + } + catch (Exception ex) + { + return DeleteResult.Failure(ex.Message); + } + } + } + + public interface IBlogRepository + { + Task> GetAllBlogsAsync(); + Task<(IEnumerable blogs, int totalCount)> GetPagedBlogsAsync(int page, int pageSize); + Task GetBlogByIdAsync(int id); + Task CreateBlogAsync(BlogDto dto); + Task UpdateBlogAsync(BlogDto dto); + Task DeleteBlogAsync(int id); + } +} diff --git a/SampleWebApp.Core/Services/IBlogService.cs b/SampleWebApp.Core/Services/IBlogService.cs new file mode 100644 index 0000000..115891f --- /dev/null +++ b/SampleWebApp.Core/Services/IBlogService.cs @@ -0,0 +1,13 @@ +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public interface IBlogService : + IListService, + IDetailService, + ICreateService, + IUpdateService, + IDeleteService + { + } +} diff --git a/SampleWebApp.Core/Services/ICreateService.cs b/SampleWebApp.Core/Services/ICreateService.cs new file mode 100644 index 0000000..a833056 --- /dev/null +++ b/SampleWebApp.Core/Services/ICreateService.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using SampleWebApp.Core.Common; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Services +{ + public interface ICreateService where TDto : BaseDto + { + Task CreateAsync(TDto dto); + Task GetCreateDtoAsync(); + } +} diff --git a/SampleWebApp.Core/Services/IDeleteService.cs b/SampleWebApp.Core/Services/IDeleteService.cs new file mode 100644 index 0000000..e822c6c --- /dev/null +++ b/SampleWebApp.Core/Services/IDeleteService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using SampleWebApp.Core.Common; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Services +{ + public interface IDeleteService where TDto : BaseDto + { + Task DeleteAsync(int id); + } +} diff --git a/SampleWebApp.Core/Services/IDetailService.cs b/SampleWebApp.Core/Services/IDetailService.cs new file mode 100644 index 0000000..2123460 --- /dev/null +++ b/SampleWebApp.Core/Services/IDetailService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using SampleWebApp.Core.Common; + +namespace SampleWebApp.Core.Services +{ + public interface IDetailService where TDto : BaseDto + { + Task GetByIdAsync(int id); + Task GetBySlugAsync(string slug); + } +} diff --git a/SampleWebApp.Core/Services/IListService.cs b/SampleWebApp.Core/Services/IListService.cs new file mode 100644 index 0000000..8241e0f --- /dev/null +++ b/SampleWebApp.Core/Services/IListService.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using SampleWebApp.Core.Common; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Services +{ + public interface IListService where TDto : BaseDto + { + Task> GetAllAsync(); + Task> GetFilteredAsync(Expression> filter); + Task> GetPagedAsync(int page, int pageSize); + } +} diff --git a/SampleWebApp.Core/Services/IPostService.cs b/SampleWebApp.Core/Services/IPostService.cs new file mode 100644 index 0000000..6c28b08 --- /dev/null +++ b/SampleWebApp.Core/Services/IPostService.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public interface IPostService : + IListService, + IDetailService, + ICreateService, + IUpdateService, + IDeleteService + { + Task> GetByBlogIdAsync(int blogId); + Task> GetAllSimpleAsync(); + Task> GetSimpleByBlogIdAsync(int blogId); + } +} diff --git a/SampleWebApp.Core/Services/ITagService.cs b/SampleWebApp.Core/Services/ITagService.cs new file mode 100644 index 0000000..9f6f5d0 --- /dev/null +++ b/SampleWebApp.Core/Services/ITagService.cs @@ -0,0 +1,13 @@ +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public interface ITagService : + IListService, + IDetailService, + ICreateService, + IUpdateService, + IDeleteService + { + } +} diff --git a/SampleWebApp.Core/Services/IUpdateService.cs b/SampleWebApp.Core/Services/IUpdateService.cs new file mode 100644 index 0000000..92ab331 --- /dev/null +++ b/SampleWebApp.Core/Services/IUpdateService.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using SampleWebApp.Core.Common; +using SampleWebApp.Core.Common.Results; + +namespace SampleWebApp.Core.Services +{ + public interface IUpdateService where TDto : BaseDto + { + Task UpdateAsync(TDto dto); + Task GetUpdateDtoAsync(int id); + Task ResetDtoAsync(TDto dto); + } +} diff --git a/SampleWebApp.Core/Services/PostService.cs b/SampleWebApp.Core/Services/PostService.cs new file mode 100644 index 0000000..d67dcdb --- /dev/null +++ b/SampleWebApp.Core/Services/PostService.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AutoMapper; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public class PostService : IPostService + { + private readonly IMapper _mapper; + private readonly IPostRepository _repository; + + public PostService(IMapper mapper, IPostRepository repository) + { + _mapper = mapper; + _repository = repository; + } + + public async Task> GetAllAsync() + { + var posts = await _repository.GetAllPostsAsync(); + return _mapper.Map>(posts); + } + + public async Task> GetFilteredAsync(Expression> filter) + { + var posts = await _repository.GetAllPostsAsync(); + var dtos = _mapper.Map>(posts); + return dtos.AsQueryable().Where(filter); + } + + public async Task> GetPagedAsync(int page, int pageSize) + { + var (posts, totalCount) = await _repository.GetPagedPostsAsync(page, pageSize); + var dtos = _mapper.Map>(posts); + return new PagedResult(dtos, totalCount, page, pageSize); + } + + public async Task GetByIdAsync(int id) + { + var post = await _repository.GetPostByIdAsync(id); + return _mapper.Map(post); + } + + public Task GetBySlugAsync(string slug) + { + throw new NotImplementedException("Posts do not have slugs"); + } + + public async Task CreateAsync(PostDto dto) + { + try + { + var id = await _repository.CreatePostAsync(dto); + return CreateResult.Success($"Post '{dto.Title}' created successfully", id); + } + catch (Exception ex) + { + return CreateResult.Failure(ex.Message); + } + } + + public async Task GetCreateDtoAsync() + { + var dto = new PostDto(); + await SetupSecondaryDataAsync(dto); + return dto; + } + + public async Task UpdateAsync(PostDto dto) + { + try + { + await _repository.UpdatePostAsync(dto); + return UpdateResult.Success($"Post '{dto.Title}' updated successfully"); + } + catch (Exception ex) + { + return UpdateResult.Failure(ex.Message); + } + } + + public async Task GetUpdateDtoAsync(int id) + { + var dto = await GetByIdAsync(id); + if (dto != null) + { + await SetupSecondaryDataAsync(dto); + } + return dto; + } + + public async Task ResetDtoAsync(PostDto dto) + { + await SetupSecondaryDataAsync(dto); + return dto; + } + + public async Task DeleteAsync(int id) + { + try + { + await _repository.DeletePostAsync(id); + return DeleteResult.Success("Post deleted successfully"); + } + catch (Exception ex) + { + return DeleteResult.Failure(ex.Message); + } + } + + public async Task> GetByBlogIdAsync(int blogId) + { + var posts = await _repository.GetPostsByBlogIdAsync(blogId); + return _mapper.Map>(posts); + } + + public async Task> GetAllSimpleAsync() + { + var posts = await _repository.GetAllPostsAsync(); + return _mapper.Map>(posts); + } + + public async Task> GetSimpleByBlogIdAsync(int blogId) + { + var posts = await _repository.GetPostsByBlogIdAsync(blogId); + return _mapper.Map>(posts); + } + + private async Task SetupSecondaryDataAsync(PostDto dto) + { + var blogs = await _repository.GetAllBlogsAsync(); + var tags = await _repository.GetAllTagsAsync(); + + dto.Bloggers.SetupDropDownListContent( + blogs.Select(x => new KeyValuePair(x.Name, x.BlogId.ToString("D"))), + "--- choose blogger ---"); + + if (dto.PostId != 0) + { + dto.Bloggers.SetSelectedValue(dto.BlogId.ToString("D")); + } + + var preselectedTags = dto.PostId == 0 + ? new List>() + : dto.Tags?.Select(x => new KeyValuePair(x.Name, x.TagId)).ToList() + ?? new List>(); + + dto.UserChosenTags.SetupMultiSelectList( + tags.Select(x => new KeyValuePair(x.Name, x.TagId)), + preselectedTags); + } + } + + public interface IPostRepository + { + Task> GetAllPostsAsync(); + Task<(IEnumerable posts, int totalCount)> GetPagedPostsAsync(int page, int pageSize); + Task GetPostByIdAsync(int id); + Task> GetPostsByBlogIdAsync(int blogId); + Task CreatePostAsync(PostDto dto); + Task UpdatePostAsync(PostDto dto); + Task DeletePostAsync(int id); + Task> GetAllBlogsAsync(); + Task> GetAllTagsAsync(); + } +} diff --git a/SampleWebApp.Core/Services/TagService.cs b/SampleWebApp.Core/Services/TagService.cs new file mode 100644 index 0000000..be174d7 --- /dev/null +++ b/SampleWebApp.Core/Services/TagService.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AutoMapper; +using SampleWebApp.Core.Common.Results; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Services +{ + public class TagService : ITagService + { + private readonly IMapper _mapper; + private readonly ITagRepository _repository; + + public TagService(IMapper mapper, ITagRepository repository) + { + _mapper = mapper; + _repository = repository; + } + + public async Task> GetAllAsync() + { + var tags = await _repository.GetAllTagsAsync(); + return _mapper.Map>(tags); + } + + public async Task> GetFilteredAsync(Expression> filter) + { + var tags = await _repository.GetAllTagsAsync(); + var dtos = _mapper.Map>(tags); + return dtos.AsQueryable().Where(filter); + } + + public async Task> GetPagedAsync(int page, int pageSize) + { + var (tags, totalCount) = await _repository.GetPagedTagsAsync(page, pageSize); + var dtos = _mapper.Map>(tags); + return new PagedResult(dtos, totalCount, page, pageSize); + } + + public async Task GetByIdAsync(int id) + { + var tag = await _repository.GetTagByIdAsync(id); + return _mapper.Map(tag); + } + + public async Task GetBySlugAsync(string slug) + { + var tag = await _repository.GetTagBySlugAsync(slug); + return _mapper.Map(tag); + } + + public async Task CreateAsync(TagDto dto) + { + try + { + var id = await _repository.CreateTagAsync(dto); + return CreateResult.Success($"Tag '{dto.Name}' created successfully", id); + } + catch (Exception ex) + { + return CreateResult.Failure(ex.Message); + } + } + + public async Task GetCreateDtoAsync() + { + return await Task.FromResult(new TagDto()); + } + + public async Task UpdateAsync(TagDto dto) + { + try + { + await _repository.UpdateTagAsync(dto); + return UpdateResult.Success($"Tag '{dto.Name}' updated successfully"); + } + catch (Exception ex) + { + return UpdateResult.Failure(ex.Message); + } + } + + public async Task GetUpdateDtoAsync(int id) + { + return await GetByIdAsync(id); + } + + public async Task ResetDtoAsync(TagDto dto) + { + return await Task.FromResult(dto); + } + + public async Task DeleteAsync(int id) + { + try + { + await _repository.DeleteTagAsync(id); + return DeleteResult.Success("Tag deleted successfully"); + } + catch (Exception ex) + { + return DeleteResult.Failure(ex.Message); + } + } + } + + public interface ITagRepository + { + Task> GetAllTagsAsync(); + Task<(IEnumerable tags, int totalCount)> GetPagedTagsAsync(int page, int pageSize); + Task GetTagByIdAsync(int id); + Task GetTagBySlugAsync(string slug); + Task CreateTagAsync(TagDto dto); + Task UpdateTagAsync(TagDto dto); + Task DeleteTagAsync(int id); + } +} diff --git a/SampleWebApp.Core/UiClasses/DropDownListType.cs b/SampleWebApp.Core/UiClasses/DropDownListType.cs new file mode 100644 index 0000000..b422576 --- /dev/null +++ b/SampleWebApp.Core/UiClasses/DropDownListType.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SampleWebApp.Core.UiClasses +{ + public class DropDownListType + { + public List> KeyValueList { get; private set; } + + public string SelectedValue { get; set; } + + public int? SelectedValueAsInt + { + get + { + if (int.TryParse(SelectedValue, out int result)) + return result; + return null; + } + } + + public void SetupDropDownListContent(IEnumerable> keyValueList, string promptString) + { + KeyValueList = keyValueList.ToList(); + if (promptString != null) + KeyValueList.Insert(0, new KeyValuePair(promptString, null)); + else if (KeyValueList.Any()) + SelectedValue = KeyValueList[0].Value; + } + + public void SetSelectedValue(string valueAsString) + { + var foundEntry = KeyValueList.FirstOrDefault(x => x.Value == valueAsString); + SelectedValue = KeyValueList.Any(x => x.Value == valueAsString) + ? KeyValueList.First(x => x.Value == valueAsString).Value + : "--- select from list ---"; + } + + public override string ToString() + { + if (SelectedValue != null) + return string.Format("Selected Value = {0}", SelectedValue); + + if (KeyValueList == null) + return "KeyValue list has not been set up yet."; + + return string.Format("{0} items in list, but no selected value", KeyValueList.Count); + } + } +} diff --git a/SampleWebApp.Core/UiClasses/MultiSelectListType.cs b/SampleWebApp.Core/UiClasses/MultiSelectListType.cs new file mode 100644 index 0000000..57c5236 --- /dev/null +++ b/SampleWebApp.Core/UiClasses/MultiSelectListType.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SampleWebApp.Core.UiClasses +{ + public class MultiSelectListType + { + public List> AllPossibleOptions { get; private set; } + + public List> InitialSelection { get; private set; } + + public string[] FinalSelection { get; set; } + + public void SetupMultiSelectList(IEnumerable> allPossibleOptions, + IEnumerable> initialSelectionValues) + { + AllPossibleOptions = allPossibleOptions.ToList(); + InitialSelection = initialSelectionValues.ToList(); + FinalSelection = InitialSelection.Select(x => x.Value.ToString("D")).ToArray(); + } + + public int[] GetFinalSelectionAsInts() + { + var result = new List(); + if (FinalSelection == null) + return result.ToArray(); + + foreach (var intAsString in FinalSelection) + { + if (!int.TryParse(intAsString, out int id)) + throw new ArgumentException("One of the FinalSelection answers was not an integer"); + + result.Add(id); + } + return result.ToArray(); + } + } +} diff --git a/SampleWebApp.Core/Validators/BlogValidator.cs b/SampleWebApp.Core/Validators/BlogValidator.cs new file mode 100644 index 0000000..f89ee87 --- /dev/null +++ b/SampleWebApp.Core/Validators/BlogValidator.cs @@ -0,0 +1,27 @@ +using FluentValidation; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Validators +{ + public class BlogValidator : AbstractValidator + { + public BlogValidator() + { + RuleFor(b => b.Name) + .NotEmpty() + .WithMessage("Name is required") + .MinimumLength(2) + .WithMessage("Name must be at least 2 characters") + .MaximumLength(64) + .WithMessage("Name cannot exceed 64 characters"); + + RuleFor(b => b.EmailAddress) + .NotEmpty() + .WithMessage("Email address is required") + .MaximumLength(256) + .WithMessage("Email address cannot exceed 256 characters") + .EmailAddress() + .WithMessage("Please enter a valid email address"); + } + } +} diff --git a/SampleWebApp.Core/Validators/PostValidator.cs b/SampleWebApp.Core/Validators/PostValidator.cs new file mode 100644 index 0000000..18ef279 --- /dev/null +++ b/SampleWebApp.Core/Validators/PostValidator.cs @@ -0,0 +1,45 @@ +using FluentValidation; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Validators +{ + public class PostValidator : AbstractValidator + { + public PostValidator() + { + RuleFor(p => p.Title) + .NotEmpty() + .WithMessage("Title is required") + .MinimumLength(2) + .WithMessage("Title must be at least 2 characters") + .MaximumLength(128) + .WithMessage("Title cannot exceed 128 characters") + .Must(title => title == null || !title.Contains("!")) + .WithMessage("Sorry, but you can't get too excited and include a ! in the title.") + .Must(title => title == null || !title.EndsWith("?")) + .WithMessage("Sorry, but you can't ask a question, i.e. the title can't end with '?'."); + + RuleFor(p => p.Content) + .NotEmpty() + .WithMessage("Content is required") + .Must(content => content == null || !content.Contains(" sheep.")) + .WithMessage("Sorry. Not allowed to end a sentence with 'sheep'.") + .Must(content => content == null || !content.Contains(" lamb.")) + .WithMessage("Sorry. Not allowed to end a sentence with 'lamb'.") + .Must(content => content == null || !content.Contains(" cow.")) + .WithMessage("Sorry. Not allowed to end a sentence with 'cow'.") + .Must(content => content == null || !content.Contains(" calf.")) + .WithMessage("Sorry. Not allowed to end a sentence with 'calf'."); + + RuleFor(p => p.BlogId) + .GreaterThan(0) + .WithMessage("Please select a valid blog"); + + RuleFor(p => p.Tags) + .NotNull() + .WithMessage("Tags collection cannot be null") + .Must(tags => tags != null && tags.Count > 0) + .WithMessage("The post must have at least one tag."); + } + } +} diff --git a/SampleWebApp.Core/Validators/TagValidator.cs b/SampleWebApp.Core/Validators/TagValidator.cs new file mode 100644 index 0000000..97011e5 --- /dev/null +++ b/SampleWebApp.Core/Validators/TagValidator.cs @@ -0,0 +1,25 @@ +using FluentValidation; +using SampleWebApp.Core.DTOs; + +namespace SampleWebApp.Core.Validators +{ + public class TagValidator : AbstractValidator + { + public TagValidator() + { + RuleFor(t => t.Slug) + .NotEmpty() + .WithMessage("Slug is required") + .MaximumLength(64) + .WithMessage("Slug cannot exceed 64 characters") + .Matches(@"^\w*$") + .WithMessage("The slug must not contain spaces or non-alphanumeric characters."); + + RuleFor(t => t.Name) + .NotEmpty() + .WithMessage("Name is required") + .MaximumLength(128) + .WithMessage("Name cannot exceed 128 characters"); + } + } +} diff --git a/SampleWebApp.sln b/SampleWebApp.sln index b474189..4d96402 100644 --- a/SampleWebApp.sln +++ b/SampleWebApp.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLayer", "ServiceLaye EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{6D9E7904-B2AC-49E3-83A7-6B48876F46B9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApp.Core", "SampleWebApp.Core\SampleWebApp.Core.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AF8764F7-FBEE-48AD-AF62-23010DA35D70}" ProjectSection(SolutionItems) = preProject Licence.txt = Licence.txt @@ -56,6 +58,14 @@ Global {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|Any CPU.Build.0 = Release|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.WebWizRelease|Any CPU.ActiveCfg = WebWizRelease|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.AzureRelease|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.AzureRelease|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.WebWizRelease|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.WebWizRelease|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE