Conditional binding in WebApi2 controller method - c#

I am using Ninject with the following packages:
Ninject
Ninject.MVC5
Ninject.Web.Common (and Common.WebHost)
Ninject.Web.WebApi (and WebApi.WebHost)
I have a WebApi2 Controller that looks like the below. My Get() method must be performant and it doesn't depend on the value of IMyFooService, thus I don't care if it gets injected or not when Get() is requested.
Question:
Is there a way for me to selectively bind interfaces only if certain api methods are called? Whether through using attributes or...?
public class FooController : ApiController {
public IMyFooService fooService;
public FooController(IMyFooService fooService) {
this.fooService = fooService;
}
[NonDependent] // Don't really care about the value of fooService
public JsonResult Get() {}
[Dependent] // Must have valid dependency injection
public async Task<JsonResult> Post([FromBody] IList foos) {
var didMyFoo = this.fooService.DoTheFoo();
}
}
Here is my NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMyFooService>().To<MyConcreteService>().InRequestScope();
}
I noticed that To<T>() has many .When() options. Perhaps I can make use of this to say .When(/* Controller = Foo, Action = Post */).

The easiest, and probably most succinct, way is to use Lazy<T> which was made exactly for this use case - quoting from the docs:
Use lazy initialization to defer the creation of a large or
resource-intensive object, or the execution of a resource-intensive
task, particularly when such creation or execution might not occur
during the lifetime of the program.
Support for Lazy<T> injection comes with Ninject.Extensions.Factory (also see it's Wiki Page on Lazy<T>). Install it's nuget package and you should be ready to inject Lazy<T>.
Adapt the code of your controller as follows:
public class FooController : ApiController {
public Lazy<IMyFooService> fooService;
public FooController(Lazy<IMyFooService> fooService) {
this.fooService = fooService;
}
public JsonResult Get() {}
public async Task<JsonResult> Post([FromBody] IList foos) {
var didMyFoo = this.fooService.Value.DoTheFoo();
}
}
Please notice that the actual service is accessed by the .Value Property on Lazy<T>. On first access to this property the instance is retrieved.

Similar question has been asked some time ago. Check this out. So for you particular case you can just modify IsRouteValueDefined method (you can think about some better naming, I would suggest something like IsRoutePoitingTo) from original answer to something like this (you might revisit if that works for WebApi, but for sure there is a way to get current route for that as well):
public static bool IsRouteValueDefined(string controller, string action)
{
var mvcHanlder = (MvcHandler)HttpContext.Current.Handler;
var routeValues = mvcHanlder.RequestContext.RouteData.Values;
var containsRouteKey = routeValues.ContainsKey(routeKey);
if (routeValue == null)
return containsRouteKey;
return containsRouteKey &&
routeValues["controller"].ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase) &&
routeValues["action"].ToString().Equals(action, StringComparison.InvariantCultureIgnoreCase);
}
And binding will look like:
kernel.Bind<IMyFooService>()
.To<MyConcreteService>()
.When(x=> IsRouteValueDefined("foo", "get"));
Just not sure about "get" as for ApiController the actual route could be http://website.com/foo/, if so, simply use string.Empty as "action" param. You can check that with your particular project. As you don't need default injection (which is present in original answer) - I just dropped that.

Related

ASP.Net Core 2.2 - Method overload appears in Visual Studio but does not work at run-time

I am attempting to verify that a user is authorized via a custom policy. I followed the tutorial at Ode To Code to add this functionality to my controller. From within Visual Studio, the code appears to be correct and utilizing a known overload.
Notice that it says that the overload is an "extension". I didn't take much notice of this until I spent 5 hours today trying to solve the following error:
As you can see, it would appear that the overload I'm attempting to use isn't being utilized. Am I doing something wrong here? Is there something special I have to do to include these extended methods? I've attempted cleaning and rebuilding the solution but this hasn't solved the problem.
While you've defined the field for IAuthorizationSerivce, you haven't provided any way for that to be set. You need to define a constructor for the LRController that takes a single parameter of IAuthorizationService, and assign that to the field.
I think there was a definition of that constructor in the tutorial.
Please note the name change: such as the global variable name for IAuthorizationService _authorization has been prefixed with an underscore. Obviously not required, but as a good rule of thumb/good coding standard, IMO. :-)
public class LRController : Controller
{
private readonly IAuthorizationService _authorization;
// you're missing this constructor & this pattern is known as Constructor Dependency Injection
public LRController(IAuthorizationService authorization)
{
_authorization = authorization;
}
public async Task<RedirectToActionResult> Index()
{
var superAdmin = await _authorization.AuthorizeAsync(User, "IsLucky");
//rest of your code here
}
}
EDIT
Additionally, if you wanted/needed to inject other interfaces into this controller, you would add it to that LRController constructor. Would look something like this:
public class LRController : Controller
{
private readonly IAuthorizationService _authorization;
private readonly IOtherService _otherService;
public LRController(IAuthorizationService authorization, IOtherService otherService)
{
_authorization = authorization;
_otherService = otherService;
}
public async Task<RedirectToActionResult> Index()
{
var superAdmin = await _authorization.AuthorizeAsync(User, "IsLucky");
}
public async Task Foo()
{
await _otherService.Bar();
}
}

Dotnet core method injection with controller methods

Lets say i have the following controller in dotnet core:
[Route("api/v1/[controller]")]
public class ValuesController : Controller
{
private readonly IHandler<ValuesRequest, ValuesResponse> _valueHandler;
private readonly IHandler<ValuesIdRequest, ValuesIdResponse> _valueIdHandler;
public ValuesController(IHandler<ValuesRequest, ValuesResponse> valueHandler,
IHandler<ValuesIdRequest, ValuesIdResponse> valueIdHandler)
{
_valueHandler = valueHandler;
_valueIdHandler = valueIdHandler;
}
[HttpGet]
public ValuesResponse Get(ValuesRequest request)
{
return _valueHandler.Handle(request);
}
[HttpGet("{id}")]
public ValuesIdResponse Get(ValuesIdRequest request)
{
return _valueIdHandler.Handle(request);
}
}
As you can see in the code above, I'm using dependency injection though the constructor. However, I was thinking on how I could reduce the amount of code. So, I was thinking about using method injection, which should reduce the code to something like this:
[Route("api/v1/[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public ValuesResponse Get(ValuesRequest request, IHandler<ValuesRequest, ValuesResponse> handler)
{
return handler.Handle(request);
}
[HttpGet("{id}")]
public ValuesIdResponse Get(ValuesIdRequest request, IHandler<ValuesIdRequest, ValuesIdResponse> handler)
{
return handler.Handle(request);
}
}
I was wondering if it is possible to do something like this in combination with controller params. I tried finding an answer on the web, however I could not find similar problem/solution.
Reference Action Injection with FromServices
Sometimes you don't need a service for more than one action within your controller. In this case, it may make sense to inject the service as a parameter to the action method. This is done by marking the parameter with the attribute [FromServices] as shown here:
public ValuesResponse Get(ValuesRequest request, [FromServices]IHandler<ValuesRequest, ValuesResponse> handler)
{
return handler.Handle(request);
}
While the answer would work using [FromService] within your actions, I have another suggestion.
From what I understand by reading the code you have provided is, that you use some kind of CQRS. For that case I can suggest MediatR. You will then only need to inject one interface into your controller and send your request using the IMediator. This way you will keep your controller small and clean and you will not need to inject all the other handlers.
There is a nice and handy extension for Microsoft's IoC-Container to register all your handlers and all other necessary classes to use MediatR.
services.AddMediatR(typeof(Startup).Assembly);

WebApi: Per Request Per Action DbSession using IoC, how?

Our existing database deployment has a single 'master' and a read-only replica. Using ASP.NET's Web API2 and an IoC container I want to create controller actions whose attribute (or lack there of) indicate which database connection is to be used for that request (See Controller and Services usage below)...
public MyController : ApiController
{
public MyController(IService1 service1, IService2 service2) { ... }
// this action just needs the read only connection
// so no special attribute is present
public Foo GetFoo(int id)
{
var foo = this.service1.GetFoo(id);
this.service2.GetSubFoo(foo);
return foo;
}
// This attribute indicates a readwrite db connection is needed
[ReadWrteNeeded]
public Foo PostFoo(Foo foo)
{
var newFoo = this.service1.CreateFoo(foo);
return newFoo;
}
}
public Service1 : IService1
{
// The dbSession instance injected here will be
// based off of the action invoked for this request
public Service1(IDbSession dbSession) { ... }
public Foo GetFoo(int id)
{
return this.dbSession.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.dbSession.Insert<Foo>(newFoo);
return newFoo;
}
}
I know how to setup my IoC (structuremap or Autofac) to handle per request IDbSession instances.
However, I'm not sure how I would go about making the type of IDbSession instance for the request to key off the indicator attribute (or lack there of) on the matching controller's action. I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write). But how do I make sure that the created IDbSession's lifecycle is managed by the container? You don't inject instances into the container at runtime, that would be silly. I know Filters are created once at startup (making them singleton-ish) so I can't inject a value into the Filter's ctor.
I thought about creating an IDbSessionFactory that would have 'CreateReadOnlyDbSession' and 'CreateReadWriteDbSession' interfaces, but don't I need the IoC container (and its framework) to create the instance otherwise it can't manage its lifecycle (call dispose when the http request is complete).
Thoughts?
PS During development, I have just been creating a ReadWrite connection for every action, but I really want to avoid that long-term. I could also split out the Services methods into separate read-only and read-write classes, but I'd like to avoid that as well as placing GetFoo and WriteFoo in two different Service implementations just seems a bit wonky.
UPDATE:
I started to use Steven's suggestion of making a DbSessionProxy. That worked, but I was really looking for a pure IoC solution. Having to use HttpContext and/or (in my case) Request.Properties just felt a bit dirty to me. So, if I had to get dirty, I might as well go all the way, right?
For IoC I used Structuremap and WebApi.Structuremap. The latter package sets up a nested container per Http Request plus it allows you to inject the current HttpRequestMessage into a Service (this is important). Here's what I did...
IoC Container Setup:
For<IDbSession>().Use(() => DbSession.ReadOnly()).Named("ReadOnly");
For<IDbSession>().Use(() => DbSession.ReadWrite()).Named("ReadWrite");
For<ISampleService>().Use<SampleService>();
DbAccessAttribute (ActionFilter):
public class DbAccessAttribute : ActionFilterAttribute
{
private readonly DbSessionType dbType;
public DbAccessAttribute(DbSessionType dbType)
{
this.dbType = dbType;
}
public override bool AllowMultiple => false;
public override void OnActionExecuting(HttpActionContext actionContext)
{
var container = (IContainer)actionContext.GetService<IContainer>();
var dbSession = this.dbType == DbSessionType.ReadOnly ?
container.GetInstance<IDbSession>("ReadOnly") :
container.GetInstance<IDbSession>("ReadWrite");
// if this is a ReadWrite HttpRequest start an Request long
// database transaction
if (this.dbType == DbSessionType.ReadWrite)
{
dbSession.Begin();
}
actionContext.Request.Properties["DbSession"] = dbSession;
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var dbSession = (IDbSession)actionExecutedContext.Request.Properties["DbSession"];
if (this.dbType == DbSessionType.ReadWrite)
{
// if we are responding with 'success' commit otherwise rollback
if (actionExecutedContext.Response != null &&
actionExecutedContext.Response.IsSuccessStatusCode &&
actionExecutedContext.Exception == null)
{
dbSession.Commit();
}
else
{
dbSession.Rollback();
}
}
}
}
Updated Service1:
public class Service1: IService1
{
private readonly HttpRequestMessage request;
private IDbSession dbSession;
public SampleService(HttpRequestMessage request)
{
// WARNING: Never attempt to access request.Properties[Constants.RequestProperty.DbSession]
// in the ctor, it won't be set yet.
this.request = request;
}
private IDbSession Db => (IDbSession)request.Properties["DbSession"];
public Foo GetFoo(int id)
{
return this.Db.Query<Foo>(...);
}
public Foo CreateFoo(Foo newFoo)
{
this.Db.Insert<Foo>(newFoo);
return newFoo;
}
}
I assume I will need to create an ActionFilter that will look for the indicator attribute and with that information identify, or create, the correct type of IDbSession (read-only or read-write).
With your current design, I would say an ActionFilter is the way to go. I do think however that a different design would serve you better, which is one where business operations are more explicitly modelled behind a generic abstraction, since you can in that case place the attribute in the business operation, and when you explicitly separate read operations from write operations (CQS/CQRS), you might not even need this attribute at all. But I'll consider this out of scope of your question right now, so that means an ActionFilter is the the way to go for you.
But how do I make sure that the created IDbSession's lifecycle is managed by the container?
The trick is let the ActionFilter store information about which database to use in a request-global value. This allows you to create a proxy implementation for IDbSession that is able to switch between a readable and writable implementation internally, based on this setting.
For instance:
public class ReadWriteSwitchableDbSessionProxy : IDbSession
{
private readonly IDbSession reader;
private readonly IDbSession writer;
public ReadWriteSwitchableDbSessionProxy(
IDbSession reader, IDbSession writer) { ... }
// Session operations
public IQueryable<T> Set<T>() => this.CurrentSession.Set<T>();
private IDbSession CurrentSession
{
get
{
var write = (bool)HttpContext.Current.Items["WritableSession"];
return write ? this.writer : this.reader;
}
}
}

How to register Drum.UriMaker<> using Simple Injector?

I'm using Drum which provides a generic class `UriMaker:
public class UriMaker<TController>
{
// I need use this one
public UriMaker(UriMakerContext context, HttpRequestMessage request) { }
public UriMaker(Func<MethodInfo, RouteEntry> mapper, UrlHelper urlHelper) { }
}
Used like this:
public class UserController : ApiController
{
public UserController(UriMaker<UserController> urlMaker) {}
}
I've used to register it with Unity:
container.RegisterType(typeof(UriMaker<>),
new InjectionConstructor(typeof(UriMakerContext), typeof(HttpRequestMessage)));
but now migrating to Simple Injector. I already have this:
UriMakerContext uriMaker = config.MapHttpAttributeRoutesAndUseUriMaker();
container.RegisterSingle(uriMakerContext);
So how now register UriMaker<> itself?
Although it is possible to configure Simple Injector to allow injecting an UriMaker<TController> directly into your controllers, I strongly advice against this for multiple reasons.
First of all, you should strive to minimize the dependencies your application takes on external libraries. This can easily be done by defining an application specific abstraction (conforming the ISP).
Second, injecting the UriMaker directly makes your extremely hard to test, since the UriMaker is pulled into your test code, while it assumes an active HTTP request and assumes the Web API route system to be configured correctly. These are all things you don't want your test code to be dependent upon.
Last, it makes verifying the object graph harder, since the UriMaker depends on an HttpRequestMessage, which is a runtime value. In general, runtime values should not be injected into the constructors of your services. You should build up your object graph with components (the stuff that contains the application's behavior) and you send runtime data through the object graph after construction.
So instead, I suggest the following abstraction:
public interface IUrlProvider
{
Uri UriFor<TController>(Expression<Action<TController>> action);
}
Now your controllers can depend on this IUrlProvider instead of depending on an external library:
public class UserController : ApiController
{
private readonly IUrlProvider urlProvider;
public UserController(IUrlProvider urlProvider)
{
this.urlProvider = urlProvider;
}
public string Get()
{
this.urlProvider.UriFor<HomeController>(c => c.SomeFancyAction());
}
}
Under the covers you of course still need to call Drum, and for this you need to define a proxy implementation for IUrlProvider:
public class DrumUrlProvider : IUrlProvider
{
private readonly UriMakerContext context;
private readonly Func<HttpRequestMessage> messageProvider;
public DrumUrlProvider(UriMakerContext context,
Func<HttpRequestMessage> messageProvider)
{
this.context = context;
this.messageProvider= messageProvider;
}
public Uri UriFor<TController>(Expression<Action<TController>> action)
{
HttpRequestMessage message = this.messageProvider.Invoke();
var maker = new UriMaker<TController>(this.context, message);
return maker.UriFor(action);
}
}
This implementation can be registered as singleton in the following way:
container.EnableHttpRequestMessageTracking(config);
UriMakerContext uriMakerContext =
config.MapHttpAttributeRoutesAndUseUriMaker();
IUrlProvider drumProvider = new DrumUrlProvider(uriMakerContext,
() => container.GetCurrentHttpRequestMessage());
container.RegisterSingle<IUrlProvider>(drumProvider);
This example uses the Simple Injector Web API integration package to allow retrieving the current request's HttpRequestMessage using the EnableHttpRequestMessageTracking and GetCurrentHttpRequestMessage extension methods as explained here.

How to do custom paging with EntitySetController

I'm trying to implement custom paging with the EntitySetController.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
return result.AsQueryable()
}
}
I think I'm missing something because it looks like the controller is trying to apply paging to the results of the Search method that already returns just one page. How can I prevent it from doing that and apply paging myself?
It looks like I can just inherit from ODataController instead and implement:
public IEnumerable<Poco> Get(ODataQueryOptions odataQueryOptions)
but I was wondering if I can stay with EntitySetController so that there's less plumbing code to write.
I wanted to stick to the OData format and not return PageResult<>
You can only take full control of the query using ODataQueryOptions or let the framework handle it completely for you using QueryableAttribute. There is no middle ground unfortunately.
So, I think doing an ODataController is the right way to solve this now.
That said, I can suggest a dirty workaround that would work for the time being. Beware that this relies on internal implementations that might/will change and break you.
public class MyController : EntitySetController<Poco, int>
{
public IQueryable<Poco> Get()
{
var result = _myBusinessLogic.Search(QueryOptions.Top.Value);
RemoveQueryString(Request, "$top");
return result.AsQueryable()
}
// This method relies that code that looks for query strings uses the extension
// method Request.GetQueryNameValuePairs that relies on cached implementation to
// not parse request uri again and again.
public static void RemoveQueryString(HttpRequestMessage request, string name)
{
request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] = request.GetQueryNameValuePairs().Where(kvp => kvp.Key != name);
}
}

Categories

Resources