added BlazorAppObjectMappingwithMapster

This commit is contained in:
M. Akif Tokatlioglu
2024-01-27 22:09:50 +03:00
parent f13d05d210
commit a7223c7721
31 changed files with 1186 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34408.163
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorAppObjectMappingwithMapster", "BlazorAppObjectMappingwithMapster\BlazorAppObjectMappingwithMapster.csproj", "{EF76FB8E-AE8C-4D71-9503-83111FC1CC61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EF76FB8E-AE8C-4D71-9503-83111FC1CC61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF76FB8E-AE8C-4D71-9503-83111FC1CC61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF76FB8E-AE8C-4D71-9503-83111FC1CC61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF76FB8E-AE8C-4D71-9503-83111FC1CC61}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8CBC3D86-3BD9-4193-B824-DEED2EEB8D6C}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mapster" Version="7.4.1-pre01" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="BlazorAppObjectMappingwithMapster.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>

View File

@@ -0,0 +1,23 @@
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

View File

@@ -0,0 +1,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@@ -0,0 +1,24 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">BlazorAppObjectMappingwithMapster</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="BlogPost">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> BlogPost
</NavLink>
</div>
</nav>
</div>

View File

@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -0,0 +1,69 @@
@page "/BlogPost/Create"
<PageTitle>Create</PageTitle>
<h1>Create</h1>
<h4>BlogPost</h4>
<hr />
@if (blogPost == null)
{
<p><em>Loading...</em></p>
}
else
{
<div class="row">
<div class="col-md-4">
<EditForm Model="@blogPost" OnValidSubmit="@HandleValidSubmit" Context="createBlogPost">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label class="control-label">@nameof(BlogPostViewModel.Title)</label>
<InputText @bind-Value="blogPost.Title" class="form-control" />
<ValidationMessage For="@(() => blogPost.Title)" class="text-danger" />
</div>
<div class="form-group">
<label class="control-label">@nameof(BlogPostViewModel.Content)</label>
<InputText @bind-Value="blogPost.Content" class="form-control" />
<ValidationMessage For="@(() => blogPost.Content)" class="text-danger" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</EditForm>
</div>
</div>
<div>
<a href="/BlogPost">Back to List</a>
</div>
}
@code {
private BlogPostViewModel? blogPost;
protected override void OnInitialized()
{
blogPost = new();
}
private async void HandleValidSubmit()
{
if (blogPost is null) return;
// option 1
// var model = blogPost.ToEntity();
// option 2
var model = Mapper.Map<BlogPostViewModel, BlogPost>(blogPost);
bool result = await BlogPostService.AddBlogPostAsync(model);
if (result)
NavigationManager.NavigateTo("/BlogPost");
}
}

View File

@@ -0,0 +1,74 @@
@page "/BlogPost/Delete/{id:int}"
<PageTitle>Delete</PageTitle>
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
@if (blogPost == null)
{
<p><em>Loading...</em></p>
}
else
{
<div>
<h4>BlogPost</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@nameof(BlogPost.Id)
</dt>
<dd class="col-sm-10">
@blogPost.Id
</dd>
<dt class="col-sm-2">
@nameof(BlogPost.Title)
</dt>
<dd class="col-sm-10">
@blogPost.Title
</dd>
<dt class="col-sm-2">
@nameof(BlogPost.Content)
</dt>
<dd class="col-sm-10">
@blogPost.Content
</dd>
</dl>
</div>
<div>
<button class="btn btn-danger" @onclick="DeleteButtonClick">Delete</button> |
<a href="/BlogPost">Back to List</a>
</div>
}
@code {
[Parameter]
public int id { get; set; }
private BlogPostViewModel? blogPost;
protected override async Task OnInitializedAsync()
{
if (blogPost is null)
{
var result = await BlogPostService.GetbyId(id);
if (result != null)
{
// option 1
// blogPost = BlogPostViewModel.FromEntity(result);
// option 2
blogPost = Mapper.Map<BlogPost, BlogPostViewModel>(result);
}
}
}
private async void DeleteButtonClick()
{
bool result = await BlogPostService.DeletebyIdAsync(id);
if (result)
NavigationManager.NavigateTo("/BlogPost");
}
}

View File

@@ -0,0 +1,63 @@
@page "/BlogPost/Details/{id:int}"
<PageTitle>Details</PageTitle>
<h1>Details</h1>
@if (blogPost == null)
{
<p><em>Loading...</em></p>
}
else
{
<div>
<h4>BlogPost</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@nameof(BlogPost.Id)
</dt>
<dd class="col-sm-10">
@blogPost.Id
</dd>
<dt class="col-sm-2">
@nameof(BlogPost.Title)
</dt>
<dd class="col-sm-10">
@blogPost.Title
</dd>
<dt class="col-sm-2">
@nameof(BlogPost.Content)
</dt>
<dd class="col-sm-10">
@blogPost.Content
</dd>
</dl>
</div>
<div>
<a href="/BlogPost/Edit/@blogPost.Id">Edit</a> |
<a href="/BlogPost">Back to List</a>
</div>
}
@code {
[Parameter]
public int id { get; set; }
private BlogPostViewModel? blogPost;
protected override async Task OnInitializedAsync()
{
if (blogPost is null)
{
var result = await BlogPostService.GetbyId(id);
if (result != null)
{
// option 1
// blogPost = BlogPostViewModel.FromEntity(result);
// option 2
blogPost = Mapper.Map<BlogPost, BlogPostViewModel>(result);
}
}
}
}

View File

@@ -0,0 +1,83 @@
@page "/BlogPost/Edit/{id:int}"
<PageTitle>Edit</PageTitle>
<h1>Edit</h1>
<h4>BlogPost</h4>
<hr />
@if (blogPost == null)
{
<p><em>Loading...</em></p>
}
else
{
<div class="row">
<div class="col-md-4">
<EditForm Model="@blogPost" OnValidSubmit="@HandleValidSubmit" Context="editBlogPost">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label class="control-label">@nameof(BlogPostViewModel.Title)</label>
<InputText @bind-Value="blogPost.Title" class="form-control" />
<ValidationMessage For="@(() => blogPost.Title)" class="text-danger" />
</div>
<div class="form-group">
<label class="control-label">@nameof(BlogPostViewModel.Content)</label>
<InputText @bind-Value="blogPost.Content" class="form-control" />
<ValidationMessage For="@(() => blogPost.Content)" class="text-danger" />
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</EditForm>
</div>
</div>
<div>
<a href="/BlogPost">Back to List</a>
</div>
}
@code {
[Parameter]
public int id { get; set; }
private BlogPostViewModel? blogPost;
protected override async Task OnInitializedAsync()
{
if (blogPost is null)
{
var result = await BlogPostService.GetbyId(id);
if (result != null)
{
// option 1
// blogPost = BlogPostViewModel.FromEntity(result);
// option 2
blogPost = Mapper.Map<BlogPost, BlogPostViewModel>(result);
}
}
}
private async void HandleValidSubmit()
{
if (blogPost is null) return;
// option 1
// var model = blogPost.ToEntity();
// option 2
var model = Mapper.Map<BlogPostViewModel, BlogPost>(blogPost);
bool result = await BlogPostService.UpdateBlogPostAsync(id, model);
if (result)
NavigationManager.NavigateTo("/BlogPost");
}
}

View File

@@ -0,0 +1,56 @@
@page "/BlogPost"
<PageTitle>Index</PageTitle>
<h1>Index</h1>
<p>
<a href="/BlogPost/Create">Create New</a>
</p>
@if (blogPosts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>@nameof(BlogPostViewModel.Id)</th>
<th>@nameof(BlogPostViewModel.Title)</th>
<th>@nameof(BlogPostViewModel.Content)</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var blogpost in blogPosts)
{
<tr>
<td>@blogpost.Id</td>
<td>@blogpost.Title</td>
<td>@blogpost.Content</td>
<td>
<a href="/BlogPost/Details/@blogpost.Id">Details</a> |
<a href="/BlogPost/Edit/@blogpost.Id">Edit</a> |
<a href="/BlogPost/Delete/@blogpost.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
}
@code {
private IEnumerable<BlogPostViewModel>? blogPosts;
protected override async Task OnInitializedAsync()
{
if (blogPosts == null)
{
var result = await BlogPostService.GetAllAsync();
blogPosts = Mapper.Map<IEnumerable<BlogPost>, IEnumerable<BlogPostViewModel>>(result);
}
}
}

View File

@@ -0,0 +1,9 @@
@using MapsterMapper
@using BlazorAppObjectMappingwithMapster.Data;
@using BlazorAppObjectMappingwithMapster.Models;
@using BlazorAppObjectMappingwithMapster.Services;
@using BlazorAppObjectMappingwithMapster.ViewModels;
@inject IMapper Mapper
@inject NavigationManager NavigationManager
@inject BlogPostService BlogPostService

View File

@@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,7 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

View File

@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorAppObjectMappingwithMapster
@using BlazorAppObjectMappingwithMapster.Components

View File

@@ -0,0 +1,19 @@
using BlazorAppObjectMappingwithMapster.Models;
using Microsoft.EntityFrameworkCore;
namespace BlazorAppObjectMappingwithMapster.Data;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<BlogPost> BlogPosts => Set<BlogPost>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}

View File

@@ -0,0 +1,42 @@
using Mapster;
namespace BlazorAppObjectMappingwithMapster.Data;
public abstract class BaseDTO<TDTO, TEntity> : IRegister
where TDTO : class, new()
where TEntity : class, new()
{
public TEntity ToEntity()
{
return this.Adapt<TEntity>();
}
public TEntity ToEntity(TEntity entity)
{
return (this as TDTO).Adapt(entity);
}
public static TDTO FromEntity(TEntity entity)
{
return entity.Adapt<TDTO>();
}
private TypeAdapterConfig Config { get; set; }
public virtual void AddCustomMappings() { }
protected TypeAdapterSetter<TDTO, TEntity> SetCustomMappings()
=> Config.ForType<TDTO, TEntity>();
protected TypeAdapterSetter<TEntity, TDTO> SetCustomMappingsInverse()
=> Config.ForType<TEntity, TDTO>();
public void Register(TypeAdapterConfig config)
{
Config = config;
AddCustomMappings();
}
}

View File

@@ -0,0 +1,101 @@
using BlazorAppObjectMappingwithMapster.Models;
namespace BlazorAppObjectMappingwithMapster.Data;
public class SeedData
{
private readonly ApplicationDbContext _context;
public SeedData(ApplicationDbContext context)
{
_context = context;
}
public async Task CreateInitialData()
{
var posts = GetAllBlogPosts();
await _context.BlogPosts.AddRangeAsync(posts);
await _context.SaveChangesAsync();
}
private static IEnumerable<BlogPost> GetAllBlogPosts()
{
List<BlogPost> posts = new();
for (int i = 0; i < 50; i++)
{
BlogPost post = new() { Id = i + 1, Title = titles[i], Content = contents[i % 10] };
posts.Add(post);
}
return posts;
}
private static readonly string[] titles = {
"Introduction to Object-Oriented Programming",
"Mastering Data Structures and Algorithms",
"Building Web Applications with ASP.NET",
"Creating Mobile Apps with Xamarin",
"Exploring Artificial Intelligence and Machine Learning",
"Understanding Functional Programming Concepts",
"Developing Games with Unity",
"Securing Web Applications from Cyber Attacks",
"Optimizing Code Performance for Better Efficiency",
"Implementing Design Patterns in Software Development",
"Testing and Debugging Strategies for Reliable Software",
"Working with Databases and SQL",
"Building Responsive User Interfaces with HTML and CSS",
"Exploring Cloud Computing and Serverless Architecture",
"Developing Cross-Platform Applications with React Native",
"Introduction to Internet of Things (IoT)",
"Creating Scalable Microservices with Docker and Kubernetes",
"Understanding Network Protocols and TCP/IP",
"Building RESTful APIs with Node.js and Express",
"Exploring Big Data Analytics and Apache Hadoop",
"Mastering Version Control with Git and GitHub",
"Developing Desktop Applications with WPF",
"Securing Mobile Applications from Malicious Attacks",
"Optimizing Database Performance with Indexing",
"Implementing Continuous Integration and Deployment",
"Testing Mobile Apps on Different Platforms",
"Working with NoSQL Databases like MongoDB",
"Building Progressive Web Apps with React",
"Exploring Quantum Computing and Quantum Algorithms",
"Introduction to Cybersecurity and Ethical Hacking",
"Creating Chatbots with Natural Language Processing",
"Understanding Software Development Life Cycle",
"Developing Augmented Reality (AR) Applications",
"Securing Web APIs with OAuth and JWT",
"Optimizing Front-End Performance for Better User Experience",
"Implementing Machine Learning Models with TensorFlow",
"Testing Web Applications for Cross-Browser Compatibility",
"Working with Blockchain Technology and Smart Contracts",
"Building Real-Time Applications with SignalR",
"Exploring Cryptography and Encryption Techniques",
"Introduction to Agile Software Development",
"Creating Voice User Interfaces with Amazon Alexa",
"Understanding Web Accessibility and Inclusive Design",
"Developing Natural Language Processing Applications",
"Securing Cloud Infrastructure and Services",
"Optimizing Backend Performance for Scalability",
"Implementing Continuous Monitoring and Alerting",
"Testing APIs with Postman and Swagger",
"Working with Data Visualization Libraries like D3.js",
"Building E-commerce Applications with Shopify",
"Exploring Robotic Process Automation (RPA)",
"Introduction to DevOps and CI/CD Pipelines"
};
private static readonly string[] contents = new string[]
{
"Lorem ipsum dolor sit amet, consectetur t.",
"Sed ut perspiciatis unde omnis iste natuccusantium doloremque laudantium.",
"Nemo enim ipsam voluptatem quia voluptas aut fugit.",
"Quis autem vel eum iure reprehenderit quesse quam nihil molestiae consequatur.",
"At vero eos et accusamus et iusto odio d.",
"Similique sunt in culpa qui officia de.",
"Et harum quidem rerum facilis est et expio.",
"Nam libero tempore, cum soluta nobis est.",
"Omnis voluptas assumenda est, omnis dolo",
"Temporibus autem quibusdam et aut offic"
};
}

View File

@@ -0,0 +1,8 @@
namespace BlazorAppObjectMappingwithMapster.Models;
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,79 @@
using BlazorAppObjectMappingwithMapster.Components;
using BlazorAppObjectMappingwithMapster.Data;
using BlazorAppObjectMappingwithMapster.Services;
using Mapster;
using MapsterMapper;
using Microsoft.EntityFrameworkCore;
using System.Reflection;
internal class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("ConnectionInMemory")
);
builder.Services.AddScoped<SeedData>();
builder.Services.AddScoped<BlogPostService>();
builder.Services.AddMapster();
builder.Services.CreateDatabase().GetAwaiter().GetResult();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
}
}
public static class ServiceCollectionExtensions
{
public static async Task CreateDatabase(this IServiceCollection services)
{
using (IServiceScope tmp = services.BuildServiceProvider().CreateScope())
{
await using var _context = tmp.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var seedData = tmp.ServiceProvider.GetRequiredService<SeedData>();
await seedData.CreateInitialData();
}
}
}
public static class MapsterConfiguration
{
public static void AddMapster(this IServiceCollection services)
{
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
Assembly applicationAssembly = typeof(BaseDTO<,>).Assembly;
typeAdapterConfig.Scan(applicationAssembly);
var mapperConfig = new Mapper(typeAdapterConfig);
services.AddSingleton<IMapper>(mapperConfig);
}
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,82 @@
using BlazorAppObjectMappingwithMapster.Data;
using BlazorAppObjectMappingwithMapster.Models;
using Microsoft.EntityFrameworkCore;
namespace BlazorAppObjectMappingwithMapster.Services;
public class BlogPostService
{
private readonly ILogger<BlogPostService> _logger;
private readonly ApplicationDbContext _context;
public BlogPostService(ILogger<BlogPostService> logger, ApplicationDbContext context)
{
_context = context;
_logger = logger;
}
public Task<BlogPost?> GetbyId(int id)
{
_logger.LogInformation($"Called GetbyId: ", id);
return _context.BlogPosts.FirstOrDefaultAsync(x => x.Id == id);
}
public async Task<IEnumerable<BlogPost>> GetAllAsync()
{
_logger.LogInformation($"Called GetAllAsync.");
var query = _context.BlogPosts.AsQueryable();
IEnumerable<BlogPost>? result = await query.ToListAsync();
return result;
}
public async Task<bool> AddBlogPostAsync(BlogPost blogPost)
{
_logger.LogInformation($"Called AddBlogPostAsync ", blogPost);
try
{
await _context.BlogPosts.AddAsync(blogPost);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Called AddBlogPostAsync Error", blogPost);
return false;
}
return true;
}
public async Task<bool> UpdateBlogPostAsync(int id, BlogPost blogPost)
{
_logger.LogInformation($"Called UpdateBlogPostAsync ", blogPost);
try
{
var oldBlogPost = _context.BlogPosts.FirstOrDefault(x => x.Id == id);
if (oldBlogPost == null) return false;
oldBlogPost.Title = blogPost.Title;
oldBlogPost.Content = blogPost.Content;
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Called AddBlogPostAsync Error", blogPost);
return false;
}
return true;
}
public async Task<bool> DeletebyIdAsync(int id)
{
_logger.LogInformation($"Called DeletebyIdAsync ", id);
var blogPost = await _context.BlogPosts.FirstOrDefaultAsync(x => x.Id == id);
if (blogPost == null)
return false;
_context.BlogPosts.Remove(blogPost);
await _context.SaveChangesAsync();
return true;
}
}

View File

@@ -0,0 +1,35 @@
using BlazorAppObjectMappingwithMapster.Data;
using BlazorAppObjectMappingwithMapster.Models;
using System.ComponentModel.DataAnnotations;
namespace BlazorAppObjectMappingwithMapster.ViewModels;
public class BlogPostViewModel : BaseDTO<BlogPostViewModel, BlogPost>
{
public int Id { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Title can not be empty")]
public string Title { get; set; } = string.Empty;
[Required(AllowEmptyStrings = false, ErrorMessage = "Content can not be empty")]
public string Content { get; set; } = string.Empty;
public string TitleShort { get; set; } = string.Empty;
public string ContentShort { get; set; } = string.Empty;
public override void AddCustomMappings()
{
// from BlogPostViewModel to BlogPost
SetCustomMappings().Map(dst => dst.Title,
src => src.Title);
// from BlogPost to BlogPostViewModel
SetCustomMappingsInverse().Map(dst => dst.TitleShort,
src => src.Title.Substring(0, Math.Min(src.Title.Count(), 10)))
.Map(dst => dst.ContentShort,
src => src.Content.Substring(0, Math.Min(src.Content.Count(), 50)));
// base.AddCustomMappings();
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,51 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB