Victor H
Victor H
CC#
Created by Victor H on 3/2/2025 in #help
Generic static factories
Hello. I have the following interface:
public interface IStronglyTypedId<TValue, out TSelf>
where TSelf : IStronglyTypedId<TValue, TSelf>
{
TValue Value { get; }
static abstract TSelf From(TValue value);
}
public interface IStronglyTypedId<TValue, out TSelf>
where TSelf : IStronglyTypedId<TValue, TSelf>
{
TValue Value { get; }
static abstract TSelf From(TValue value);
}
This works well but I want to have a specific type of strongly typed id's of type AggregateId which must have an underlying backing type of Guid, especially a version 7 guid. So basically how can I have default implementations for abstract records? I tried this but infinite recursion.
public abstract record AggregateId<TId>(Guid Value) : IStronglyTypedId<Guid, TId>
where TId : IStronglyTypedId<Guid, TSelf>
{
public static TSelf From(Guid value) => TSelf.From(value); // <- infinite recursion
}
public abstract record AggregateId<TId>(Guid Value) : IStronglyTypedId<Guid, TId>
where TId : IStronglyTypedId<Guid, TSelf>
{
public static TSelf From(Guid value) => TSelf.From(value); // <- infinite recursion
}
13 replies
CC#
Created by Victor H on 2/24/2025 in #help
Tighter type constraint
Hello. Let's say I have the following
public abstract class Aggregate<TId> where TId : AggregateId
{
public abstract TId { get; protected set; }
}

public abstract record AggregateId(Guid Value);
public abstract class Aggregate<TId> where TId : AggregateId
{
public abstract TId { get; protected set; }
}

public abstract record AggregateId(Guid Value);
and then maybe I have concrete like:
public record OrderId(Guid Id) : AggregateId(Id);

public class Order : Aggregate<OrderId>
{
public override OrderId Id { get; protected set; }
}
public record OrderId(Guid Id) : AggregateId(Id);

public class Order : Aggregate<OrderId>
{
public override OrderId Id { get; protected set; }
}
I can do the following now for type-safety, i.e., that to get Order you must provide an OrderId and you can't mistakenly provide the wrong strongly typed ID:
public TAggregate LoadAggregate<TAggregate, TId>(TId aggregateId)
where TAggregate : Aggregate<TId>
where TId : AggregateId
public TAggregate LoadAggregate<TAggregate, TId>(TId aggregateId)
where TAggregate : Aggregate<TId>
where TId : AggregateId
However, this makes for quite a verbose API, is it possible somehow to constrain this so it is possible that LoadAggregate<Order>(orderId) is only valid if orderId is an OrderId and not say ProductId?
10 replies
CC#
Created by Victor H on 2/1/2025 in #help
Parsing data from several sources into a common format.
I have several data sources that I get data from that I must parse into a common format. Say this is the common format (simplified example):
public record ProductDto(
string ProductId,
Price Price,
);

public abstract record Price(decimal Value);
public sealed record FixedPrice(decimal Value) : Price(Value);
public sealed record PricePerUnit(decimal Value, string Unit) : Price(Value);
public record ProductDto(
string ProductId,
Price Price,
);

public abstract record Price(decimal Value);
public sealed record FixedPrice(decimal Value) : Price(Value);
public sealed record PricePerUnit(decimal Value, string Unit) : Price(Value);
One approach would be to set up something like a public record Source1ProductDto and use annotations like JsonPropertyName for each data source and let System.Text.JsonSerializer handle it. However, for more complex types like Price I might need to combine data from several fields of the original data to parse it into my custom format. My current approach is to use some type of strategy pattern for each field by using this interface:
public interface IFieldParser<out T>
{
T Parse(JsonElement json);
}
public interface IFieldParser<out T>
{
T Parse(JsonElement json);
}
then I do this (simplified parsing for illustration purpose):
public class Source1IdParser : IFieldParser<string>
{
public string Parse(JsonElement)
{
// Here it might be called "id"
return json.GetProperty("id").GetString() ?? string.Empty;
}
}

public class Source2IdParser : IFieldParser<string>
{
public string Parse(JsonElement json)
{
// Here it might be called "productCode"
return json.GetProperty("productCode").GetString() ?? string.Empty;
}
}

public class Source1PriceParser : IFieldParser<Price>
{
public Price Parse(JsonElement json)
{
var unit = json.GetProperty("unit").GetString() ?? "";
var amount = json.GetProperty("price").GetString() ?? "";
return unit == ""
? new FixedPrice(Convert.ToDecimal(amount))
: new PricePerUnit(Convert.ToDecimal(amount), unit);
}
}
public class Source1IdParser : IFieldParser<string>
{
public string Parse(JsonElement)
{
// Here it might be called "id"
return json.GetProperty("id").GetString() ?? string.Empty;
}
}

public class Source2IdParser : IFieldParser<string>
{
public string Parse(JsonElement json)
{
// Here it might be called "productCode"
return json.GetProperty("productCode").GetString() ?? string.Empty;
}
}

public class Source1PriceParser : IFieldParser<Price>
{
public Price Parse(JsonElement json)
{
var unit = json.GetProperty("unit").GetString() ?? "";
var amount = json.GetProperty("price").GetString() ?? "";
return unit == ""
? new FixedPrice(Convert.ToDecimal(amount))
: new PricePerUnit(Convert.ToDecimal(amount), unit);
}
}
You get the point for PriceParser2. Can you give me advice on how to improve? Possibly how to leverage .NET strengths better.
32 replies
CC#
Created by Victor H on 1/3/2025 in #help
Configuration of Testcontainer, WebApplicationFactory and ConfigurationSource in ASP.NET Core.
Hi, I'm trying to figure out a nice approach to setup my tests. I am writing an application where I am using the the Options pattern in my Program.cs to setup some connections option:
// Program.cs
builder.Services.AddOptions<PostgreSqlConnectionOptions>().BindConfiguration("PostgreSqlConnectionOptions");
builder.Services.AddOptions<RabbitMqConnectionOptions>().BindConfiguration("RabbitMqConnectionOptions");
// Program.cs
builder.Services.AddOptions<PostgreSqlConnectionOptions>().BindConfiguration("PostgreSqlConnectionOptions");
builder.Services.AddOptions<RabbitMqConnectionOptions>().BindConfiguration("RabbitMqConnectionOptions");
where for instance:
// PostgreSqlConnectionOptions.cs
public class PostgreSqlConnectionOptions
{
public required string Host { get; set; } = "localhost";
public required string Database { get; set; } = "myDb";
public required string Username { get; set; } = "username";
public required string Password { get; set; } = "password";
public required int Port { get; set; } = 5432;
}
// PostgreSqlConnectionOptions.cs
public class PostgreSqlConnectionOptions
{
public required string Host { get; set; } = "localhost";
public required string Database { get; set; } = "myDb";
public required string Username { get; set; } = "username";
public required string Password { get; set; } = "password";
public required int Port { get; set; } = 5432;
}
In my tests where I am using WebApplicationFactory together with Testcontainers I want to improve my setup. Can you offer me any advice on how to improve my setup code (next message).
7 replies
CC#
Created by Victor H on 4/9/2023 in #help
❔ Switch-case on byte as characters like in C
Hi can I switch case on bytes using character syntax without having to cast to byte? Just wondering if I can make it look nicer without having to write 32 for space for example and not having to cast.
byte c;
switch (c) {

// I preferably don't want to have to do either of these
case (byte) ' ':
case (byte) 32:

// Preferably:
case ' ':
}
byte c;
switch (c) {

// I preferably don't want to have to do either of these
case (byte) ' ':
case (byte) 32:

// Preferably:
case ' ':
}
4 replies