Dependency Provider Guide for MIB Consumers
This document explains how a consumer project can register custom services for the MIB FrontEnd server and how those services are resolved when MIB creates custom components through reflection.
Current scope
The current implementation covers:
- custom components created through reflection by MIB
- plugins/custom behaviors that are instantiated through the existing plugin loader flow and now reuse the same request scope for dependency resolution
It does not yet cover:
- custom repositories loaded through
RepositoryFactory - every reflection-based activation point in the solution
Why this exists
MIB creates several component instances internally. Because the consumer project does not control that new operation, constructor injection would normally not work for those components.
The dependency provider feature adds a registration point on the MIB side and allows MIB to resolve constructor dependencies when it creates a component dynamically.
Recommended consumer architecture
The recommended setup is:
- a host web project that references
MediaiBox.Cms.FrontEnd.Server - one or more class libraries for:
- custom components
- custom services
- shared contracts
Even if the consumer does not expose its own business Web API, it should still host MIB in an ASP.NET Core web project so the MIB server can run inside the normal HTTP pipeline.
Public entry point
Consumers register services through:
MediaiBox.Cms.FrontEnd.Server.DependencyInjection.MibServerDependencyBootstrap.ConfigureServices(...)
This registration is applied when the MIB server configures its internal service collection.
Registration API
The registration callback receives an IMibDependencyRegistrar.
Currently supported methods:
AddSingleton<TService, TImplementation>()AddSingleton<TService>(instance)AddScoped<TService, TImplementation>()AddScoped<TService>()AddTransient<TService, TImplementation>()AddTransient<TService>()AddHttpClient<TClient>()AddHttpClient<TClient>(configureClient)AddHttpClient<TClient, TImplementation>()AddHttpClient<TClient, TImplementation>(configureClient)
Example: consumer host project
using MediaiBox.Cms.FrontEnd.Server.DependencyInjection;
using MyProject.Services;
MibServerDependencyBootstrap.ConfigureServices(services =>
{
services.AddScoped<IMyTitleService, MyTitleService>();
services.AddSingleton<IMyClock>(new MyClock());
services.AddHttpClient<IMyApiClient, MyApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(15);
});
});
The important point is that this configuration must run before the MIB host starts processing requests.
Example: custom service
using System.Net.Http;
public interface IMyTitleService
{
string GetTitle();
}
public class MyTitleService : IMyTitleService
{
private readonly HttpClient _httpClient;
public MyTitleService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string GetTitle() => _httpClient.BaseAddress?.Host ?? "Injected title from consumer";
}
Example: custom component with constructor injection
using MediaiBox.Cms.Api.Client.Model;
using MediaiBox.Cms.FrontEnd.Model.Mvc.UI.Component;
using MediaiBox.Cms.FrontEnd.Model.UI.Component;
using MediaiBox.Cms.FrontEnd.Model.UI.Context;
using MediaiBox.Cms.FrontEnd.Model.Workflow;
using Microsoft.AspNetCore.Http;
public class MyComponent : IRenderableComponent<ComponentConfiguration, object>
{
private readonly IMyTitleService _titleService;
public MyComponent(IMyTitleService titleService)
{
_titleService = titleService;
}
public ContextViewData Context { get; set; }
public ComponentConfiguration Configuration { get; set; }
public ComponentDisplayMode DisplayMode => ComponentDisplayMode.Edit;
public Task Initialize(
IMibApiClientLibrary apiClientLibrary,
IPublicWorkflowFactory workflowFactory,
IHttpContextAccessor httpContextAccessor)
{
return Task.CompletedTask;
}
public IInformationRequest GetInformationRequests() => null;
public ComponentPermission GetPermissions(List<ResponseItemContent> responses)
{
return new ComponentPermission();
}
public Task<ComponentRenderData<object>> GetRenderData(
List<ResponseItemContent> responseItemContents,
CancellationToken cancellationToken)
{
return Task.FromResult(new ComponentRenderData<object>
{
Title = _titleService.GetTitle()
});
}
public List<string> GetScripts() => [];
public List<string> GetStyles() => [];
}
Optional access to the MIB dependency resolver
MIB also supports injecting:
MediaiBox.Cms.FrontEnd.Model.Mvc.DependencyInjection.IMibDependencyResolver
This is optional. Use it only when the component really needs dynamic or optional resolution.
Example:
public class MyComponent
{
private readonly IMyTitleService _titleService;
private readonly IMibDependencyResolver _resolver;
public MyComponent(IMyTitleService titleService, IMibDependencyResolver resolver)
{
_titleService = titleService;
_resolver = resolver;
}
}
For most components, constructor injection of the required service types is enough.
How MIB resolves dependencies
When MIB creates a component through reflection, it now follows this strategy:
- inspect the available public constructors
- try the constructors with the highest parameter count first
- for each constructor parameter:
- if the parameter type is
IMibDependencyResolver, MIB injects its resolver wrapper - otherwise MIB tries to resolve the parameter from the current request service provider
- if the parameter type is
- if all parameters of one constructor can be resolved, MIB uses that constructor
- if no constructor can be resolved, MIB falls back to the parameterless constructor
This also means typed clients registered through AddHttpClient(...) can be injected into services or components, as long as those services are available in the current request scope.
Where the services come from at runtime
Resolution is based on:
HttpContext.RequestServices
That means component dependencies are resolved from the current request scope.
This is important because:
Scopedservices behave as expected per request- MIB does not use a detached global container for reflected components
- the behavior stays aligned with the ASP.NET Core dependency injection model
Plugin and custom behavior support
The same request scope is now also available to plugin-based flows such as:
- image URL resolver plugins
- custom validators
- custom business rules
This was implemented without changing plugin public contracts.
In practice, plugin loaders still keep their own built-in registrations (for example IMibLog and MIB API wrappers), but unresolved constructor dependencies may now be resolved from the same request scope used by components.
This means a plugin can keep its current interface contract and still receive consumer-registered services through constructor injection, as long as those services are available in the current request scope.
Backward compatibility
Existing components remain compatible.
If a component has a parameterless constructor, and MIB cannot resolve a dependency-aware constructor, it falls back to the old behavior.
So this feature is additive:
- old components continue to work
- new components may opt into constructor injection
Current limitations
At this stage:
- reflected components are covered
- plugin/custom behavior constructors can resolve services from the same request scope
- repository overrides are still using the existing constructor flow
Practical recommendation
For new custom components:
- keep constructor dependencies explicit
- inject only the services you actually need
- use
IMibDependencyResolveronly for advanced or optional resolution - keep the parameterless constructor only if you still want an easy fallback path
Summary
With the current implementation, a consumer can:
- host MIB in its own ASP.NET Core host project
- register services through
MibServerDependencyBootstrap.ConfigureServices(...) - register standard services and typed clients through
AddHttpClient(...) - create custom components that receive consumer-defined services through constructor injection
- allow plugins/custom behaviors to resolve consumer-registered services from the same request scope without changing plugin contracts
- rely on request-scoped resolution and backward-compatible fallback behavior