You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Web API 2中使用StructureMap将请求数据注入依赖构造函数的疑问

Injecting Request-Specific Data with StructureMap.WebApi2

Great question! When working with StructureMap in WebApi2, injecting request-specific dynamic values like clientCode or bodyData into dependency constructors requires a bit of extra work because IoC containers typically handle static dependencies by default. Let's break down the best approaches for your scenario:

The simplest and most maintainable approach is to wrap your business logic in a service class, inject that service into your controller, and pass the dynamic request parameters directly to the service's methods. This avoids tying your dependencies to the HTTP context and keeps your code testable.

Step 1: Define a Service Interface and Implementation

First, extract your logic into a service:

public interface ISomeDataService
{
    SomeData CreateSomeData(string clientCode, MyDto bodyData);
}

public class SomeDataService : ISomeDataService
{
    private readonly IClientManager _clientManager;

    // Inject static dependencies (like IClientManager) via constructor
    public SomeDataService(IClientManager clientManager)
    {
        _clientManager = clientManager;
    }

    public SomeData CreateSomeData(string clientCode, MyDto bodyData)
    {
        var client = _clientManager.GetClientByCode(clientCode);
        return new SomeData
        {
            User = bodyData.User,
            ClientCode = clientCode,
            SomeField = client.SomeField
        };
    }
}

Step 2: Refactor Your Controller to Use Dependency Injection

Update your controller to inject the service instead of manually instantiating ClientManager:

public class MyController : ApiController
{
    private readonly ISomeDataService _someDataService;

    // Inject the service via constructor
    public MyController(ISomeDataService someDataService)
    {
        _someDataService = someDataService;
    }

    public IHttpActionResult MyAction(string clientCode, [FromBody]MyDto bodyData)
    {
        var someData = _someDataService.CreateSomeData(clientCode, bodyData);
        // Continue with your logic...
        return Ok(someData);
    }
}

Step 3: Register Dependencies in StructureMap

Add these registrations to your StructureMap registry:

public class WebApiRegistry : Registry
{
    public WebApiRegistry()
    {
        For<ISomeDataService>().Use<SomeDataService>();
        For<IClientManager>().Use<ClientManager>(); // Register your ClientManager here
    }
}

Approach 2: Factory Pattern (For Dependencies Needing Dynamic Parameters)

If your ClientManager (or another dependency) requires the clientCode directly in its constructor, use a factory pattern to create instances with the dynamic parameter at runtime. This keeps your dependencies decoupled from the HTTP context.

Step 1: Create a Factory Interface and Implementation

public interface IClientManagerFactory
{
    IClientManager Create(string clientCode);
}

public class ClientManagerFactory : IClientManagerFactory
{
    // Inject any static dependencies your ClientManager needs here
    private readonly IDatabaseService _dbService;

    public ClientManagerFactory(IDatabaseService dbService)
    {
        _dbService = dbService;
    }

    public IClientManager Create(string clientCode)
    {
        return new ClientManager(clientCode, _dbService); // Pass dynamic + static dependencies
    }
}

Step 2: Use the Factory in Your Controller

public class MyController : ApiController
{
    private readonly IClientManagerFactory _clientManagerFactory;

    public MyController(IClientManagerFactory clientManagerFactory)
    {
        _clientManagerFactory = clientManagerFactory;
    }

    public IHttpActionResult MyAction(string clientCode, [FromBody]MyDto bodyData)
    {
        var clientManager = _clientManagerFactory.Create(clientCode);
        var client = clientManager.GetClientByCode(clientCode);
        var someData = new SomeData
        {
            User = bodyData.User,
            ClientCode = clientCode,
            SomeField = client.SomeField
        };
        // Continue with your logic...
        return Ok(someData);
    }
}

Step 3: Register the Factory in StructureMap

public class WebApiRegistry : Registry
{
    public WebApiRegistry()
    {
        For<IClientManagerFactory>().Use<ClientManagerFactory>();
        For<IDatabaseService>().Use<DatabaseService>(); // Register static dependencies
    }
}

If you absolutely need to inject request-specific values directly into constructor parameters, you can store the values in the HTTP request properties and resolve them via StructureMap. However, this ties your dependencies to the HTTP context, making unit testing harder.

Step 1: Capture Request Data with an Action Filter

public class CaptureRequestDataFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // Store clientCode in request properties
        if (actionContext.ActionArguments.TryGetValue("clientCode", out var clientCode))
        {
            actionContext.Request.Properties["ClientCode"] = clientCode.ToString();
        }

        // Store bodyData if needed
        if (actionContext.ActionArguments.TryGetValue("bodyData", out var bodyData))
        {
            actionContext.Request.Properties["BodyData"] = bodyData as MyDto;
        }

        base.OnActionExecuting(actionContext);
    }
}

Register the filter in WebApiConfig.cs:

config.Filters.Add(new CaptureRequestDataFilter());

Step 2: Resolve Values in StructureMap

Create a helper to access the current request:

public static class HttpContextHelper
{
    public static HttpRequestMessage CurrentRequest =>
        HttpContext.Current?.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
}

Then register your dependency with the dynamic constructor parameter:

public class WebApiRegistry : Registry
{
    public WebApiRegistry()
    {
        For<IClientManager>().Use<ClientManager>()
            .LifecycleIs(new HttpRequestLifecycle()) // Ensure per-request instance
            .Ctor<string>("clientCode").Is(() =>
            {
                var request = HttpContextHelper.CurrentRequest;
                if (request?.Properties.TryGetValue("ClientCode", out var clientCode) ?? false)
                {
                    return clientCode.ToString();
                }
                throw new InvalidOperationException("ClientCode not found in request properties.");
            });
    }
}

Final Notes

  • Prefer Approach 1 or 2 over Approach 3: They keep your code cleaner, more testable, and decoupled from the HTTP context.
  • Always register request-scoped dependencies with HttpRequestLifecycle to avoid sharing instances across requests.
  • Don't forget to set up StructureMap as your WebApi dependency resolver in WebApiConfig.cs:
    var container = new Container(new WebApiRegistry());
    config.DependencyResolver = new StructureMapDependencyResolver(container);
    

内容的提问来源于stack exchange,提问作者Bruno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:09:21