1

Is there a way to define a scope for a specific lifestyle? I am attempting to implement my own scope that I want to persist across an application, but internally I also create another scope, and then a request to GetInstance returns the inner scoped instance instead.

I thought if I could define my lifestyle as:

public class MyScopedLifestyle : ExecutionContextScopeLifestyle
{
    public MyScopedLifestyle(bool disposeInstanceWhenScopeEnds)
        : base("MyScopedLifestyle", disposeInstanceWhenScopeEnds)
    {
    }

    protected override int Length
    {
        get
        {
            return 100;
        }
    }
}

And my usage is:

var container = new Container();
container.Register<IRequestData, RequestData>(new MyScopedLifestyle());

// i had hoped I could say
//   container.BeginExecutionContextScope(MyScopedLifestyle)
// or something similar
// this is controlled by me
using (var scope1 = container.BeginExecutionContextScope())
{
    // do some stuff
    container.GetInstance<IRequestData>().RequestMarket = "en-US";

    // this is done via the webapi execution scope (using simpleinjector dependency resolver)
    using (var scope2 = container.BeginExecutionContextScope())
    {
        Assert.Equal("en-US", container.GetInstance<IRequestData>().RequestMarket); // false
    }
}

But I'm unsure how to utilize my custom lifestyle when creating the inner execution scope.

What I really want to happen, is that my instance of IRequestData used in scope1, is the same instance of IRequestData inside of scope2. Is this something I can achieve with SimpleInjector?

Edit I removed the fact that I'm attempting to create an instance of an object per OWIN request, rather than per WebAPI request. Ideally I'm attempting to create:

container.RegisterOwinRequest<IRequestData, RequestData>();

So that when I resolve IFoo anywhere within my pipeline (be it an OWIN middleware, or in the WebAPI part, the same instance is returned for a particular request).

Edit 2 Swapped our IFoo/Foo/MyProperty for better names.

Steven
  • 166,672
  • 24
  • 332
  • 435
tris
  • 1,780
  • 3
  • 18
  • 28
  • Sooo.... you want `Foo` to have a singleton lifestyle or I'm I misinterpreting your question? You can register it as `Lifestyle.Singleton`. Persisting instances across applications is not something you should typically do, especially not with services. – Steven Aug 26 '14 at 14:27
  • No, sorry. I attempted to make the question simpler, but in the process I guess I removed that part of it. I'm after a per request type object, but within the notion of an 'OwinRequest'. Basically I have a middleware that I want to set a property on this per request object, and then utilize it within the webapi section of my code. Ideally what I was attempting to create, what the ability to `container.RegisterPerOwinRequest()` – tris Aug 26 '14 at 14:33
  • Don't you need a [hybrid](https://simpleinjector.codeplex.com/wikipage?title=ObjectLifestyleManagement#Hybrid) lifestyle than? – Steven Aug 26 '14 at 16:14
  • That looks like it could work, but how does it get me around the different instance per ExecutionContext? If my hybrid is a `WebApiRequestLifestyle`/`ExecutionContextScopeLifestyle`, then a different instance would be returned in `scope1` vs `scope2`. – tris Aug 26 '14 at 21:30
  • Does your `scope1` wrap multiple Web API requests or does it always wrap just one `scope2`? Can you update your question and change the rather abstract `IFoo`, `Foo`, `MyProperty` and `foo` with the naming of things for your specific case. I'm hoping this gets more insight of what you're trying to achieve to get a better picture and giving you better advice. – Steven Aug 27 '14 at 08:29
  • Correct, `scope1` would be created per OWIN request, which would only contain a single WebAPI request (i.e. `scope2` in my example). Updated the names in the example to better indicate my goal. In the meantime, I've gone with a `DelegatingHandler` approach (using a proxy to allow WebApiRequest lifestyle resolution), and this works ok. I was just trying to understand if it was possible to create a 'per owin request' to replace the 'per webapi request'. – tris Aug 27 '14 at 12:32

1 Answers1

2

What you're trying to accomplish with a custom lifestyle is absolutely possible, but might not be that easy, because you will have to store that scope somewhere (probably in the CallContext) and need to create a BeginMyCustomScope method that creates a new scope and have a custom Scope implementation that removes itself from the CallContext when Dispose is called. I think this is too much work and too much complexity.

The problem exists because during the time you want to set the RequestMarket property, there is no Web API request scope started. The way to usually force such scope to be started is to call the GetDependencyScope method on the HttpRequestMessage:

message.GetDependencyScope();

Right after that you can resolve the IRequestData and it works as expected:

container.GetInstance<IRequestData>().RequestMarket = "en-US";

I'm unsure however, whether the HttpRequestMessage is available at that point in time, so if not, I think working with a DelegatingHandler as you expressed in the comments is a good alternative.

In the past, a good way to communicate data across the callstack was using thread-specific variables, but that obviously fails when using async/await models as with Web API and OWIN. So the new way to do this is using the CallContext. So instead of using a DelegatingHandler, you might be able to do something like this:

container.RegisterInitializer<IRequestData>(data =>
    data.RequestMarket = (string)CallContext.LogicalGetData("RequestMarketKey"));

And when the OWIN request starts, you do this:

CallContext.LogicalSetData("RequestMarketKey", "en-US");
Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thanks, Steven. I'll stick with the `DelegatingHandler` approach for now, as I don't want to take the dependency on `CallContext` (just yet). As for the amount of work required to implement the custom lifestyle, this seems like a general request (`RegisterOwinRequest`, especially with the push towards OWIN), so I wonder if it would be worthwhile opening an issue for voting on the SimpleInjector site, and see where that goes. – tris Aug 28 '14 at 07:01
  • @Tris: Feel free to open the request on the project site. – Steven Aug 28 '14 at 07:49