如何通过Autofac基于请求参数注入不同对象及带参Database Context?
Absolutely! Both of your requirements are totally achievable with Autofac—let's break down how to implement each one clearly:
Autofac’s factory method resolution is perfect for this scenario—it lets you dynamically pick implementations based on incoming request data. First, make sure you can access the current request context (we’ll use ASP.NET Core’s IHttpContextAccessor as an example):
Step 1: Define Your Service Interface and Implementations
public interface IFeatureService { string ProcessRequest(); } public class MobileFeatureService : IFeatureService { public string ProcessRequest() => "Handling mobile-specific logic"; } public class DesktopFeatureService : IFeatureService { public string ProcessRequest() => "Handling desktop-specific logic"; }
Step 2: Register Services with Dynamic Resolution
First, register IHttpContextAccessor to access request details, then use a factory method to resolve the correct IFeatureService based on a request parameter:
var builder = new ContainerBuilder(); // Register HttpContextAccessor to access request data builder.RegisterType<HttpContextAccessor>() .As<IHttpContextAccessor>() .SingleInstance(); // Register the dynamic service resolver builder.Register<IFeatureService>((context, parameters) => { var httpContext = context.Resolve<IHttpContextAccessor>().HttpContext; var deviceType = httpContext.Request.Query["deviceType"].ToString().ToLower(); return deviceType switch { "mobile" => context.Resolve<MobileFeatureService>(), "desktop" => context.Resolve<DesktopFeatureService>(), _ => throw new ArgumentException("Invalid device type parameter") }; }).InstancePerRequest(); // Match request lifetime
Now, whenever IFeatureService is injected, Autofac will automatically pick the right implementation based on the deviceType query parameter.
For a DbContext that relies on request-specific data (like a tenant ID or connection string), you can use the same factory method pattern to inject the required parameter at resolution time:
Step 1: Define Your Parameterized DbContext
public class TenantDbContext : DbContext { private readonly string _tenantConnectionString; public TenantDbContext(string tenantConnectionString) { _tenantConnectionString = tenantConnectionString; } // Configure DbContext options using the connection string protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_tenantConnectionString); } // DbSet declarations here public DbSet<User> Users { get; set; } }
Step 2: Register the DbContext with Request-Driven Parameters
Use a factory method to pull the required parameter (e.g., a tenant ID from request headers) and instantiate the DbContext with it:
builder.Register<TenantDbContext>((context, parameters) => { var httpContext = context.Resolve<IHttpContextAccessor>().HttpContext; var tenantId = httpContext.Request.Headers["X-Tenant-Id"].ToString(); // Fetch the connection string based on the tenant ID (replace with your logic) var connectionString = GetTenantConnectionString(tenantId); return new TenantDbContext(connectionString); }).InstancePerRequest(); // Critical: DbContext should be scoped to the request
Key Notes
- Lifetime Management: Always register your DbContext with a per-request lifetime (
InstancePerRequest()orInstancePerLifetimeScope()in ASP.NET Core) to avoid concurrency issues. - Error Handling: Add validation for missing request parameters (e.g., if
X-Tenant-Idis missing) to prevent runtime failures. - Non-ASP.NET Scenarios: If you’re not using ASP.NET, pass request parameters via a custom scope object that you register in Autofac’s lifetime scope.
Both approaches leverage Autofac’s flexible resolution system to tie service creation directly to request data, keeping your code aligned with dependency injection principles while handling dynamic requirements.
内容的提问来源于stack exchange,提问作者Tomek Krupa

