Preparing the Database
docker run --name postgres -e POSTGRES_PASSWORD=postgres -e TZ=Asia/Shanghai -d -p 5432:5432 postgres
docker run --name pgadmin -e PGADMIN_DEFAULT_EMAIL=test@test.com -e PGADMIN_DEFAULT_PASSWORD=test -e TZ=Asia/Shanghai -d -p 5050:80 dpage/pgadmin4
Initializing the Dapr Runtime
dapr init -s --from-dir C:\dapr
Extending the Wait Time for RabbitMQ Startup
public static IResourceBuilder<IDaprSidecarResource> WithReference(this IResourceBuilder<IDaprSidecarResource> builder, IResourceBuilder<IResourceWithConnectionString> resourceBuilder, int waitInSeconds = 10)
Updating MediatR Dependency Injection
Previously, behaviors were registered with the options.AddBehavior method; now they are registered with the options.AddOpenBehavior method.
builder.Services.AddMediatR(options =>
{
options.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
options.AddOpenBehavior(typeof(LoggingBehavior<,>));
options.AddOpenBehavior(typeof(ValidatorBehavior<,>));
options.AddOpenBehavior(typeof(TransactionBehavior<,>));
});
Microservices Using Distributed Events Need the HttpContext Injected
builder.Services.AddHttpContextAccessor();
Dropping a Database in Azure Data Studio
-- Terminate all sessions connected to the target database
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'my_database'
AND pid <> pg_backend_pid();
-- Drop the database
DROP DATABASE my_database;
Automatically Creating the Database Before IdentityService Demo Data Generation
await serviceProvider.GetRequiredService<IdentityServiceDbContext>().Database.EnsureCreatedAsync(cancellationToken);
Consistently Use DateTimeOffset Instead of DateTime
The advantage of using UTC time is that it can be converted between time zones without losing time information, which helps programs handle time better and benefits the internationalization and localization of cross-time-zone applications.
var utcNow = DateTimeOffset.UtcNow;
When displaying time, you can use the ToLocalTime method to convert it to local time.
var localTime = utcNow.LocalDateTime;
The time type in PostgreSQL databases is timestamp with time zone, which converts time to UTC for storage.
All times in a PostgreSQL database are UTC, but they are converted to local time according to the time zone when queried. This process is transparent and automatic.
Remove the previously configured, inelegant time conversion settings.
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
Prefer TimeProvider for Getting the Current Time
Use the TimeProvider abstract class whenever possible, because it makes it easier to mock time in tests.
var now = TimeProvider.System.Now;
var utcNow = TimeProvider.System.UtcNow;
which is equivalent to
var now = DateTimeOffset.Now;
var utcNow = DateTimeOffset.UtcNow;
public class MyTimeProvider: TimeProvider
{
public override DateTimeOffset Now => new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero);
public override DateTimeOffset UtcNow => new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero);
}
services.AddSingleton<TimeProvider, MyTimeProvider>();
public class MyService
{
private readonly TimeProvider _timeProvider;
public MyService(TimeProvider timeProvider)
{
_timeProvider = timeProvider;
}
public void DoSomething()
{
var now = _timeProvider.Now;
var utcNow = _timeProvider.UtcNow;
}
}
Configuring OrderingService Authentication
const string issuerSigningKey = ServiceDefaults.Constants.IdentityConstantsIssuerSigningKey;
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
options.TokenValidationParameters.ValidateAudience = false;
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(issuerSigningKey));
});
Configuring OrderingService Object Mapping
public class OrdersMapConfiguration : Profile
{
public OrdersMapConfiguration()
{
CreateMap<CreateOrderRequest, CreateOrderCommand>().ForMember(dest => dest.OrderItems, opt => opt.MapFrom(src => src.Items));
CreateMap<BasketItem, CreateOrderCommand.CreateOrderCommandItem>().ForMember(dest => dest.Units, opt => opt.MapFrom(src => src.Quantity));
CreateMap<CreateOrderCommand.CreateOrderCommandItem, OrderItem>();
CreateMap<CreateOrderCommand, Address>();
}
}
Configuring the Field Lengths of the Product Entity in ProductService
public class ProductEntityTypeConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.Property(x => x.Name).HasMaxLength(64);
}
}