diff --git a/.editorconfig b/.editorconfig
index c3fb77d..60b9c12 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -308,6 +308,7 @@ dotnet_diagnostic.CA2240.severity = warning
dotnet_diagnostic.CA2241.severity = warning
dotnet_diagnostic.CA2242.severity = warning
dotnet_diagnostic.CS1573.severity = suggestion
+dotnet_diagnostic.CS1587.severity = none
dotnet_diagnostic.CS1591.severity = none
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
diff --git a/samples/FeatureModulesSample.Module1/SampleModule1.cs b/samples/FeatureModulesSample.Module1/SampleModule1.cs
index d50b036..c8172bf 100644
--- a/samples/FeatureModulesSample.Module1/SampleModule1.cs
+++ b/samples/FeatureModulesSample.Module1/SampleModule1.cs
@@ -11,7 +11,7 @@ namespace FeatureModulesSample.Module1;
public class SampleModule1 : WebFeatureModule
{
- public override IModuleInfo? ModuleInfo { get; } = new FeatureModuleInfo("SampleModule1", "1.0.0");
+ public override IModuleInfo ModuleInfo { get; } = new FeatureModuleInfo("SampleModule1", "1.0.0");
public override void MapEndpoints(WebApplication app)
{
diff --git a/samples/FeatureModulesSample/FeatureModule.cs b/samples/FeatureModulesSample/FeatureModule.cs
index f0e16c5..b240947 100644
--- a/samples/FeatureModulesSample/FeatureModule.cs
+++ b/samples/FeatureModulesSample/FeatureModule.cs
@@ -4,10 +4,5 @@ namespace FeatureModulesSample;
public class FeatureModule : Infinity.Toolkit.FeatureModules.FeatureModule
{
- public override IModuleInfo? ModuleInfo { get; } = new FeatureModuleInfo("FeatureModule", "1.0.0");
-
- public override ModuleContext RegisterModule(ModuleContext moduleContext)
- {
- return base.RegisterModule(moduleContext);
- }
+ public override IModuleInfo ModuleInfo { get; } = new FeatureModuleInfo("FeatureModule", "1.0.0");
}
diff --git a/samples/FeatureModulesSample/WeatherModule.cs b/samples/FeatureModulesSample/WeatherModule.cs
index 4ed90ef..88f33ca 100644
--- a/samples/FeatureModulesSample/WeatherModule.cs
+++ b/samples/FeatureModulesSample/WeatherModule.cs
@@ -4,7 +4,7 @@ namespace FeatureModulesSample;
internal class WeatherModule : WebFeatureModule
{
- public override IModuleInfo? ModuleInfo { get; } = new FeatureModuleInfo("WeatherModule", "1.0.0");
+ public override IModuleInfo ModuleInfo { get; } = new FeatureModuleInfo("WeatherModule", "1.0.0");
public override void MapEndpoints(WebApplication builder)
{
diff --git a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs
index dd1ef97..a9f540e 100644
--- a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs
+++ b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs
@@ -114,6 +114,19 @@ public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostA
builder.Configuration.GetSection(configSectionName).Bind(settings);
configureSettings?.Invoke(settings);
+ // Make sure we have a connection string or endpoint before adding Azure App Configuration
+ if (builder.Configuration.GetConnectionString("AzureAppConfig") is null && settings.Endpoint is null && Environment.GetEnvironmentVariable("AZURE_APP_CONFIG_ENDPOINT") is null)
+ {
+ logger.LogWarning(message: $"""
+ Azure App Configuration is not configured.
+ Please provide a valid connection string or endpoint using one of the following sources:
+ - 'ConnectionStrings:AzureAppConfig' (connection string)
+ - '{configSectionName}:Endpoint' configuration section
+ - 'AZURE_APP_CONFIG_ENDPOINT' environment variable
+ """);
+ return builder;
+ }
+
builder.Configuration.AddAzureAppConfiguration(options =>
{
if (builder.Configuration.GetConnectionString("AzureAppConfig") is string connectionString)
@@ -134,15 +147,7 @@ public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostA
var envEndpoint = Environment.GetEnvironmentVariable("AZURE_APP_CONFIG_ENDPOINT");
if (!Uri.TryCreate(envEndpoint, UriKind.Absolute, out endpointUri))
{
- // Log instead of throwing
- logger?.LogError(message: $"""
- Unable to find a valid Azure App Configuration endpoint.
- Please provide a valid endpoint using one of the following sources:
- - 'ConnectionStrings:AzureAppConfig' (connection string)
- - '${configSectionName}:Endpoint' configuration section
- - 'AZURE_APP_CONFIG_ENDPOINT' environment variable
- """);
- return;
+ logger?.LogWarning("Found invalid Azure App Configuration endpoint in environment variable 'AZURE_APP_CONFIG_ENDPOINT': {EnvEndpoint}", envEndpoint);
}
}
diff --git a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj
index 1bc1e67..bb52831 100644
--- a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj
+++ b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj
@@ -5,7 +5,7 @@
enable
enable
Infinity.Toolkit.Azure
- 1.0.1
+ 1.0.2
diff --git a/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs b/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs
index 251a724..3aecf64 100644
--- a/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs
+++ b/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs
@@ -5,9 +5,9 @@
///
public abstract class FeatureModule : IFeatureModule
{
- public abstract IModuleInfo? ModuleInfo { get; }
+ public abstract IModuleInfo ModuleInfo { get; }
- public virtual ModuleContext RegisterModule(ModuleContext moduleContext) => moduleContext;
+ public virtual void RegisterModule(IHostApplicationBuilder builder) { }
}
///
@@ -16,7 +16,7 @@ public abstract class FeatureModule : IFeatureModule
///
public abstract class WebFeatureModule : IWebFeatureModule
{
- public abstract IModuleInfo? ModuleInfo { get; }
+ public abstract IModuleInfo ModuleInfo { get; }
public virtual void RegisterModule(IHostApplicationBuilder builder) { }
diff --git a/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs b/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs
index b97420f..6eb210b 100644
--- a/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs
+++ b/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs
@@ -6,7 +6,7 @@ public interface IFeatureModuleBase
///
/// Gets the meta data that describes the module such as name and version.
///
- IModuleInfo? ModuleInfo { get; }
+ IModuleInfo ModuleInfo { get; }
}
///
@@ -22,7 +22,7 @@ public interface IFeatureModule : IFeatureModuleBase
///
/// Register all dependencies needed by a module in the DI-container.
///
- ModuleContext RegisterModule(ModuleContext moduleContext);
+ void RegisterModule(IHostApplicationBuilder builder);
}
///
@@ -33,15 +33,10 @@ public interface IFeatureModule : IFeatureModuleBase
/// module to independently register services and endpoints. This facilitates separation of concerns and improves
/// maintainability in large applications. Modules should ensure that all required services are registered before
/// mapping endpoints.
-public interface IWebFeatureModule : IFeatureModuleBase
+public interface IWebFeatureModule : IFeatureModule
{
///
/// Maps all endpoints provided by the module in the DI-container.
///
void MapEndpoints(WebApplication app);
-
- ///
- /// Register all dependencies needed by a web module in the DI-container.
- ///
- void RegisterModule(IHostApplicationBuilder builder);
}
diff --git a/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs b/src/Infinity.Toolkit.FeatureModules/IHostApplicationBuilderExtensions.cs
similarity index 59%
rename from src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs
rename to src/Infinity.Toolkit.FeatureModules/IHostApplicationBuilderExtensions.cs
index 105e423..30e2b3e 100644
--- a/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs
+++ b/src/Infinity.Toolkit.FeatureModules/IHostApplicationBuilderExtensions.cs
@@ -1,17 +1,18 @@
using Infinity.Toolkit.LogFormatter;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.DependencyModel;
namespace Infinity.Toolkit.FeatureModules;
-public static class WebApplicationBuilderExtensions
+public static class IHostApplicationBuilderExtensions
{
private const string FeatureModulesConfigKey = "FeatureModules";
///
/// Add all feature modules that are found in the solution.
///
- public static WebApplicationBuilder AddFeatureModules(
- this WebApplicationBuilder builder,
+ public static IHostApplicationBuilder AddFeatureModules(
+ this IHostApplicationBuilder builder,
Action configure,
string configKey = FeatureModulesConfigKey,
ILoggerFactory? loggerFactory = null)
@@ -26,8 +27,8 @@ public static WebApplicationBuilder AddFeatureModules(
///
/// Add all feature modules that are found in the solution.
///
- public static WebApplicationBuilder AddFeatureModules(
- this WebApplicationBuilder builder,
+ public static IHostApplicationBuilder AddFeatureModules(
+ this IHostApplicationBuilder builder,
IConfiguration config)
{
var options = new FeatureModuleOptions();
@@ -38,12 +39,12 @@ public static WebApplicationBuilder AddFeatureModules(
///
/// Add all feature modules that are found in the solution.
///
- public static WebApplicationBuilder AddFeatureModules(this WebApplicationBuilder builder)
+ public static IHostApplicationBuilder AddFeatureModules(this IHostApplicationBuilder builder)
{
return builder.AddFeatureModules(options => { });
}
- internal static WebApplicationBuilder RegisterFeatureModules(this WebApplicationBuilder builder, FeatureModuleOptions options, ILoggerFactory? loggerFactory)
+ internal static IHostApplicationBuilder RegisterFeatureModules(this IHostApplicationBuilder builder, FeatureModuleOptions options, ILoggerFactory? loggerFactory)
{
loggerFactory ??= LoggerFactory.Create(loggingBuilder =>
{
@@ -60,19 +61,61 @@ internal static WebApplicationBuilder RegisterFeatureModules(this WebApplication
{
logger?.LogDebug(new EventId(1000, "Scanning"), "Scanning assemblies for feature modules...");
- var discoveredModules = ModuleUtilities.DiscoverModules(options, logger);
+ var discoveredModules = DiscoverModules(options, logger);
RegisterModules(discoveredModules, builder, logger);
logger?.LogDebug(new EventId(1003, "ScanningComplete"), "Registering feature modules completed.");
}
catch (Exception ex)
{
- logger.LogError(new EventId(5000, "ScanningFailed"), "Failed to register feature modules. {ex}", ex.Message);
+ logger?.LogError(new EventId(5000, "ScanningFailed"), "Failed to register feature modules. {ex}", ex.Message);
}
return builder;
}
+ ///
+ /// Discover all modules that references IFeatureModule.
+ ///
+ /// A list of all feature modules in the solution.
+ private static IEnumerable DiscoverModules(FeatureModuleOptions options, ILogger? logger)
+ {
+ var assemblies = new HashSet
+ {
+ typeof(Assembly).Assembly,
+ };
+
+ var entryAssembly = Assembly.GetEntryAssembly();
+ var context = DependencyContext.Load(entryAssembly!)!;
+
+ foreach (var assembly in context.RuntimeLibraries)
+ {
+ if (IsReferencingCurrentAssembly(assembly, typeof(IHostApplicationBuilderExtensions).Assembly.GetName().Name))
+ {
+ foreach (var assemblyName in assembly.GetDefaultAssemblyNames(context))
+ {
+ assemblies.Add(Assembly.Load(assemblyName));
+ }
+ }
+ }
+
+ var typesAssignableTo = assemblies
+ .SelectMany(x =>
+ x.DefinedTypes
+ .Where(type => type is { IsAbstract: false, IsInterface: false } &&
+ type.IsAssignableTo(typeof(IFeatureModuleBase)) &&
+ !options.ExcludedModules.Any(t => t == type.FullName)))
+ .OrderBy(c => c.FullName);
+
+ logger?.LogInformation(new EventId(1001, "ModulesFound"), "Found {moduleCount} feature modules.", typesAssignableTo.Count());
+ return typesAssignableTo;
+ }
+
+ private static bool IsReferencingCurrentAssembly(Library library, string? currentAssemblyName)
+ {
+ return library.Dependencies.Any(dependency => dependency.Name.Equals(currentAssemblyName));
+ }
+
///
/// Register all classes implementing IFeatureModule while scanning the project to IServiceCollection.
///
@@ -99,20 +142,10 @@ private static void RegisterModules(IEnumerable discoveredModules, IHo
{
registeredFeatureModules.Add(module.GetType(), module);
- if (module is IWebFeatureModule webModule)
+ if (module is IFeatureModule featureModule)
{
- logger?.LogInformation(new EventId(1002, "RegisteringModules"), "Registering web feature module: {module}", module.GetType().FullName);
- webModule.RegisterModule(builder);
- }
- else if (module is IFeatureModule featureModule)
- {
- logger?.LogInformation(new EventId(1002, "RegisteringModules"), "Registering feature module: {module}", module.GetType().FullName);
- featureModule?.RegisterModule(new()
- {
- Configuration = builder.Configuration,
- Environment = builder.Environment,
- Services = builder.Services
- });
+ logger?.LogInformation(new EventId(1002, "RegisteringModules"), "Registering feature module: {module} - v{version}", module.ModuleInfo?.Name ?? module.GetType().FullName, module.ModuleInfo?.Version ?? "1.0");
+ featureModule?.RegisterModule(builder);
}
else
{
@@ -125,5 +158,4 @@ private static void RegisterModules(IEnumerable discoveredModules, IHo
options.AdditionalAssemblies.AddRange([.. registeredFeatureModules.Keys.Select(x => x.Assembly)]);
});
}
-
}
diff --git a/src/Infinity.Toolkit.FeatureModules/IModuleInfo.cs b/src/Infinity.Toolkit.FeatureModules/IModuleInfo.cs
index a11a754..81585ee 100644
--- a/src/Infinity.Toolkit.FeatureModules/IModuleInfo.cs
+++ b/src/Infinity.Toolkit.FeatureModules/IModuleInfo.cs
@@ -11,5 +11,5 @@ public class FeatureModuleInfo(string? name, string? version) : IModuleInfo
{
public string? Name { get; init; } = name;
- public string? Version { get; init; } = version;
+ public string? Version { get; init; } = version ?? "1.0.0";
}
diff --git a/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj b/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj
index 12ca6b8..b7f6fc9 100644
--- a/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj
+++ b/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj
@@ -4,7 +4,7 @@
net10.0;net9.0;net8.0
enable
enable
- 1.3.0
+ 1.4.0
Infinity.Toolkit.FeatureModules
Infinity Toolkit Feature Modules let's you automatically register dependencies and endpoints in modules which simplifies development when you are working with vertical feature slices.
FeatureModules;VerticalSliceArchitecture;ModularMonoliths;MinimalApis
diff --git a/src/Infinity.Toolkit.FeatureModules/ModuleContext.cs b/src/Infinity.Toolkit.FeatureModules/ModuleContext.cs
deleted file mode 100644
index 9716bcd..0000000
--- a/src/Infinity.Toolkit.FeatureModules/ModuleContext.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Infinity.Toolkit.FeatureModules;
-
-public sealed record ModuleContext
-{
- ///
- /// Gets the host environment.
- ///
- public required IHostEnvironment Environment { get; init; }
-
- ///
- /// Gets the service collection.
- ///
- public required IServiceCollection Services { get; init; }
-
- ///
- /// Gets the configuration.
- ///
- public required IConfiguration Configuration { get; init; }
-}
diff --git a/src/Infinity.Toolkit.FeatureModules/ModuleUtilities.cs b/src/Infinity.Toolkit.FeatureModules/ModuleUtilities.cs
deleted file mode 100644
index f6826ca..0000000
--- a/src/Infinity.Toolkit.FeatureModules/ModuleUtilities.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Microsoft.Extensions.DependencyModel;
-
-namespace Infinity.Toolkit.FeatureModules;
-
-internal static class ModuleUtilities
-{
-
- ///
- /// Discover all modules that references IFeatureModule.
- ///
- /// A list of all feature modules in the solution.
- public static IEnumerable DiscoverModules(FeatureModuleOptions options, ILogger? logger)
- {
- var assemblies = new HashSet
- {
- typeof(Assembly).Assembly,
- };
-
- var entryAssembly = Assembly.GetEntryAssembly();
- var context = DependencyContext.Load(entryAssembly!)!;
-
- foreach (var assembly in context.RuntimeLibraries)
- {
- if (IsReferencingCurrentAssembly(assembly, typeof(WebApplicationBuilderExtensions).Assembly.GetName().Name))
- {
- foreach (var assemblyName in assembly.GetDefaultAssemblyNames(context))
- {
- assemblies.Add(Assembly.Load(assemblyName));
- }
- }
- }
-
- var typesAssignableTo = assemblies
- .SelectMany(x =>
- x.DefinedTypes
- .Where(type => type is { IsAbstract: false, IsInterface: false } &&
- type.IsAssignableTo(typeof(IFeatureModuleBase)) &&
- !options.ExcludedModules.Any(t => t == type.FullName)));
-
- logger?.LogInformation(new EventId(1001, "ModulesFound"), "Found {moduleCount} feature modules.", typesAssignableTo.Count());
- return typesAssignableTo;
- }
-
- private static bool IsReferencingCurrentAssembly(Library library, string? currentAssemblyName)
- {
- return library.Dependencies.Any(dependency => dependency.Name.Equals(currentAssemblyName));
- }
-}