Skip to content

Commit f75922e

Browse files
committed
Domain to contract mapping with automapper
1 parent 8fd3049 commit f75922e

16 files changed

+340
-95
lines changed

.vs/ApiModel/v16/.suo

16.5 KB
Binary file not shown.

ApiModel.csproj

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,25 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9+
<PackageReference Include="AutoMapper" Version="10.1.1" />
10+
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
911
<PackageReference Include="Cosmonaut" Version="2.11.3" />
1012
<PackageReference Include="Cosmonaut.Extensions.Microsoft.DependencyInjection" Version="2.3.0" />
13+
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
1114
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
1215
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.4" />
1316
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.4" />
1417
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.4" />
1518
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.4" />
1619
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.4" />
20+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4">
21+
<!--<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.13">-->
22+
<PrivateAssets>all</PrivateAssets>
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
</PackageReference>
1725
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.4" />
1826
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.4" />
19-
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.8" />
27+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.6" />
2028
<PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
2129
<PackageReference Include="NSwag.Core" Version="13.10.8" />
2230
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.1" />

Contracts/V1/Requests/CreatePostRequest.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace ApiModel.Contracts.V1.Requests
77
{
88
public class CreatePostRequest
99
{
10-
public string Name { get; set; }
10+
public string Name { get; set; }
11+
public IEnumerable<string> Tags { get; internal set; }
1112
}
1213
}

Contracts/V1/Response/PostResponse.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using ApiModel.Domain;
2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading.Tasks;
@@ -7,6 +8,9 @@ namespace ApiModel.Contracts.V1.Response
78
{
89
public class PostResponse
910
{
10-
public Guid Id { get; set; }
11+
public Guid Id { get; set; }
12+
public string Name { get; set; }
13+
public string UserId { get; set; }
14+
public IEnumerable<TagResponse> Tags { get; set; }
1115
}
1216
}

Contracts/V1/Response/TagResponse.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
6+
namespace ApiModel.Contracts.V1.Response
7+
{
8+
public class TagResponse
9+
{
10+
public string Name { get; internal set; }
11+
}
12+
}

Controllers/V1/PostsController.cs

+31-12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using ApiModel.Domain;
55
using ApiModel.Extensions;
66
using ApiModel.Services;
7+
using AutoMapper;
78
using Microsoft.AspNetCore.Authentication.JwtBearer;
89
using Microsoft.AspNetCore.Authorization;
910
using Microsoft.AspNetCore.Mvc;
@@ -17,26 +18,42 @@ namespace ApiModel.Controllers
1718
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
1819
public class PostsController : Controller
1920
{
20-
private readonly IPostService _postService;
21-
public PostsController(IPostService postService)
21+
private readonly IPostService _postService;
22+
private readonly IMapper _mapper;
23+
24+
25+
public PostsController(IPostService postService, IMapper mapper)
2226
{
2327
_postService = postService;
28+
_mapper = mapper;
2429
}
2530
[HttpGet(ApiRoutes.Posts.GetAll)]
2631
public async Task<IActionResult> GetAll()
2732
{
28-
return Ok(await _postService.GetPostsAsync());
33+
var posts = await _postService.GetPostsAsync();
34+
//var postResponse = _mapper.Map<List<PostResponse>>(posts);
35+
return Ok(_mapper.Map<List<PostResponse>>(posts));
2936
}
3037

31-
[HttpGet(ApiRoutes.Posts.Get)]
38+
39+
// posts.Select(post => new PostResponse
40+
// {
41+
// Id = post.Id,
42+
// Name = post.Name,
43+
// UserId = post.UserId,
44+
// Tags = post.Tags.Select(x => new TagResponse { Name = x.TagName
45+
//}).ToList()
46+
// }).ToList()
47+
48+
49+
[HttpGet(ApiRoutes.Posts.Get)]
3250
public async Task<IActionResult> Get([FromRoute]Guid postId)
3351
{
3452
var post = await _postService.GetPostByIdAsync(postId);
3553
if (post == null)
3654
return NotFound();
3755

38-
39-
return Ok(post);
56+
return Ok(_mapper.Map<List<PostResponse>>(post));
4057
}
4158
[HttpPut(ApiRoutes.Posts.Update)]
4259
public async Task<IActionResult> Update([FromRoute] Guid postId, [FromBody] UpdatePostRequest request)
@@ -52,8 +69,7 @@ public async Task<IActionResult> Update([FromRoute] Guid postId, [FromBody] Upda
5269
var updated = await _postService.UpdatePostAsync(post);
5370

5471
if (updated)
55-
56-
return Ok(post);
72+
return Ok(_mapper.Map<List<PostResponse>>(post));
5773

5874

5975
return NotFound();
@@ -76,19 +92,22 @@ public async Task<IActionResult> Delete([FromRoute] Guid postId)
7692
[HttpPost(ApiRoutes.Posts.Create)]
7793
public async Task<IActionResult> Create([FromBody] CreatePostRequest postRequest)
7894
{
79-
var post = new Post
95+
var newPostId = Guid.NewGuid();
96+
var post = new Post
8097
{
98+
Id = newPostId,
8199
Name = postRequest.Name,
82-
UserId = HttpContext.GetUserId()
100+
UserId = HttpContext.GetUserId(),
101+
Tags = postRequest.Tags.Select(x => new PostTag { PostId = newPostId, TagName = x }).ToList()
83102
};
84103

85104
await _postService.CreatePostAsync(post);
86105

87106
var baseUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.ToUriComponent()}";
88107
var locationUri = baseUrl + "/" + ApiRoutes.Posts.Get.Replace("{postId}", post.Id.ToString());
89108

90-
var response = new PostResponse { Id = post.Id };
91-
return Created(locationUri, response);
109+
110+
return Created(locationUri, _mapper.Map<List<PostResponse>>(post));
92111
}
93112
}
94113
}

Controllers/V1/TagsController.cs

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using ApiModel.Contracts;
22
using ApiModel.Contracts.V1.Requests;
3+
using ApiModel.Contracts.V1.Response;
34
using ApiModel.Domain;
45
using ApiModel.Extensions;
56
using ApiModel.Services;
7+
using AutoMapper;
68
using Microsoft.AspNetCore.Authentication.JwtBearer;
79
using Microsoft.AspNetCore.Authorization;
810
using Microsoft.AspNetCore.Mvc;
@@ -17,16 +19,21 @@ namespace ApiModel.Controllers.V1
1719
public class TagsController : Controller
1820
{
1921
private readonly IPostService _postService;
20-
public TagsController(IPostService postService)
22+
private readonly IMapper _mapper;
23+
24+
public TagsController(IPostService postService, IMapper mapper)
2125
{
2226
_postService = postService;
27+
_mapper = mapper;
2328
}
2429

2530
[HttpGet(ApiRoutes.Tags.GetAll)]
26-
[Authorize(Policy = "TagViewer")]
31+
//[Authorize(Policy = "TagViewer")]
2732
public async Task<IActionResult> GetAll()
2833
{
29-
return Ok(await _postService.GetAllTagsAsync());
34+
35+
var tags = await _postService.GetAllTagsAsync();
36+
return Ok(_mapper.Map<List<TagResponse>>(tags));
3037
}
3138

3239
public IActionResult Ping()
@@ -53,7 +60,7 @@ public async Task<IActionResult> Create([FromBody] CreateTagRequest request)
5360

5461
var baseUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.ToUriComponent()}";
5562
var locationUri = baseUrl + "/" + ApiRoutes.Tags.Get.Replace("{tagName}", newTag.Name);
56-
return Created(locationUri, newTag);
63+
return Created(locationUri, _mapper.Map<List<TagResponse>>(newTag));
5764
}
5865

5966
[HttpDelete(ApiRoutes.Tags.Delete)]
@@ -79,7 +86,7 @@ public async Task<IActionResult> Get([FromRoute] string tagName)
7986
return NotFound();
8087
}
8188

82-
return Ok(tag);
89+
return Ok(_mapper.Map<List<TagResponse>>(tag));
8390
}
8491

8592
[HttpPut(ApiRoutes.Tags.Update)]

Data/DataContext.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ public DataContext(DbContextOptions<DataContext> options)
1515
}
1616
public DbSet<Post> Posts { get; set; }
1717
public DbSet<RefreshToken> RefreshTokens { get; set; }
18-
public DbSet<Tag> Tags { get; set; }
18+
public DbSet<Tag> Tags { get; set; }
19+
20+
public DbSet<PostTag> PostTags { get; set; }
21+
22+
protected override void OnModelCreating(ModelBuilder builder)
23+
{
24+
base.OnModelCreating(builder);
25+
26+
builder.Entity<PostTag>().Ignore(xx => xx.Post).HasKey(x => new { x.PostId, x.TagName });
27+
}
28+
1929
}
2030
}

Domain/Post.cs

+1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ public class Post
1616
public string UserId { get; set; }
1717
[ForeignKey(nameof(UserId))]
1818
public IdentityUser User { get; set; }
19+
public virtual List<PostTag> Tags { get; set; }
1920
}
2021
}

Domain/PostTag.cs

+6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations.Schema;
34
using System.Linq;
45
using System.Threading.Tasks;
56

67
namespace ApiModel.Domain
78
{
89
public class PostTag
910
{
11+
[ForeignKey(nameof(TagName))]
12+
public virtual Tag Tag { get; set; }
13+
public string TagName { get; set; }
14+
public virtual Post Post { get; set; }
15+
public Guid PostId { get; set; }
1016

1117
}
1218
}

Mapping/DomainToResponseProfile.cs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using ApiModel.Contracts.V1.Response;
2+
using ApiModel.Domain;
3+
using AutoMapper;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
9+
namespace ApiModel.Mapping
10+
{
11+
public class DomainToResponseProfile : Profile
12+
{
13+
public DomainToResponseProfile()
14+
{
15+
CreateMap<Post, PostResponse>()
16+
.ForMember(dest => dest.Tags, opt =>
17+
opt.MapFrom(src => src.Tags.Select(x=> new TagResponse { Name = x.TagName })));
18+
19+
CreateMap<Tag, TagResponse>();
20+
}
21+
}
22+
}

Startup.cs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public Startup(IConfiguration configuration)
3030
public void ConfigureServices(IServiceCollection services)
3131
{
3232
services.InstallServicesInAssembly(Configuration);
33+
services.AddAutoMapper(typeof(Startup));
3334
}
3435

3536
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

obj/ApiModel.csproj.nuget.dgspec.json

+20-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@
4848
"net5.0": {
4949
"targetAlias": "net5.0",
5050
"dependencies": {
51+
"AutoMapper": {
52+
"target": "Package",
53+
"version": "[10.1.1, )"
54+
},
55+
"AutoMapper.Extensions.Microsoft.DependencyInjection": {
56+
"target": "Package",
57+
"version": "[8.1.1, )"
58+
},
5159
"Cosmonaut": {
5260
"target": "Package",
5361
"version": "[2.11.3, )"
@@ -56,6 +64,10 @@
5664
"target": "Package",
5765
"version": "[2.3.0, )"
5866
},
67+
"Humanizer.Core": {
68+
"target": "Package",
69+
"version": "[2.8.26, )"
70+
},
5971
"Microsoft.AspNet.WebApi.Client": {
6072
"target": "Package",
6173
"version": "[5.2.7, )"
@@ -80,6 +92,12 @@
8092
"target": "Package",
8193
"version": "[5.0.4, )"
8294
},
95+
"Microsoft.EntityFrameworkCore.Design": {
96+
"include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive",
97+
"suppressParent": "All",
98+
"target": "Package",
99+
"version": "[5.0.4, )"
100+
},
83101
"Microsoft.EntityFrameworkCore.InMemory": {
84102
"target": "Package",
85103
"version": "[5.0.4, )"
@@ -88,9 +106,9 @@
88106
"target": "Package",
89107
"version": "[5.0.4, )"
90108
},
91-
"Microsoft.EntityFrameworkCore.Tools": {
109+
"Microsoft.EntityFrameworkCore.SqlServer.Design": {
92110
"target": "Package",
93-
"version": "[3.1.8, )"
111+
"version": "[1.1.6, )"
94112
},
95113
"Microsoft.OpenApi": {
96114
"target": "Package",

obj/ApiModel.csproj.nuget.g.props

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@
2020
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
2121
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\3.0.0\build\Microsoft.Extensions.ApiDescription.Server.props" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\3.0.0\build\Microsoft.Extensions.ApiDescription.Server.props')" />
2222
<Import Project="$(NuGetPackageRoot)swashbuckle.aspnetcore\6.1.1\build\Swashbuckle.AspNetCore.props" Condition="Exists('$(NuGetPackageRoot)swashbuckle.aspnetcore\6.1.1\build\Swashbuckle.AspNetCore.props')" />
23-
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore.design\3.1.8\build\netcoreapp2.0\Microsoft.EntityFrameworkCore.Design.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore.design\3.1.8\build\netcoreapp2.0\Microsoft.EntityFrameworkCore.Design.props')" />
23+
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore.design\5.0.4\build\netcoreapp3.0\Microsoft.EntityFrameworkCore.Design.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore.design\5.0.4\build\netcoreapp3.0\Microsoft.EntityFrameworkCore.Design.props')" />
2424
<Import Project="$(NuGetPackageRoot)microsoft.aspnetcore.identity.ui\5.0.4\buildTransitive\Microsoft.AspNetCore.Identity.UI.props" Condition="Exists('$(NuGetPackageRoot)microsoft.aspnetcore.identity.ui\5.0.4\buildTransitive\Microsoft.AspNetCore.Identity.UI.props')" />
2525
</ImportGroup>
2626
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
2727
<PkgMicrosoft_Extensions_ApiDescription_Server Condition=" '$(PkgMicrosoft_Extensions_ApiDescription_Server)' == '' ">C:\Users\Administrator\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0</PkgMicrosoft_Extensions_ApiDescription_Server>
2828
<PkgNewtonsoft_Json Condition=" '$(PkgNewtonsoft_Json)' == '' ">C:\Users\Administrator\.nuget\packages\newtonsoft.json\10.0.1</PkgNewtonsoft_Json>
29-
<PkgMicrosoft_EntityFrameworkCore_Tools Condition=" '$(PkgMicrosoft_EntityFrameworkCore_Tools)' == '' ">C:\Users\Administrator\.nuget\packages\microsoft.entityframeworkcore.tools\3.1.8</PkgMicrosoft_EntityFrameworkCore_Tools>
3029
</PropertyGroup>
3130
</Project>

0 commit comments

Comments
 (0)