0

I have a web api 2 controller: TestController.cs and an action filter: TestAuthorizeAttribute.cs

I am using StructureMap.WebApi2 nuget package for Web API 2 project for setting the dependency injection.

I am trying to create instance of TestService object in both TestController.cs and TestAuthorizeAttribute.cs.

Is this the correct approach to create instance of TestService.

Is it possible that the multiple threads seem to refer to Web API handling multiple simultaneous requests that are somehow handled by the same DataContext

Please help me to know are there any issues with the below mentioned code.

[RoutePrefix("api/test")]
public class TestController : ApiController
{

public TestController(ITestService testService)

    {
        _testService = testService;            
    }

    /// <summary>
    /// Get details of individual test
    /// </summary>
    /// <param name="Id"> Id</param>
    /// <param name="selectedSection">Selected Section</param>
    /// <returns>Details of the Test</returns>
    [Route("{Id:int}/{selectedSection?}", Name = "TestDetails")]
    [HttpGet]
    [TestAuthorize]
    public HttpResponseMessage Get(int Id, string selectedSection = "")
    {
        var testDetails = _testService.GetTestResults(Id);

        if (scan != null)
        {
            var progress = _testService.GetProgress(scan, user);


            return Request.CreateResponse(HttpStatusCode.OK, scanDetails);
        }
        else
        {
            return Request.CreateResponse(HttpStatusCode.NotFound, new { error = GlobalConstants.ERROR_REVIEW_NOTFOUND });
        }
    }
}


[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class TestAuthorizeAttribute : ActionFilterAttribute
{

      ITestService testService;

    public ScanAuthorizeAttribute()
    {

    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {        
        _testService = actionContext.Request.GetDependencyScope().GetService(typeof(ITestService)) as ITestService;

     var Id = Convert.ToInt32(actionContext.ActionArguments["Id"]);
         var testDetails = _testService.GetTestResults(Id);

    }
santosh kumar patro
  • 7,231
  • 22
  • 71
  • 143

1 Answers1

0

What you're doing looks pretty much spot on. This is pretty much exactly what you want to do.

A few things to note:

  1. Assuming the ITestService is TransientScoped (the default) your Filter and your Controller will actually use the same instance of the ITestService.
  2. If your DataContext is TransientScoped it will be unique per request (since the NestedContainer is stored on the request via the DependencyScope) so you should not see a race condition you were worried about.
  3. There are a few caveats to this that I know of. One of which is ModelBinders and ModelBinderProviders are instantiated with this method from System.Web.Http.ModelBinding.ModelBinderAttribute:

    private static object GetOrInstantiate(HttpConfiguration configuration, Type type)
    {
    return configuration.DependencyResolver.GetService(type) ?? Activator.CreateInstance(type);
    }
    

This method does NOT use a DependencyScope so any DBContext used here will be completely unique (TransientScoped object from non-Nested container.)

  1. I have seen really weird race conditions when dealing with IEnumerable dependencies in webapi. The default IEnumerables used by StructureMap do not appear to be thread safe.
  2. Be careful with data access in Filters, Delegating Handlers, Model Binders, etc. Introducing a N+1 or otherwise costly query in these layers can really hurt since they get called for every request they handle.

Any other questions about this stuff please ask, I've done a ton of stuff in this area lately so I have a pretty decent understanding of all of it.

JCalder
  • 391
  • 3
  • 8