I am trying to inject CommonService dependency in TableController as like below and it works fine if I call table controller endpoint like "http://localhost:61558/api/TodoItem".
but, It throws an error when I call the same endpoint using "http://localhost:61558/tables/TodoItem"(Correct way as Mobile App SDK call this URL to sync data)
Exception:
"ExceptionType": "System.InvalidOperationException"
ExceptionMessage: "An error occurred when trying to create a controller of type 'TodoItemController'. Make sure that the controller has a parameterless public constructor.",
Startup.cs
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddMobileAppHomeController()
.MapApiControllers()
.AddTables(new MobileAppTableConfiguration()
.MapTableControllers()
.AddEntityFramework()
)
.ApplyTo(config);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
app.UseWebApi(config);
}
}
I have properly configured DIUnityConfig:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType(typeof(ICommonService), typeof(CommonService));
Current = container;
}
Here is the table controller code:
[Authorize]
public class TodoItemController : TableController<TodoItem>
{
private readonly ICommonService _ICommonService;
public TodoItemController(ICommonService commonService)
{
_ICommonService = commonService;
}
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<TodoItem>(context, Request, enableSoftDelete: true);
}
// PATCH tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
{
var item = await UpdateAsync(id, patch);
await PushToSyncAsync("todoitem", item.Id);
_ICommonService.SendMail();// Want to send mail on business logic.
return item;
}
}
Your table controller is not complete. You need to pass the HttpControllerContext into the constructor and then to the base. Something like this is normal:
public class DatesController : TableController<Dates>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var context = new AppDbContext();
DomainManager = new EntityDomainManager<Dates>(context, Request);
}
// Rest of your controller here
}
Without calling Initialize(), your table controller is never registered.
Related
Edit: This question has been modified completely from its original version.
My objective is to commit the database transaction at the action filer level. I am using Web API .Net Framework (4.8) along with Unity DI.
My Transaction Filter Attribute:
public class TransactionFilterAttribute : ActionFilterAttribute
{
private readonly PortalContext _context;
private DbContextTransaction _transactionScope;
public TransactionFilterAttribute(IUnityContainer container)
{
_context = container.Resolve<PortalContext>();
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
_transactionScope = _context.Database.BeginTransaction();
using (_transactionScope)
{
base.OnActionExecuting(actionContext);
}
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
try
{
base.OnActionExecuted(actionExecutedContext);
_transactionScope.Commit(); //Error occurred
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
}
Unity Configuration:
public static class UnityConfig
{
private static readonly Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer Container => container.Value;
private static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<PortalContext>();
}
}
I am getting an error while executing _transactionScope.Commit()
Error: Value cannot be null.
Parameter name: connection --> The underlying provider failed on Commit.
Any help would be appreciated.
You could change your code to look something like this. This would open the transaction when the controller gets called and would wait for the method to return before committing the transaction. This is done by the "await next()" part. You should resolve the _databaseContext from your IOC container so make sure to add it at startup. Using a service filter allows you to use the IOC container within a filter. Have a look at this post to learn more about implementing a service filter in .NET projects: https://www.strathweb.com/2015/06/action-filters-service-filters-type-filters-asp-net-5-mvc-6/. The example below it made for an async method, use the sync version of this method if needed.
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
using (var transaction = _champContext.Database.BeginTransaction())
{
var result = await next();
if ((result.Exception == null || result.ExceptionHandled) &&
IsHttpSuccessStatusCode(context.HttpContext.Response.StatusCode))
{
transaction.Commit();
return;
}
transaction.Rollback();
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
var controllerName = controllerActionDescriptor?.ControllerName;
var actionName = controllerActionDescriptor?.ActionName;
_logger.Error("Tried to commit transaction for the {ActionName}" +
" method on the {ControllerName} controller with the following parameters: {ActionParameters}" +
" but got exception: {Exception}",
actionName, controllerName, context.ActionArguments, result.Exception);
}
}
This is purely a guess, but I'm wondering if the using clause in your OnActionExecuting override is to blame.
As soon as the base call to OnActionExecuting returns, your _transactionScope is Dispose'd. Thus, assuming that OnActionExecuted isn't called from within the base.OnActionExecuting method, _transactionScope will not be in the state you're expecting in OnActionExecuted.
Suggest:
public override void OnActionExecuting(HttpActionContext actionContext)
{
_transactionScope = _context.Database.BeginTransaction();
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
try
{
base.OnActionExecuted(actionExecutedContext);
_transactionScope.Commit(); //Error occurred
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
_transactionScope.Rollback(); // because something went wrong during your transaction
}
finally
{
_transactionScope.Dispose(); // now that we're definitely done.
}
}
I have a service CompanyService
This service is dependent on 2 other services - ICompanyRepository and IDataCacheService
public class CompanyService : ICompanyService
{
private readonly ICompanyRepository _companyRepository;
private IDataCacheService _dataCacheService;
public CompanyService(ICompanyRepository companyRepository, IDataCacheService dataCacheService)
{
_companyRepository = companyRepository;
_dataCacheService = dataCacheService;
}
}
This services themselves have no dependencies
Now I need to make this available via the built in injection within my Azure Function
so in Startup.cs, I modified Configure to add the new services
public override void Configure(IFunctionsHostBuilder builder)
{
var cosmosDbConnectionString = new CosmosDBConnectionString(Environment.GetEnvironmentVariable("CosmosDBConnection"));
builder.Services.AddSingleton<IDocumentClient>(s =>
new DocumentClient(cosmosDbConnectionString.ServiceEndpoint, cosmosDbConnectionString.AuthKey));
var companyRepository = new CompanyRepository();
builder.Services.AddSingleton<ICompanyRepository>(companyRepository);
var dataCacheService = new DataCacheService();
builder.Services.AddSingleton<IDataCacheService>(dataCacheService);
var companyService = new CompanyService(companyRepository, dataCacheService);
builder.Services.AddSingleton<ICompanyService>(companyService);
}
This compiles and runs through fine
However, when I add ICompanyService as a parameter of my function I get the error
Cannot bind parameter 'companyService' to type ICompanyService
My method is below
public class Companies
{
private const string OperationName = "OPERATION";
[FunctionName(OperationName)]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "myroute/")]HttpRequest req,
ICompanyService companyService,
ILogger log)
{
//logic here
}
}
This is the same way as other function run methods within my project
What have I done wrong?
This also doesn't work with the standard AddSingleton syntax
builder.Services.AddSingleton<ICompanyService, CompanyService>();
Paul
Given the shown configuration, the registration can be simplified to
public override void Configure(IFunctionsHostBuilder builder) {
var services = builder.Services;
var cosmosDbConnectionString = new CosmosDBConnectionString(Environment.GetEnvironmentVariable("CosmosDBConnection"));
services.AddSingleton<IDocumentClient>(s =>
new DocumentClient(cosmosDbConnectionString.ServiceEndpoint, cosmosDbConnectionString.AuthKey));
services.AddSingleton<ICompanyRepository, CompanyRepository>();
services.AddSingleton<IDataCacheService, DataCacheService>();
services.AddSingleton<ICompanyService, CompanyService>();
}
All that is left is to make sure to explicitly inject the required service into the function instance via constructor injection.
public class Companies {
private const string OperationName = "OPERATION";
private readonly ICompanyService companyService;
public Companies(ICompanyService companyService) {
this.companyService = companyService;
}
[FunctionName(OperationName)]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "myroute/")]HttpRequest req,
ILogger log) {
//logic here
}
}
Reference Use dependency injection in .NET Azure Functions
I have a simple C# Web Api project that provides a few restful endpoints.
Controller fatal server error handling/logging is generally well described by either using:
Implementing/overriding Application_Error method in Global.asax.cs
protected override void Application_Error(object sender, EventArgs e)
{
var ex = Server.GetLastError();
_logger.Error("Unexpected error while initializing application", ex);
}
Or by adding an exception handler filter:
config.Filters.Add(new ExceptionHandlingAttribute());
OR
GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlingAttribute());
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
private static readonly ILog _logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
_logger.Error("Unexpected error in API.", actionExecutedContext.Exception);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}
However, when an error occurs during instantiation of a controller due to a failure in dependency injection within the constructor of this code:
public class DataController : ApiController
{
private readonly IDataService _dataService;
public DataController(IDataService dataService)
{
_dataService = dataService;
}
[AllowAnonymous]
[HttpGet]
public IHttpActionResult GetSomeStuff()
{
return Ok(new AjaxResponse("somestuff"));
}
none of the above methods catches the error. How can I catch those errors?
This is described very nicely in this blog post. Excerpt to answer question below:
Create a class:
public class GlobalExceptionHandler : ExceptionHandler
{
public async override Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
// Access Exception
// var exception = context.Exception;
const string genericErrorMessage = "An unexpected error occured";
var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError,
new
{
Message = genericErrorMessage
});
response.Headers.Add("X-Error", genericErrorMessage);
context.Result = new ResponseMessageResult(response);
}
}
Then Register you exception handler as below from you application startup or owin setup as below:
public static class SetupFiltersExtensions
{
public static IAppBuilder SetupFilters(this IAppBuilder builder, HttpConfiguration config)
{
config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());
return builder;
}
}
As stated in his post, he isn't logging in the above method but prefers to do so through a GlobalErrorLogger as such:
public class GlobalErrorLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
var exception = context.Exception;
// Write your custom logging code here
}
}
Registered as such:
public static class SetupFiltersExtensions
{
public static IAppBuilder SetupFilters(this IAppBuilder builder, HttpConfiguration config)
{
config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());
config.Services.Add(typeof(IExceptionLogger), new GlobalErrorLogger());
return builder;
}
}
I finally found the answer myself which is the following.
One has to implement and override the IHttpControllerActivator interface on a GlobalConfiguration.Configuration.Services level (this is important as config.Services only deals with already instantiated controllers).
Here are some snippets:
Startup.cs
// the following will make sure that any errors that happen within the constructor
// of any controller due to dependency injection error will also get logged
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),
new ExceptionHandlingControllerActivator(
GlobalConfiguration.Configuration.Services.GetHttpControllerActivator())
);
ExceptionHandlingControllerActivator.cs
/// <summary>
/// This class handles instantiation of every api controller. It handles and logs
/// any exception that occurs during instatiation of each controller, e.g. errors
/// that can happen due to dependency injection.
/// </summary>
public class ExceptionHandlingControllerActivator : IHttpControllerActivator
{
private IHttpControllerActivator _concreteActivator;
private static readonly ILog _logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator)
{
_concreteActivator = concreteActivator;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
try
{
return _concreteActivator.Create(request, controllerDescriptor, controllerType);
}
catch (Exception ex)
{
_logger.Error("Internal server error occured while creating API controller " + controllerDescriptor.ControllerName, ex);
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Unexpected error while creating controller " + controllerDescriptor.ControllerName));
}
}
}
I'm using ASP.NET Core and FluentValidation.
When a POST action receives invalid input, it's customary to re-render the input form view, with validation errors:
if (!ModelState.IsValid)
return View("nameOfViewRenderedByGetAction", model);
But my validation is actually performed in a service, by FluentValidation, which throws ValidationException. I want to handle it in an exception filter:
public class ValidationFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// only handle ValidationException
var ex = context.Exception as ValidationException;
if (ex == null) return;
// re-render get action's view, or redirect to get action
// ??
}
}
I'm stuck at the "??" part, because Core has changed the signatures of many types, and ExceptionContext doesn't surface the data I need to make this work.
How do I do this?
It's a little late for an answer but I have a working solution for exactly the same application design. I use ASP.NET Core 3.0 and FluentValidation 8.x.
public class MvcValidationExceptionFilterAttribute : ExceptionFilterAttribute
{
private IModelMetadataProvider ModelMetadataProvider { get; }
public MvcValidationExceptionFilterAttribute(IModelMetadataProvider modelMetadataProvider)
{
ModelMetadataProvider = modelMetadataProvider;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Framework calls without null")]
public override void OnException(ExceptionContext context)
{
if (context.Exception is ValidationException ex)
{
var validationResult = new ValidationResult(ex.Errors);
validationResult.AddToModelState(context.ModelState, null);
context.Result = new ViewResult { ViewData = new ViewDataDictionary(ModelMetadataProvider, context.ModelState) };
context.ExceptionHandled = true;
}
}
}
As this filter has a dependency we can't use the Attribute directly but register it with dependency injection in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MvcValidationExceptionFilterAttribute>();
To use the ExceptionFilter either apply it via the ServiceFilterAttribute:
[ServiceFilter(typeof(MvcValidationExceptionFilterAttribute))]
public class MyController : Controller
{
Or apply it globally in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add<MvcValidationExceptionFilterAttribute>();
})
From an exception filter, You can render a custom view by setting the context result.
public class ValidationFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// only handle ValidationException
var ex = context.Exception as ValidationException;
if (ex == null) return;
// re-render get action's view, or redirect to get action
var result = new ViewResult { ViewName = "GetView" }
context.HttpContext.Response.Clear();
context.Result = result;
}
}
Where GetView should be the name of your Get action's view.
Sample exception filter that uses a custom developer error view to display details about exceptions.
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public CustomExceptionFilterAttribute(
IHostingEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
_modelMetadataProvider = modelMetadataProvider;
}
public override void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
// do nothing
return;
}
var result = new ViewResult {ViewName = "CustomError"};
result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState);
result.ViewData.Add("Exception", context.Exception);
// TODO: Pass additional detailed data via ViewData
context.Result = result;
}
}
Note that the above code is sending the context, model state and exception to the view.
In case all you need is custom error page refer to ASP.NET Core Error Handling
Generally, you should not be using an exception filter to turn an error into success. Consider using an action filter if you have a requirement like that.
Having said that, for some reason if you still need to redirect from an exception filter, this is how it can be done
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
public CustomExceptionFilterAttribute(
IHostingEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
}
public override void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
// do nothing
return;
}
var result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Home", action = "Error" }));
context.Result = result;
}
}
This is my controller
public class SuggestionController : ApiController
{
public ISuggestionRepository Repository { get; private set; }
public SuggestionController(ISuggestionRepository repository)
{
this.Repository = repository;
}
// to post suggestion
[HttpPost]
[ActionName("PostSuggestion")]
public HttpResponseMessage PostSuggestion(Suggestion suggestion)
{
var answerCorrect = this.Repository.CreateSuggestion(suggestion);
if (answerCorrect == true)
return Request.CreateResponse(HttpStatusCode.OK);
else
return Request.CreateResponse(HttpStatusCode.Conflict);
}
}
and this is my RegisterServices method in NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
kernel.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
kernel.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
kernel.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
But am getting an exception that my suggestion controller does not have a default constructor and its showing a 500 internal server when am hitting the controller from a client app
I know that we get the exception of controller not having default constructor if the ninject dependency is not working properly but the below is another controller i have implemeneted similar to suggestion controller and its working absolutely fine.
public IUserRepository Repository { get; private set; }
public SSOController(IUserRepository repository)
{
this.Repository = repository;
}
[HttpPost]
[ActionName("PostUser")]
public HttpResponseMessage PostUser([FromBody]string id)
{
var accessToken = id;
var client = new FacebookClient(accessToken);
dynamic result = client.Get("me", new { fields = "name,email" });
string name = result.name;
string email = result.email;
var existingUser = this.Repository.FindByUserIdentity(name);
if (existingUser == null)
{
var newUser = new User
{
Username = name,
Email = email,
};
var success = this.Repository.CreateAccount(newUser);
if (!success)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
//return created status code as we created the user
return Request.CreateResponse<User>(HttpStatusCode.Created, newUser);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I have no idea where am going wrong. Please let me know if u have any suggestions.
EDIT:
my Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
}
Dependency resolver am using
// Provides a Ninject implementation of IDependencyScope
// which resolves services using the Ninject container.
public class NinjectDependencyScope : IDependencyScope
{
IResolutionRoot resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.GetAll(serviceType);
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
}
// This class is the resolver, but it is also the global scope
// so we derive from NinjectScope.
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
and calling it in CreateKernel() method in NinjectWebCommon
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
// Install our Ninject-based IDependencyResolver into the Web API config
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
Suggestion Repository
public class SuggestionRepository : Repository<Suggestion>, ISuggestionRepository
{
public SuggestionRepository(IServiceContext<Suggestion> servicecontext)
: base(servicecontext)
{ }
public bool CreateSuggestion(Suggestion suggestion)
{
this.ServiceContext.Create(suggestion);
this.ServiceContext.Save();
return true;
}
}
ISuggestionRepository
public interface ISuggestionRepository
{
bool CreateSuggestion(Suggestion suggestion);
}
Repository
public abstract class Repository<T>
{
public IServiceContext<T> ServiceContext { get; private set; }
public Repository(IServiceContext<T> serviceContext)
{
this.ServiceContext = serviceContext;
}
}
IserviceContext
public interface IServiceContext<T>
{
IQueryable<T> QueryableEntities { get; }
void Create(T entity);
void Update(T entity);
void Delete(T entity);
void Save();
}
Since you're using WebApi, you will need to use the WebApi extension for Ninject. Unfortunately, the current Ninject.WebApi nuget package is out of date, and doesn't work with the released version of WebApi.
Temporarily, until Remo gets around to updating Ninject.WebApi to the release version, you can use Ninject.WebApi-RC http://nuget.org/packages/Ninject.Web.WebApi-RC
http://www.eyecatch.no/blog/2012/06/using-ninject-with-webapi-rc/
EDIT:
To recap the information discussed in comments, Here are the recommendations:
1) Use Ninject.MVC3 and Ninject.Web.WebApi (but use Ninject.Web.WebApi-RC until the official is updated) as discussed above. Do not use a custom DependencyResolver, and let Ninject.Web.Mvc and .WebApi do their job.
2) Change your bindings to this:
kernel.Bind<ICompetitionRepository>().To<CompetitionRepository>();
... similar bindings
3) Add a generic binding for your ServiceContext
kernel.Bind(typeof(IServiceContext<>)).To(typeof(InMemoryDataContext<>));
I think the problem is you're using the ApiController.
Controllers and apiControllers are using a different dependancy injection container.
Both of them however expose the same methods.
If the working controller is inheriting the Controller class then that's your cause.
For a work around take a look at
this topic
I have faced the same issue.
This is how I rectified:
I created a WebContainerManager which is just a static wrapper around container.
Static container wrappers useful when you don't control instantiation and can't rely on injection - e.g. action filter attributes
public static class WebContainerManager
{
public static IKernel GetContainer()
{
var resolver = GlobalConfiguration.Configuration.DependencyResolver as NinjectDependencyResolver;
if (resolver != null)
{
return resolver.Container;
}
throw new InvalidOperationException("NinjectDependencyResolver not being used as the MVC dependency resolver");
}
public static T Get<T>()
{
return GetContainer().Get<T>();
}
}
Inside your controller, call your empty constructor like this with no parameters:
public SuggestionController() : this(WebContainerManager.Get<ISuggestionRepository>())
{
}
This should work.
This technique i got from the book on MVC4 by Jamie Kurtz #jakurtz.
You probably need to do some dependency injection so you can inject the ISuggestionRepository parameter on your SuggestionController constructor. To do that you need to override methods in the DefaultControllerFactory class to customize the creation of controllers. Since you are using NInject, you can have something like:
public class NInjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new CustomModule());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)kernel.Get(controllerType);
}
public class CustomModule : NinjectModule
{
public override void Load()
{
this.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
this.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
this.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
this.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
}
}
Then in your Global.asax.cs, you can add a line to swap out the controller factory
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new NInjectControllerFactory());
}