Skip to content

Commit 258e58b

Browse files
authored
Merge pull request #7 from sj-distributor/clutter
Enhance infrastructure
2 parents 870c1c6 + 6071368 commit 258e58b

22 files changed

+303
-131
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
# Wax
2+
3+
[![.NET](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml/badge.svg)](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml)
4+
[![Nuget](https://img.shields.io/nuget/v/Wax.Template)](https://www.nuget.org/packages/Wax.Template)
5+
26
WilTechs Architecture Solution Template for .NET 6
7+
8+
## Getting Started
9+
10+
Using dotnet cli template, install the template:
11+
12+
```
13+
dotnet new -i Wax.Template
14+
```
15+
16+
Run this command to create the solution:
17+
18+
```
19+
dotnet new wax -o ProjectName
20+
```

src/Wax.Api/Filters/GlobalExceptionFilter.cs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Net;
2-
using Microsoft.AspNetCore.Mvc;
1+
using Microsoft.AspNetCore.Mvc;
32
using Microsoft.AspNetCore.Mvc.Filters;
43
using Wax.Core.Exceptions;
54

@@ -39,28 +38,12 @@ private void HandleBusinessException(ExceptionContext context)
3938
{
4039
_logger.Warning(context.Exception.Message);
4140

42-
var problemDetails = new ValidationProblemDetails
43-
{
44-
Instance = context.HttpContext.Request.Path,
45-
Status = StatusCodes.Status400BadRequest,
46-
Detail = "Please refer to the errors property for additional details."
47-
};
48-
49-
problemDetails.Errors.Add("BusinessValidations", new string[] { context.Exception.Message });
50-
51-
context.Result = new BadRequestObjectResult(problemDetails);
52-
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
53-
}
54-
55-
private void HandleInternalServerError(ExceptionContext context)
56-
{
57-
_logger.Error(context.Exception, context.Exception.Message);
58-
5941
var problemDetails = new ProblemDetails
6042
{
61-
Status = StatusCodes.Status500InternalServerError,
62-
Title = "Internal error",
63-
Detail = "An error occur.Try it again."
43+
Status = StatusCodes.Status403Forbidden,
44+
Title = "Business error",
45+
Detail = context.Exception.Message,
46+
Instance = context.HttpContext.Request.Path
6447
};
6548

6649
context.Result = new ObjectResult(problemDetails);
@@ -76,7 +59,7 @@ private void HandleEntityNotFoundException(ExceptionContext context)
7659
{
7760
Status = StatusCodes.Status404NotFound,
7861
Title = "The specified resource was not found.",
79-
Detail = exception.Message
62+
Detail = exception.Message,
8063
};
8164

8265
context.Result = new NotFoundObjectResult(details);
@@ -93,4 +76,18 @@ private void HandleValidationException(ExceptionContext context)
9376

9477
context.ExceptionHandled = true;
9578
}
79+
80+
private void HandleInternalServerError(ExceptionContext context)
81+
{
82+
_logger.Error(context.Exception, context.Exception.Message);
83+
84+
var problemDetails = new ProblemDetails
85+
{
86+
Status = StatusCodes.Status500InternalServerError,
87+
Title = "Internal error",
88+
Detail = "An error occur.Try it again later."
89+
};
90+
91+
context.Result = new ObjectResult(problemDetails);
92+
}
9693
}

src/Wax.Api/Program.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@
33

44
namespace Wax.Api;
55

6-
public class Program
6+
public partial class Program
77
{
88
public static void Main(string[] args)
99
{
10-
var application = typeof(Program).Namespace ?? "Wax.Api";
11-
10+
var configuration = GetConfiguration();
11+
12+
Log.Logger = CreateSerilogLogger(configuration);
13+
1214
try
1315
{
16+
Log.Information("Configuring api host ({ApplicationContext})... ", AppName);
17+
1418
var webHost = CreateWebHostBuilder(args).Build();
19+
20+
Log.Information("Starting api host ({ApplicationContext})...", AppName);
1521

1622
webHost.Run();
1723
}
1824
catch (Exception ex)
1925
{
20-
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", application);
26+
Log.Fatal(ex, "Program terminated unexpectedly!");
2127
}
2228
finally
2329
{
@@ -27,6 +33,40 @@ public static void Main(string[] args)
2733

2834
private static IHostBuilder CreateWebHostBuilder(string[] args) =>
2935
Host.CreateDefaultBuilder(args)
36+
.ConfigureLogging(l => l.AddSerilog(Log.Logger))
3037
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
31-
.ConfigureWebHostDefaults(builder => { builder.UseStartup<Startup>(); });
38+
.ConfigureWebHostDefaults(builder => { builder.UseStartup<Startup>(); }).UseSerilog();
39+
40+
private static IConfiguration GetConfiguration()
41+
{
42+
var builder = new ConfigurationBuilder()
43+
.SetBasePath(Directory.GetCurrentDirectory())
44+
.AddJsonFile("appsettings.json", false, true)
45+
.AddEnvironmentVariables();
46+
47+
return builder.Build();
48+
}
49+
50+
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
51+
{
52+
var seqServerUrl = configuration["Serilog:Seq:ServerUrl"];
53+
var seqApiKey = configuration["Serilog:Seq:ApiKey"];
54+
55+
return new LoggerConfiguration()
56+
.MinimumLevel.Information()
57+
.Enrich.WithProperty("ApplicationContext", AppName)
58+
.Enrich.FromLogContext()
59+
.Enrich.WithCorrelationId()
60+
.WriteTo.Console()
61+
.WriteTo.Seq(seqServerUrl, apiKey: seqApiKey)
62+
.CreateLogger();
63+
}
64+
}
65+
66+
public partial class Program
67+
{
68+
private static readonly string Namespace = typeof(Startup).Namespace;
69+
70+
private static readonly string AppName =
71+
Namespace[(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1)..];
3272
}

src/Wax.Api/Startup.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,18 @@ public class Startup
1717
public Startup(IConfiguration configuration)
1818
{
1919
Configuration = configuration;
20-
21-
Log.Logger = new LoggerConfiguration()
22-
.ReadFrom.Configuration(Configuration)
23-
.CreateLogger();
24-
25-
Log.Information("Starting up");
2620
}
2721

2822
// ConfigureServices is where you register dependencies. This gets
2923
// called by the runtime before the ConfigureContainer method, below.
3024
public void ConfigureServices(IServiceCollection services)
3125
{
32-
services.AddControllers(options =>
33-
{
34-
options.Filters.Add<GlobalExceptionFilter>();
35-
});
26+
services.AddControllers(options => { options.Filters.Add<GlobalExceptionFilter>(); });
3627
services.AddOptions();
3728
services.AddCustomSwagger();
3829
services.AddScoped<ICurrentUser, CurrentUser>();
3930
services.AddLogging();
31+
services.AddHttpContextAccessor();
4032

4133
_serviceCollection = services;
4234
}
@@ -62,10 +54,8 @@ public void ConfigureContainer(ContainerBuilder builder)
6254
// Configure is where you add middleware. This is called after
6355
// ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
6456
// here if you need to resolve things from the container.
65-
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
57+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
6658
{
67-
loggerFactory.AddSerilog();
68-
6959
if (env.IsDevelopment())
7060
{
7161
app.UseSwagger();

src/Wax.Api/Wax.Api.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9+
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
910
<PackageReference Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
10-
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
11-
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
1211
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
12+
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
1313
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
1414
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
1515
</ItemGroup>

src/Wax.Api/appsettings.json

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,9 @@
11
{
22
"AllowedHosts": "*",
33
"Serilog": {
4-
"Using": [
5-
"Serilog.Sinks.Console"
6-
],
7-
"MinimumLevel": {
8-
"Default": "Debug",
9-
"Override": {
10-
"Microsoft": "Warning",
11-
"System": "Warning"
12-
}
13-
},
14-
"WriteTo": [
15-
{
16-
"Name": "Console"
17-
},
18-
{
19-
"Name": "Seq",
20-
"Args": {
21-
"serverUrl": "http://localhost:5341"
22-
}
23-
}
24-
],
25-
"Enrich": [
26-
"FromLogContext",
27-
"WithMachineName",
28-
"WithThreadId",
29-
"WithCorrelationId"
30-
],
31-
"Properties": {
32-
"Application": "Wax.Api"
4+
"Seq": {
5+
"ServerUrl": "http://localhost:5341",
6+
"ApiKey": ""
337
}
348
},
359
"ConnectionStrings": {

src/Wax.Core/ApplicationModule.cs

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
using AutoMapper.Contrib.Autofac.DependencyInjection;
44
using Mediator.Net;
55
using Mediator.Net.Autofac;
6-
using Mediator.Net.Middlewares.Serilog;
76
using Microsoft.EntityFrameworkCore;
87
using Serilog;
98
using Wax.Core.Data;
109
using Wax.Core.Data.Repositories;
1110
using Wax.Core.DependencyInjection;
1211
using Wax.Core.Domain;
13-
using Wax.Core.Domain.Customers;
14-
using Wax.Core.Processing.FluentMessageValidator;
12+
using Wax.Core.Middlewares.FluentMessageValidator;
13+
using Wax.Core.Middlewares.Logging;
1514
using Wax.Core.Services.Identity;
1615
using Module = Autofac.Module;
1716

@@ -32,7 +31,7 @@ public ApplicationModule(ILogger logger, ICurrentUser currentUser, string connec
3231
_connectionString = connectionString;
3332

3433
_assemblies = (assemblies ?? Array.Empty<Assembly>())
35-
.Concat(new[] {typeof(ApplicationModule).Assembly})
34+
.Concat(new[] { typeof(ApplicationModule).Assembly })
3635
.ToArray();
3736
}
3837

@@ -88,9 +87,6 @@ private void RegisterDatabase(ContainerBuilder builder)
8887
builder.RegisterGeneric(typeof(EfCoreRepository<>))
8988
.As(typeof(IRepository<>))
9089
.InstancePerLifetimeScope();
91-
92-
//TODO: Register all repository
93-
builder.RegisterType<EfCoreCustomerRepository>().As<ICustomerRepository>().InstancePerLifetimeScope();
9490
}
9591

9692
private void RegisterIdentity(ContainerBuilder builder)
@@ -112,7 +108,7 @@ private void RegisterMediator(ContainerBuilder builder)
112108
mediatorBuilder.RegisterHandlers(_assemblies);
113109
mediatorBuilder.ConfigureGlobalReceivePipe(c =>
114110
{
115-
c.UseSerilog(logger: _logger);
111+
c.UseLogger();
116112
c.UseMessageValidator();
117113
});
118114

@@ -125,21 +121,5 @@ private void RegisterValidator(ContainerBuilder builder)
125121
.Where(t => t.IsClass && typeof(IFluentMessageValidator).IsAssignableFrom(t))
126122
.AsSelf().AsImplementedInterfaces();
127123
}
128-
129-
private static bool IsAssignableToGenericType(Type givenType, Type genericType)
130-
{
131-
var interfaceTypes = givenType.GetInterfaces();
132-
133-
if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
134-
{
135-
return true;
136-
}
137-
138-
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
139-
return true;
140-
141-
var baseType = givenType.BaseType;
142-
return baseType != null && IsAssignableToGenericType(baseType, genericType);
143-
}
144124
}
145125
}

src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using Microsoft.EntityFrameworkCore;
2+
using Wax.Core.DependencyInjection;
23
using Wax.Core.Domain.Customers;
34

45
namespace Wax.Core.Data.Repositories;
56

6-
public class EfCoreCustomerRepository : EfCoreRepository<Customer>, ICustomerRepository
7+
public class EfCoreCustomerRepository : EfCoreRepository<Customer>, ICustomerRepository, IScopedDependency
78
{
89
private readonly DbSet<Customer> _customers;
910

src/Wax.Core/Exceptions/BusinessException.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
{
33
public class BusinessException : Exception
44
{
5-
public BusinessException()
6-
{
7-
}
8-
9-
public BusinessException(string message) : base(message)
5+
protected BusinessException(string message) : base(message)
106
{
117
}
128
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace Wax.Core.Extensions;
2+
3+
public static class TypeExtensions
4+
{
5+
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
6+
{
7+
var interfaceTypes = givenType.GetInterfaces();
8+
9+
if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
10+
{
11+
return true;
12+
}
13+
14+
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
15+
return true;
16+
17+
var baseType = givenType.BaseType;
18+
return baseType != null && IsAssignableToGenericType(baseType, genericType);
19+
}
20+
21+
public static string GetGenericTypeName(this Type type)
22+
{
23+
string typeName;
24+
25+
if (type.IsGenericType)
26+
{
27+
var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray());
28+
typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>";
29+
}
30+
else
31+
{
32+
typeName = type.Name;
33+
}
34+
35+
return typeName;
36+
}
37+
38+
public static string GetGenericTypeName(this object @object)
39+
{
40+
return @object.GetType().GetGenericTypeName();
41+
}
42+
}

0 commit comments

Comments
 (0)