Converting from a string to a generic method invocation

Looking for design patterns to help solve this problem I’m having at work. The situation is that we have a URL like GET /entities/<userid>/<entity type> where entity type is a string, and we want to fetch the entities of that type belonging to the current user. Normally I would just use a switch case to go from eg entitytype = “someentity” => entitiesService.GetEntities<SomeEntity>(). But my tech lead insists that new entity types should be automatically supported without code changes to this endpoint because he thinks it will be forgotten. I’ve landed on a solution using reflection to grab the types deriving from my base Entity class and build a dict of (string, Type), then using reflection to get the method info of GetEntities and use MakeGenericMethod with the type from the dict, but am not too pleased with it. I’m considering the following alternatives: - source generators, seemed like a lot of code to generate 1 switch case - using reflection in the testing to make the pipeline fail if another dev forgets to update the endpoint But I’d like to know if there are any other solutions people can think of / thoughts on some of the existing options
5 Replies
Dharmang
Dharmang7mo ago
One question, do you use ef core? cause i recently had to change the efcore code to dapper queries as dynamic Entities are not supported. if its a local c# entity then all good
dreadfullydistinct
Yeah we use ef core But I’m using .Set<TEntity> to accomplish the generic ness
life grinder
life grinder7mo ago
so it's a table per type situation?
dreadfullydistinct
Yeah Kinda leaning towards a Dictionary<string, Func<…> that I just manually define And then an automated test that uses reflection
Dharmang
Dharmang7mo ago
its a simple approach where you dont use switch case 😄 switch statement needs compiled value and not a generated one. you can use and enum. I have used it before and it works well. sharing one sample enum which maps to strings.
using Microsoft.Kiota.Abstractions.Extensions;

namespace YOURNAMESPACE
{
public class LookupEnum
{
public static LookupEnum States { get; } = new LookupEnum(9, nameof(States).ToFirstCharacterLowerCase()!);
public static LookupEnum Clients { get; } = new LookupEnum(10, nameof(Clients).ToFirstCharacterLowerCase()!);

public string Name { get; private set; }
public int Value { get; private set; }

private LookupEnum(int val, string name)
{
Value = val;
Name = name;
}

public static IEnumerable<LookupEnum> List()
{
return new[]
{
States,
Clients
};
}

public static LookupEnum? FromString(string values)
{
try
{
return List().Single(r => string.Equals(r.Name, values, StringComparison.OrdinalIgnoreCase));
}
catch
{
return null;
}
}

public static LookupEnum FromValue(int value)
{
return List().Single(r => r.Value == value);
}

public static IEnumerable<LookupEnum> FromStrings(string[] values)
{
return values.Select(FromString).Where(value => value != null)!;
}
}
}
using Microsoft.Kiota.Abstractions.Extensions;

namespace YOURNAMESPACE
{
public class LookupEnum
{
public static LookupEnum States { get; } = new LookupEnum(9, nameof(States).ToFirstCharacterLowerCase()!);
public static LookupEnum Clients { get; } = new LookupEnum(10, nameof(Clients).ToFirstCharacterLowerCase()!);

public string Name { get; private set; }
public int Value { get; private set; }

private LookupEnum(int val, string name)
{
Value = val;
Name = name;
}

public static IEnumerable<LookupEnum> List()
{
return new[]
{
States,
Clients
};
}

public static LookupEnum? FromString(string values)
{
try
{
return List().Single(r => string.Equals(r.Name, values, StringComparison.OrdinalIgnoreCase));
}
catch
{
return null;
}
}

public static LookupEnum FromValue(int value)
{
return List().Single(r => r.Value == value);
}

public static IEnumerable<LookupEnum> FromStrings(string[] values)
{
return values.Select(FromString).Where(value => value != null)!;
}
}
}
Obtained from an article by Steve (you might know him from Ardalis Endpoints) then you use if else statements, along with LookupEnum.FromString(entity) and you can also put validations if the mapping fails
Want results from more Discord servers?
Add your server
More Posts