Created
June 26, 2024 00:16
-
-
Save SteffenBlake/b7368b5fbd4efb2a15247f4044271782 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using AbstractResolver; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
var staticServiceFlag = StaticConfiguredServiceFlag.Ping; | |
var dynamicConfig = new DynamicConfiguredServiceConfig() | |
{ | |
// Default to Marco | |
Flag = DynamicConfiguredServiceFlag.Marco | |
}; | |
var builder = Host.CreateApplicationBuilder(args); | |
// This represents a config that is locked in at startup | |
// Likely loaded in from AppSettings or a Secrets provider, etc etc | |
// Pros: fastest performance, easiest to code | |
// Cons: Changing this requires restarting the entire application | |
_ = staticServiceFlag switch | |
{ | |
StaticConfiguredServiceFlag.Ping => | |
builder.Services.AddSingleton<IStaticConfiguredService, PingService>(), | |
StaticConfiguredServiceFlag.Pong => | |
builder.Services.AddSingleton<IStaticConfiguredService, PongService>(), | |
_ => throw new InvalidOperationException() | |
}; | |
// This represents a config that is NOT locked in at startup, and instead can be | |
// Dynamically changed, however it is changed over in somewhere A, but then accessed | |
// later in somewhere B | |
// Example: | |
// you have exposed /api/config/your-flag to be mutated on one endpoint, | |
// and then you need to resolve the service over on /api/foos/your-service | |
// In which case the flag still has to persist in memory | |
builder.Services.AddSingleton(dynamicConfig); | |
builder.Services.AddSingleton<MarcoService>(); // Note here how we register the | |
builder.Services.AddSingleton<PolloService>(); // concrete types, not abstracts | |
builder.Services.AddTransient<IDynamicConfiguredService>(svcs => | |
{ | |
var config = svcs.GetRequiredService<DynamicConfiguredServiceConfig>(); | |
return config.Flag switch | |
{ | |
DynamicConfiguredServiceFlag.Marco => svcs.GetRequiredService<MarcoService>(), | |
DynamicConfiguredServiceFlag.Pollo => svcs.GetRequiredService<PolloService>(), | |
_ => throw new InvalidOperationException() | |
}; | |
}); | |
// Third option, Factory Pattern | |
// You use this approach for when the services flag is dynamically "chosen" | |
// in the exact same pipeline it is requested | |
// For example if you have you /api/do-the-thing/{strategy:FlaggedEnum} | |
// in which case you can just "select" the right service with a factory method | |
builder.Services.AddSingleton<ScopeConfiguredServiceFactory>(); | |
builder.Services.AddSingleton<HelloService>(); // Note once again here that we register | |
builder.Services.AddSingleton<WorldService>(); // the concrete types, not the abstract | |
var app = builder.Build(); | |
// Demonstrating resolution | |
// Statically configured service, there's no way to change which one we get here | |
// Without restarting the app to load new configs | |
var staticConfiguredService = app.Services.GetRequiredService<IStaticConfiguredService>(); | |
staticConfiguredService.Run(); | |
// Dynamically configured service | |
// watch how we can change the config dynamically to switch | |
// Which one we resolve to on the fly | |
var dynamicConfiguredServiceA = app.Services.GetRequiredService<IDynamicConfiguredService>(); | |
dynamicConfiguredServiceA.Run(); | |
// Now we change the config | |
dynamicConfig.Flag = DynamicConfiguredServiceFlag.Pollo; | |
var dynamicConfiguredServiceB = app.Services.GetRequiredService<IDynamicConfiguredService>(); | |
dynamicConfiguredServiceB.Run(); | |
// Factory selected service, AKA Strategy Pattern | |
var factory = app.Services.GetRequiredService<ScopeConfiguredServiceFactory>(); | |
var scopeConfiguredServiceA = factory.Resolve(ScopeRequestedServiceFlag.Hello); | |
scopeConfiguredServiceA.Run(); | |
var scopeConfiguredServiceB = factory.Resolve(ScopeRequestedServiceFlag.World); | |
scopeConfiguredServiceB.Run(); | |
// OUTPUTS: | |
// PingService | |
// MarcoService | |
// PolloService | |
// HelloService | |
// WorldService | |
namespace AbstractResolver | |
{ | |
public interface IStaticConfiguredService | |
{ | |
void Run(); | |
} | |
public enum StaticConfiguredServiceFlag | |
{ | |
Ping, | |
Pong | |
} | |
public class PingService : IStaticConfiguredService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(PingService)); | |
} | |
} | |
public class PongService : IStaticConfiguredService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(PongService)); | |
} | |
} | |
public interface IDynamicConfiguredService | |
{ | |
void Run(); | |
} | |
public enum DynamicConfiguredServiceFlag | |
{ | |
Marco, | |
Pollo | |
} | |
public class DynamicConfiguredServiceConfig | |
{ | |
public required DynamicConfiguredServiceFlag Flag { get; set; } | |
} | |
public class MarcoService : IDynamicConfiguredService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(MarcoService)); | |
} | |
} | |
public class PolloService : IDynamicConfiguredService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(PolloService)); | |
} | |
} | |
public interface IScopeRequestedService | |
{ | |
void Run(); | |
} | |
public enum ScopeRequestedServiceFlag | |
{ | |
Hello, | |
World | |
} | |
public class ScopeConfiguredServiceFactory( | |
HelloService helloService, | |
WorldService worldService | |
) | |
{ | |
public IScopeRequestedService Resolve(ScopeRequestedServiceFlag flag) | |
{ | |
return flag switch | |
{ | |
ScopeRequestedServiceFlag.Hello => helloService, | |
ScopeRequestedServiceFlag.World => worldService, | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
} | |
public class HelloService : IScopeRequestedService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(HelloService)); | |
} | |
} | |
public class WorldService : IScopeRequestedService | |
{ | |
public void Run() | |
{ | |
Console.WriteLine(nameof(WorldService)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment