How to handle async calls with Ninject InRequestScope?

冷暖自知 提交于 2019-12-05 05:55:51

There's is no way to explicitly prolong the lifetime of an object with .InRequestScope() to extend to after the request end.

If there's not a business requirement that the work during the request and @ callback must happen in a single transaction i would go for using two DbContext instances. One during the request and one during the callback. Note: As far as i know this also means you can't take an entity from the first context and update/save it in the second context. This means you must only pass identifier (and other data relevant to the operation) from request to callback. The callback has to "create" a new DbContext and retrieve the according entitites from the context.

Conditional Binding Alternative

As an alternative you might declare a special binding for this special case. Ninject supports so called contextual bindings. This means you would have two bindings, the standard binding and a contextual, special case binding:

Bind<DbContext>().ToSelf().InRequestScope();

Bind<DbContext>().ToSelf()
    .WhenInjectedInto<SomeController>();

Notice that the second binding does not specify a scope - that means SomeController is responsible to call .Dispose(). In your case that would mean the callback would have to dispose the context. You'd also need to dispose of the context in all errors cases (errors in the callback code, errors occurring before callback is triggered,....).

Also, in reality your application is probably a bite more complex and .WhenInjectedInto<SomeController> is not going to be enough/correct, because you might want to inject the same instance into the controller plus a repository plus a query object.. what not.

That means you will need scoping, but a scope different from .InRequestScope(). You might use .InCallScope() or named scope - both are included in the named scope extension.

Furthermore you would need to adapt the When condition. You could adapt it so to traverse the requests and see if there is FooController anywhere in the request chain. But that's not very performant, instead i would recommend using a ninject IParameter to specify that you want special case treatment. The parameter would be:

public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
    public bool Equals(IParameter other)
    {
        if (other == null)
        {
            return false;
        }

        return other is NonRequestScopedParameter;
    }

    public object GetValue(IContext context, ITarget target)
    {
        throw new NotSupportedException("this parameter does not provide a value");
    }

    public string Name
    {
        get { return typeof(NonRequestScopedParameter).Name; }
    }

    // this is very important
    public bool ShouldInherit
    {
        get { return true; }
    }
}

which would be applied at the bindings like:

kernel.Bind<SomeController>().ToSelf()
      .WithParameter(new NonRequestScopedParameter());

kernel.Bind<DbContext>().ToSelf()
       .When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
       .InCallScope(); // or whatever scope you're using
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!