Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CqrsAndMediatRInAspNetCore", "CqrsAndMediatRInAspNetCore\CqrsAndMediatRInAspNetCore.csproj", "{A35A7661-54DB-466D-BFB1-F743CB102862}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CqrsAndMediatRInAspNetCoreTests", "Tests\CqrsAndMediatRInAspNetCoreTests\CqrsAndMediatRInAspNetCoreTests.csproj", "{1AAD829F-350B-49F1-B9CC-9451038A08CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|x64.ActiveCfg = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|x64.Build.0 = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|x86.ActiveCfg = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Debug|x86.Build.0 = Debug|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|Any CPU.Build.0 = Release|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|x64.ActiveCfg = Release|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|x64.Build.0 = Release|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|x86.ActiveCfg = Release|Any CPU
{A35A7661-54DB-466D-BFB1-F743CB102862}.Release|x86.Build.0 = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|x64.ActiveCfg = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|x64.Build.0 = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|x86.ActiveCfg = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Debug|x86.Build.0 = Debug|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|Any CPU.Build.0 = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|x64.ActiveCfg = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|x64.Build.0 = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|x86.ActiveCfg = Release|Any CPU
{1AAD829F-350B-49F1-B9CC-9451038A08CF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1AAD829F-350B-49F1-B9CC-9451038A08CF} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Behaviors
{
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
=> _logger = logger;

public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
_logger.LogInformation($"Handling {typeof(TRequest).Name}");

var response = await next();

_logger.LogInformation($"Handled {typeof(TResponse).Name}");

return response;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Commands
{
public record AddProductCommand(Product Product) : IRequest<Product>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using CqrsAndMediatRInAspNetCore.Commands;
using CqrsAndMediatRInAspNetCore.Notifications;
using CqrsAndMediatRInAspNetCore.Queries;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace CqrsAndMediatRInAspNetCore.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;

public ProductsController(IMediator mediator) => _mediator = mediator;

[HttpGet]
public async Task<ActionResult> GetProducts()
{
var products = await _mediator.Send(new GetProductsQuery());

return Ok(products);
}

[HttpGet("{id:int}", Name = "GetProductById")]
public async Task<ActionResult> GetProductById(int id)
{
var product = await _mediator.Send(new GetProductByIdQuery(id));

return Ok(product);
}

[HttpPost]
public async Task<ActionResult> AddProduct([FromBody] Product product)
{
var productToReturn = await _mediator.Send(new AddProductCommand(product));

await _mediator.Publish(new ProductAddedNotification(productToReturn));

return CreatedAtRoute("GetProductById", new { id = productToReturn.Id }, productToReturn);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MediatR" Version="12.5.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace CqrsAndMediatRInAspNetCore.DataStore
{
public class FakeDataStore
{
private static List<Product> _products;

Check warning on line 9 in dotnet-client-libraries/CqrsAndMediatRInAspNetCore/CqrsAndMediatRInAspNetCore/DataStore/FakeDataStore.cs

View workflow job for this annotation

GitHub Actions / Build & test (dotnet-client-libraries/CqrsAndMediatRInAspNetCore)

Non-nullable field '_products' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 9 in dotnet-client-libraries/CqrsAndMediatRInAspNetCore/CqrsAndMediatRInAspNetCore/DataStore/FakeDataStore.cs

View workflow job for this annotation

GitHub Actions / Build & test (dotnet-client-libraries/CqrsAndMediatRInAspNetCore)

Non-nullable field '_products' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

public FakeDataStore()
{
_products = new List<Product>
{
new Product { Id = 1, Name = "Test Product 1" },
new Product { Id = 2, Name = "Test Product 2" },
new Product { Id = 3, Name = "Test Product 3" }
};
}

public async Task AddProduct(Product product)
{
_products.Add(product);
await Task.CompletedTask;
}

public async Task<IEnumerable<Product>> GetAllProducts() => await Task.FromResult(_products);

public async Task<Product> GetProductById(int id) =>
await Task.FromResult(_products.Single(p => p.Id == id));

public async Task EventOccured(Product product, string evt)
{
_products.Single(p => p.Id == product.Id).Name = $"{product.Name} evt: {evt}";
await Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using CqrsAndMediatRInAspNetCore.Commands;
using CqrsAndMediatRInAspNetCore.DataStore;
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Handlers
{
public class AddProductHandler : IRequestHandler<AddProductCommand, Product>
{
private readonly FakeDataStore _fakeDataStore;

public AddProductHandler(FakeDataStore fakeDataStore) => _fakeDataStore = fakeDataStore;

public async Task<Product> Handle(AddProductCommand request, CancellationToken cancellationToken)
{
await _fakeDataStore.AddProduct(request.Product);

return request.Product;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CqrsAndMediatRInAspNetCore.DataStore;
using CqrsAndMediatRInAspNetCore.Notifications;
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CqrsAndMediatRInAspNetCore.Handlers
{
public class CacheInvalidationHandler : INotificationHandler<ProductAddedNotification>
{
private readonly FakeDataStore _fakeDataStore;

public CacheInvalidationHandler(FakeDataStore fakeDataStore) => _fakeDataStore = fakeDataStore;

public async Task Handle(ProductAddedNotification notification, CancellationToken cancellationToken)
{
await _fakeDataStore.EventOccured(notification.Product, "Cache Invalidated");
await Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CqrsAndMediatRInAspNetCore.DataStore;
using CqrsAndMediatRInAspNetCore.Notifications;
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CqrsAndMediatRInAspNetCore.Handlers
{
public class EmailHandler : INotificationHandler<ProductAddedNotification>
{
private readonly FakeDataStore _fakeDataStore;

public EmailHandler(FakeDataStore fakeDataStore) => _fakeDataStore = fakeDataStore;

public async Task Handle(ProductAddedNotification notification, CancellationToken cancellationToken)
{
await _fakeDataStore.EventOccured(notification.Product, "Email sent");
await Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using CqrsAndMediatRInAspNetCore.DataStore;
using CqrsAndMediatRInAspNetCore.Queries;
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Handlers
{
public class GetProductByIdHandler : IRequestHandler<GetProductByIdQuery, Product>
{
private readonly FakeDataStore _fakeDataStore;

public GetProductByIdHandler(FakeDataStore fakeDataStore) => _fakeDataStore = fakeDataStore;

public async Task<Product> Handle(GetProductByIdQuery request, CancellationToken cancellationToken) =>
await _fakeDataStore.GetProductById(request.Id);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CqrsAndMediatRInAspNetCore.DataStore;
using CqrsAndMediatRInAspNetCore.Queries;
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Handlers
{
public class GetProductsHandler : IRequestHandler<GetProductsQuery, IEnumerable<Product>>
{
private readonly FakeDataStore _fakeDataStore;

public GetProductsHandler(FakeDataStore fakeDataStore) => _fakeDataStore = fakeDataStore;

public async Task<IEnumerable<Product>> Handle(GetProductsQuery request,
CancellationToken cancellationToken) => await _fakeDataStore.GetAllProducts();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Notifications
{
public record ProductAddedNotification(Product Product) : INotification;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace CqrsAndMediatRInAspNetCore
{
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using CqrsAndMediatRInAspNetCore.Behaviors;
using CqrsAndMediatRInAspNetCore.DataStore;
using MediatR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

builder.Services.AddSingleton<FakeDataStore>();

builder.Services.AddSingleton(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));

builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Queries
{
public record GetProductByIdQuery(int Id) : IRequest<Product>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MediatR;

namespace CqrsAndMediatRInAspNetCore.Queries
{
public record GetProductsQuery() : IRequest<IEnumerable<Product>>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Loading
Loading