Caching odata Web Api - asp.net-core

I am developing an OData API for my Asp.net core application and i want to implement caching on this.
The problem is all my endpoints will be IQueryable with a queryable services with no execution at all. so i can't implement any caching on service level
Controller
public class TagsController : ODataController
{
private readonly ITagService _tagService;
private readonly ILogger<TagsController> _logger;
public TagsController(ITagService tagService, ILogger<TagsController> logger)
{
_tagService = tagService;
_logger = logger;
}
[HttpGet("odata/tags")]
[Tags("Odata")]
[AllowAnonymous]
[EnableCachedQuery]
public ActionResult<IQueryable<Tag>> Get()
{
try
{
return Ok(_tagService.GetAll());
}
catch (Exception ex)
{
_logger.LogError(ex, "Some unknown error has occurred.");
return BadRequest();
}
}
}
So I tried to apply an extension on EnableQuery attribute to add the caching implementation on it. so i added the following
public class EnableCachedQuery : EnableQueryAttribute
{
private IMemoryCache _memoryCache;
public EnableCachedQuery()
{
_memoryCache = new MemoryCache(new MemoryCacheOptions());
}
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
//var url = GetAbsoluteUri(actionContext.HttpContext);
var path = actionContext.HttpContext.Request.Path + actionContext.HttpContext.Request.QueryString;
//check cache
if (_memoryCache.TryGetValue(path, out ObjectResult value))
{
actionContext.Result = value;
}
else
{
base.OnActionExecuting(actionContext);
}
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception != null)
return;
var path = context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
var cacheEntryOpts = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(15));
base.OnActionExecuted(context);
_memoryCache.Set(path, context.Result, cacheEntryOpts);
}
}
the first request completed successfully and retrieved the data correctly with filters and queries applied. then when tried to add the data to cache the context.Result holds the ObjectResult and then in the second request which should be cached the value was there but with an error in executing which means that the cached value is not the final output value that should be passed to the Result
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'ApplicationDbContext'.
============================
Update:
public class ApplicationDbContext : IdentityDbContext<User, Account, Session>, IApplicationDbContext
{
public ApplicationDbContext(
DbContextOptions options,
IApplicationUserService currentUserService,
IDomainEventService domainEventService,
IBackgroundJobService backgroundJob,
IDomainEventService eventService,
IDateTime dateTime) : base(options, currentUserService, domainEventService, backgroundJob, dateTime) { }
public DbSet<Tag> Tags => Set<Tag>();
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
var entityTypes = builder.Model.GetEntityTypes()
.Where(c => typeof(AuditableEntity).IsAssignableFrom(c.ClrType))
.ToList();
foreach (var type in entityTypes)
{
var parameter = Expression.Parameter(type.ClrType);
var deletedCheck = Expression.Lambda
(Expression.Equal(Expression.Property(parameter, nameof(AuditableEntity.Deleted)), Expression.Constant(false)), parameter);
type.SetQueryFilter(deletedCheck);
}
builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
builder.ApplySeedsFromAssembly(typeof(ApplicationDbContext).Assembly);
}
}

Related

Convert HttpContext.User before API Method

I currently have a .Net Core API application with a bunch of API get methods. Currently in every single method I am needing to write this line:
[ProducesResponseType(200, Type = typeof(MetadataAttributeModel))]
[ProducesResponseType(400, Type = typeof(ValidationResultModel))]
[ProducesResponseType(500, Type = typeof(ErrorResultModel))]
public ActionResult<MetadataAttributeModel> GetAsync(string name)
{
List<Entities.DocumentAttributeView> attributes = documentAttributeViewRepo.GetByAttributeName(name);
SiteUser currentUser = new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
return Unauthorized();
}
Is there a way I can convert the HttpContext.User object to our own SiteUser object before I get to the method? I don't want to have to write this line in ALL of the API methods:
SiteUser currentUser = new SiteUser(db, HttpContext.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
TIA,
Alex
The AspNet Mvc mechanism for "Do something for every Action" is Filters.
Filters can run before the method is called, and they can, for instance, set the Http.Context.User.
A filter can be applied to an action, a controller, or (by writing code in Startup) globally.
[SwapUserToAuthorizedDatabaseUser]
public class MyController
{
public IActionResult About() => Ok(User);
}
Which will invoke this filter for every Action on the Controller :
public class SwapUserToAuthorizedDatabaseUserAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
SiteUser currentUser = new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
if (currentUser == null)
{
context.Result= new RedirectToRouteResult("/Identity/Logout");
}
else
{
var claimsIdentity =
new ClaimsIdentity(
new Claim[]
{
new Claim("Id", currentUser.Id),
new Claim("UserName", currentUser.UserName),
new Claim("WhateverElseYourSiteUserHas", currentUser.Something.ToString()),
}
);
context.HttpContext.User = new ClaimsPrincipal(new[]{claimsIdentity});
}
}
public void OnActionExecuted(ActionExecutedContext context){}
}
If overwriting the HttpContext.User isn't what you need, then it's much less code to use HttpContext.Items :
public class SwapUserToAuthorizedDatabaseUserAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Items["SiteUser"]= new SiteUser(db, User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
}
public void OnActionExecuted(ActionExecutedContext context){}
}
Instead of an IActionFilter to run on every Action, you can use an IAuthorizationFilter which has a public void OnAuthorization(AuthorizationFilterContext context) method. This would save repeatedly calling the database, but does mean you must cache your currentUser somewhere, presumably in Session.
The problem is, how do you get access to your database? If you go the route of adding a Global filter by adding it in Startup:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc(o=>o.Filters.Add(new SwapUserToAuthorizedDatabaseUserAttribute(provide a db instance here)));
}
Then you can give your Filter a constructor and pass in a database. There's also an overload for using the DependencyInjection system.
If you don't use the startup method, you have to do some DIY injection, for instance by having a static method to return a DbContext.
You can move this logic to a service:
public class UserService : IUserService
{
private readonly HttpContext context;
private readonly Db db;
public UserService(IHttpContextAccessor context, Db db)
{
this.context = context.HttpContext;
this.db = db;
}
public SiteUser GetUser()
{
return new SiteUser(db, context.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress").Value);
}
}
Inject it to controllers where it's required:
public MyController(IUserService userService) { ... }
Register it as a Scoped service in ConfigureServices in Startup.cs along with IHttpContextAccessor (should be singleton):
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<UserService>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

.NET core custom and default binding combined

I'm creating a custom model binder for a view model, implementing IModelBinder
I have a lot of properties in my view model, the majority of which do not need any custom binding. Rather than explicitly set all of the property values on my model individually from the ModelBindingContext, I would to be able to get the framework to bind the model for me, then I would carry out any custom binding:
public class ApplicationViewModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
// get .net core to bind values on model
// Cary out any customization of the models properties
bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
return Task.CompletedTask;
}
}
Basically I want to carry out the default model binding, then apply custom binding, similar to the approach taken in this SO post but for .NET Core, not framework.
I assumed applying the default binding would be straight forward, but haven't been able to find out how to do so. I believe the solution would involve ComplexTypeModelBinder and ComplexTypeModelBinderProvider classes, but can't seem to find out how to go about it.
I know I could just make any changes when the POST request hits my controller method, but this seem the wrong place and wrong time to do so.
For custom ComplexTypeModelBinder, you could inherit from ComplexTypeModelBinder.
Model
public class BinderModel
{
public int Id { get; set; }
public string Name { get; set; }
public string BinderValue { get; set; }
}
Controller Action
[HttpPost]
public void Post([FromForm]BinderModel value)
{
}
CustomBinder
public class CustomBinder : ComplexTypeModelBinder
{
private readonly IDictionary<ModelMetadata, IModelBinder> _propertyBinders;
public CustomBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders)
: base(propertyBinders)
{
_propertyBinders = propertyBinders;
}
protected override Task BindProperty(ModelBindingContext bindingContext)
{
if (bindingContext.FieldName == "BinderValue")
{
bindingContext.Result = ModelBindingResult.Success("BinderValueTest");
return Task.CompletedTask;
}
else
{
return base.BindProperty(bindingContext);
}
}
protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
{
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
}
}
CustomBinderProvider
public class CustomBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
for (var i = 0; i < context.Metadata.Properties.Count; i++)
{
var property = context.Metadata.Properties[i];
propertyBinders.Add(property, context.CreateBinder(property));
}
//var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
//return new ComplexTypeModelBinder(propertyBinders, loggerFactory);
return new CustomBinder(propertyBinders);
}
return null;
}
}
Inject provider
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => {
options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
});
}
ComplexTypeModelBinder has unfortunately been deprecated in .Net 5.0, and it's counterpart, ComplexObjectModelBinder, is sealed, so you can't extend from it.
But, you can work around that. ComplexObjectModelBinderProvider is public, and you can use that to create a ComplexObjectModelBinder. Thus, if you make your own custom IModelBinderProvider, you can have the constructor accept a ComplexObjectModelBinderProvider argument, and make use of that to make a ComplexObjectModelBinder. Then, you can pass that to your custom IModelBinder, where it'll happily do its custom work before falling back to the ComplexObjectModelBinder you supplied.
Here's an example. First, your IModelBinder. This example shows that you can use DI if you want to. (In this example, say we needed a DbContext.)
public class MyCustomModelBinder : IModelBinder
{
private readonly IModelBinder _defaultBinder;
private readonly DbContext _dbContext;
public MyCustomModelBinder(IModelBinder defaultBinder, DbContext dbContext)
{
_defaultBinder = defaultBinder;
_dbContext = dbContext;
}
public override Task BindModelAsync(ModelBindingContext bindingContext)
{
// -do custom work here-
return _defaultBinder.BindModelAsync(bindingContext);
}
}
However, in order to use DI on your custom model binder, you'll need a helper class. The problem is, when IModelBinderProvider is called, it won't have access to all the services in a typical request, like your DbContext for example. But this class will help:
internal class DIModelBinder : IModelBinder
{
private readonly IModelBinder _rootBinder;
private readonly ObjectFactory _factory;
public DIModelBinder(Type binderType, IModelBinder rootBinder)
{
if (!typeof(IModelBinder).IsAssignableFrom(binderType))
{
throw new ArgumentException($"Your binderType must derive from IModelBinder.");
}
_factory = ActivatorUtilities.CreateFactory(binderType, new[] { typeof(IModelBinder) });
_rootBinder = rootBinder;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var requestServices = bindingContext.HttpContext.RequestServices;
var binder = (IModelBinder)_factory(requestServices, new[] { _rootBinder });
return binder.BindModelAsync(bindingContext);
}
}
Now you're ready to write the custom IModelBinderProvider:
public class MyCustomModelBinderProvider : IModelBinderProvider
{
private readonly IModelBinderProvider _rootProvider;
public MyCustomModelBinderProvider(IModelBinderProvider rootProvider)
{
_rootProvider = rootProvider;
}
public IModelBinder? GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(MyModel))
{
var rootBinder = _rootProvider.GetBinder(context)
?? throw new InvalidOperationException($"Root {_rootProvider.GetType()} did not provide an IModelBinder for MyModel.");
return new DIModelBinder(typeof(MyCustomModelBinder), rootBinder);
}
return null;
}
}
Finally, in your startup file where you configure services, you can grab the ComplexObjectModelBinderProvider instance, use that to create a new instance of your MyCustomModelBinderProvider, and insert that into the ModelBinderProviders.
services.AddMvc(options =>
{
var fallbackProvider = options.ModelBinderProviders
.First(p => p is ComplexObjectModelBinderProvider);
var myProvider = new MyCustomModelBinderProvider(fallbackProvider);
options.ModelBinderProviders.Insert(0, myProvider);
})

Which HttpConfiguration object do I need to access to create a custom HttpParameterBinding?

In this post, Mike Wasson states:
"Besides ParameterBindingAttribute, there is another hook for adding a custom HttpParameterBinding. On the HttpConfiguration object"
But I have three HttpConfiguration objects in my Web API app, namely:
public static void Register(HttpConfiguration config, IWindsorContainer container) <-- in WebApiConfig.cs
private static void MapRoutes(HttpConfiguration config) <-- ""
public static void ConfigureWindsor(HttpConfiguration configuration) <-- in Global.asax.cs
Which of these (config, config, or configuration) should I use (if any)?
UPDATE
I tried this, with a breakpoint on the "if" line:
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
if (configuration.Properties.Values.Count > 0) // <-- I put a Casey Jones here
{
object o = configuration.Properties.Values.ElementAt(configuration.Properties.Values.Count - 1);
string s = o.ToString();
}
}
...but I only hit that spot once, on the server starting up, but not when the client sent a request to it...there must be some event that gets fired when a server passes a request where the incoming URL can be examined...no?
Usually you do have only one instance of HttpConfiguration which is the one you get from GlobalConfiguration.Configuration.
Said so, that's how I plugged custom binders
In global.asax
var binderMappings = new Dictionary<Type, Type>
{
{typeof(YourModelType), typeof(YourModelTypeBinder)},
//....
};
config.Services.Add(
typeof(ModelBinderProvider),
new WindsorModelBinderProvider(container, binderMappings));
WindsorModelBinderProvider
public class WindsorModelBinderProvider : ModelBinderProvider
{
private readonly IWindsorContainer _container;
private readonly IDictionary<Type, Type> _binderMappings;
public WindsorModelBinderProvider(IWindsorContainer container, IDictionary<Type, Type> binderMappings)
{
_container = container;
_binderMappings = binderMappings;
}
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
{
IModelBinder binder = null;
if (_binderMappings.ContainsKey(modelType))
{
binder = _container.Resolve(_binderMappings[modelType]) as IModelBinder;
if (binder == null)
{
throw new ComponentNotFoundException(modelType);
}
}
return binder;
}
}
YourModelTypeBinder
public class YourModelTypeBinder : IModelBinder
{
public YourModelTypeBinder(IYourServiceToLoadYourModelType service)
{
//...
}
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
bindingContext.Model = YourCustomCodeToLoadYourModelTypeUsingTheConstructorDependecies(actionContext.Request);
return true;
}
private YourModelType YourCustomCodeToLoadYourModelTypeUsingTheConstructorDependecies(HttpRequestMessage requestMessage)
{
...
}
}
YourModelTypeBinder will be resolved by the container(see WindsorModelBinderProvider), so you need to registered it first.
After all that plumbing, your controller may have a parameter, among others, as following
[ModelBinder]YourModelType user

ASP.NET Web API - How can I keep a session alive until after custom MediaTypeFormatter is complete?

I have an ASP.Net Web API project. I am using NHibernate in this project; Fluent NHibernate to be specific. I am handling NHib session management using a custom ActionFilterAttribute. It looks like this:
public class SessionManagement : ActionFilterAttribute
{
public SessionManagement()
{
SessionFactory = WebApiApplication.SessionFactory;
}
private ISessionFactory SessionFactory { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transaction = session.Transaction;
if (transaction != null && transaction.IsActive)
{
transaction.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
This was working well for my needs. However, I have recently added a custom JSON.NET MediaTypeFormatter to format the my action's resulting JSON. The problem I am having is that my ActionFilter OnActionExecuted() method is called before the MediaTypeFormatter's WriteToStreamAsync can do it's job. The result is that lazily loaded (the problem) collections are now not available because the session is closed. What is the best way to handle this? Should I remove the ActionFilter's OnActionExecuted method and just close my session in the MediaTypeFormatter?
Thanks!!
The MediaTypeFormatter is the wrong layer to close the session at because this behavior really has nothing to do with the particular formatter you're using. Here's what I recommend doing:
Derive from HttpContent and create a class that derives from ObjectContent. Override the SerializeToStreamAsync implementation to await the base implementation's SerializeToStreamAsync then close the session:
public class SessionClosingObjectContent : ObjectContent
{
private Session _session;
public SessionClosingObjectContent(Type type, object value, MediaTypeFormatter formatter, Session session)
: base(type, value, formatter)
{
_session = session;
}
protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
await base.SerializeToStreamAsync(stream, context);
// Close the session and anything else you need to do
_session.Close();
}
}
Now in your action filter, instead of closing the session, you want to replace the response Content with your new class that closes the session:
public class SessionManagement : ActionFilterAttribute
{
public SessionManagement()
{
SessionFactory = WebApiApplication.SessionFactory;
}
private ISessionFactory SessionFactory { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var response = actionExecutedContext.Response;
if (response.Content != null)
{
ObjectContent objectContent = response.Content as ObjectContent;
if (objectContent != null)
{
response.Content = new SessionClosingObjectContent(objectContent.ObjectType, objectContent.Value, objectContent.Formatter, session);
foreach (KeyValuePair<string, IEnumerable<string>> header in objectContent.Headers)
{
response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
}
}
}
You could also choose instead to return an HttpResponseMessage with your new Content directly from your controller code wherever you need it.

WCF Web API UriTemplate Elements Found in Multiple Methods

Let's say I am using the new WCF Web API to build a RESTful service and, in my service, I have a section of the URI that will describe the target resource, but is used on (nearly) all methods of the contract. For example, if I have a User service that deals with eCommerce and may look like:
[ServiceContract]
public class MyUserService
{
private MyUserRepository _UserRepo;
private MyOrganizationRepository _OrgRepo;
[WebGet (UriTemplate = "{OrganizationName}/Users")]
public IEnumerable<User> GetUsers (string OrganizationName)
{
IEnumerable<User> Users = null;
var Organization = _OrgRepo.GetOrgByName (OrganizationName);
if (Organization != null)
{
Users = Organization.GetUsers ();
}
else
{
throw new WebFaultException<string> ("Organization not found.", HttpStatusCode.NotFound);
}
return Users;
}
[WebInvoke (UriTemplate = "{OrganizationName}/Users", /*yada...yada...yada*/)]
public User AddNewUser (string OrganizationName, User User)
{
// Find the organization, like above, and throw if null.
}
}
If I have to continually load the organization and test for null, this will bog down my code and is not very DRY. (So tempted to spell out DRY...) What I would like to do is load up a property in the MyUserService class that is populated when {OrganizationName} is included in the URI and throw a WebFaultException otherwise. Because this is apart of the URI, what would be the best way to accomplish this?
EDIT:
For those that may be interested, here is an example of the HttpOperationHandler I came up with. There doesn't seem to be a whole lot of information out there covering this. I also found more information about Processors that will be coming with the WCF Web Api suite and it looks like they will handle this sort of thing better replace HttpOperationHandlers and it seems they may be easier to use. (This is just a for-instance to cover some things I found hard to find. I wrote it up a bit differently in my application.)
using Microsoft.ApplicationServer.Http.Dispatcher; // For HttpOperationHandler
using Microsoft.ApplicationServer.Http.Description; // For HttpOperationHandlerFactory
public class OrganizationHandler : HttpOperationHandler<string, Organization>
{
private Repository<Organization> _OrganizationRepository;
public OrganizationHandler (UnitOfWork Work)
: base ("OrganizationName")
{
_OrganizationRepository = Work.Organizations;
}
public override Organization OnHandle (string OrganizationName)
{
var Result = _OrganizationRepository
.Get (O => O.UrlSafeName.Equals (OrganizationName,
StringComparison.InvariantCultureIgnoreCase));
if (Result == null)
{
throw new WebFaultException<string> ("Organization not found.");
}
return Result;
}
}
public class OrganizationHandlerFactory : HttpOperationHandlerFactory
{
private UnitOfWork _Work;
public OrganizationHandlerFactory (UnitOfWork Work)
{
_Work = Work;
}
protected override Collection<HttpOperationHandler> OnCreateRequestHandlers
(ServiceEndpoint endpoint, HttpOperationDescription operation)
{
var Collection = base.OnCreateRequestHandlers (endpoint, operation);
if (operation.InputParameters.Any (IP => IP.Type.Equals (typeof (Organization))))
{
var Binding = endpoint.Binding as HttpBinding;
if (Binding != null)
{
Collection.Add (new OrganizationHandler (_Work));
}
}
return Collection;
}
}
And then to wire it up in Global.asax (I am using Ninject for IoC):
// Add this reference to get the MapServiceRoute<T> extension
using Microsoft.ApplicationServer.Http.Activation;
public class Global : HttpApplication
{
protected void Application_Start (object sender, EventArgs e)
{
var Kernel = BuildKernel ();
var Config = HttpHostConfiguration.Create ()
.SetOperationHandlerFactory
(Kernel.Get (typeof (OrganizationHandlerFactory)) as OrganizationHandlerFactory)
.SetResourceFactory (new NinjectResourceFactory (Kernel));
RouteTable.Routes.MapServiceRoute<OrganizationService> ("Organizations", Config);
}
protected IKernel BuildKernel ()
{
IKernel Kernel = new Ninject.StandardKernel ();
// Load up the Kernel
return Kernel;
}
}
public class NinjectResourceFactory : IResourceFactory
{
private readonly IKernel _Kernel;
public NinjectResourceFactory (IKernel Kernel)
{
_Kernel = Kernel;
}
public object GetInstance (Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
{
return Resolve (serviceType);
}
public void ReleaseInstance (InstanceContext instanceContext, object service)
{
throw new NotImplementedException ();
}
private object Resolve (Type type)
{
return _Kernel.Get (type);
}
}
And here it is in my Service:
[ServiceContract]
[ServiceBehavior (InstanceContextMode = InstanceContextMode.PerCall)]
public class OrganizationService
{
[WebGet (UriTemplate = "{OrganizationName}/Products")]
public IEnumerable<Product> GetProducts (Organization Organization)
{
return Organization.Products;
}
}
This is exactly what OperationHandlers are for. You create a single OperationHandler that converts the URI parameter into a strongly typed object that you can just accept as a parameter on the operation.