I want to route all of my calls to the database through my web api controller. The problem is I am using ninject to dependency inject services into my web api controller. So when My MVC controller needs to call to the web api controller I cannot just instantiate a new instance of it or I have to pass all of the injected services I use into the MVC controller and then reInject them as parameters to the api controller:
UserController : Controller
{
{
private readonly IGroupService _groupService;
public UserController(IGroupService groupService)
{
_groupService = groupService;
}
public ActionResult Index()
{
var model = new UserModel();
model.Group = _groupService.get();
//I don't want to do this
}
}
public class UserApiController : ApiController
{
private readonly IUserService _userService;
private readonly IStatusConverter _statusConverter;
public UserApiController(IUserService userService, IStatusConverter statusConverter)
{
_userService = userService;
_statusConverter = statusConverter;
}
public IEnumerable<WebUser> Get()
{
return _userService.Get();
}
I cannot instantiate a new ApiController in my MVC controller without adding unused injection to the mvc controller. Any suggestions?
Related
I have a C# solution with two projects, ProductStore.Web and ProductStore.Data, both targeting .NET Core 2.0.
I have my HomeController and CustomerRepository as follows (I've set it up in the HomeController for speed, customer creation will be in the customer controller, but not yet scaffold-ed it out):
namespace ProductStore.Web.Controllers
{
public class HomeController : Controller
{
private readonly DatabaseContext _context;
public HomeController(DatabaseContext context)
{
_context = context;
}
public IActionResult Index()
{
ICustomerRepository<Customer> cr = new CustomerRepository(_context);
Customer customer = new Customer
{
// customer details
};
//_context.Customers.Add(customer);
int result = cr.Create(customer).Result;
return View();
}
}
}
namespace ProductStore.Data
{
public class CustomerRepository : ICustomerRepository<Customer>
{
DatabaseContext _context;
public CustomerRepository(DatabaseContext context)
{
_context = context;
}
}
}
Dependency Injection resolves _context automatically inside the controller. I am then passing the context as a parameter for CustomerRepository which resides in ProductStore.Data.
My question is two fold:
Is this best practice (passing the context from controller to CustomerRepository)
If not best practice, can I access context via IServiceCollection services in a similar way to how the DatabaseContext is inserted into services in my application StartUp.cs class...
I feel like I shouldn't have to pass the context over, CustomerRepository should be responsible for acquiring the context.
FYI, relatively new to MVC and brand new to Entity Framework and Dependency Injection
Thanks
You don't need to pass context to controller to be able to use the context registered in services inside repository. The way I prefer to do that, is the following. Inject context into repository and then inject repository into controller. Using the Microsoft Dependency Injection Extension in for .Net Core it will look like this
// Service registrations in Startup class
public void ConfigureServices(IServiceCollection services)
{
// Also other service registrations
services.AddMvc();
services.AddScoped<DatabaseContext, DatabaseContext>();
services.AddScoped<ICustomerRepository<Customer>, CustomerRepository>();
}
// Controller
namespace ProductStore.Web.Controllers
{
public class HomeController : Controller
{
private readonly ICustomerRepository _customerRepository;
public HomeController(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
public IActionResult Index()
{
Customer customer = new Customer
{
// customer details
};
//_context.Customers.Add(customer);
int result = _customerRepository.Create(customer).Result;
return View();
}
}
}
//Repository
namespace ProductStore.Data
{
public class CustomerRepository : ICustomerRepository<Customer>
{
DatabaseContext _context;
public CustomerRepository(DatabaseContext context)
{
_context = context;
}
}
}
After this when DependencyResolver tries to resolve ICustomerRepository to inject into the HomeController he sees, that the registered implementation of ICustomerRepository (in our case CustomerRepository) has one constructor which needs DatabaseContext as a parameter and DependencyResolver trying to to get registered service for DatabaseContext and inject it into CustomerRepository
If you define your repository in your ConfigureServices method, you won't need to inject the DbContext into controller, just the repository:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped(typeof(ICustomerRepository<>), typeof(CustomerRepository<>));
}
Then you can just simply inject the repository into controller:
public class HomeController : Controller
{
private readonly ICustomerRepository _customerRepository;
public HomeController(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
...
}
The dependency injector takes care of injecting DbContext into your repository.
1. Is this best practice (passing the context from controller to CustomerRepository)
I think you're looking for something like a "Unit of Work" pattern.
Microsoft has written a tutorial about creating one here.
I would also inject the repository in your controller instead of your
context.
2. If not best practice, can I access context via IServiceCollection services in a similar way to how the DatabaseContext is inserted into services in my application StartUp.cs class...
If I understand you correctly, than yes, you can. Also add the
CustomerRepository to the services in your StartUp.cs so you can use
it in your controller.
Mabye this tutorial from Microsoft will also help you.
I'm trying to setup a project that uses both MVC and Web API via OWIN and I'm having trouble getting Autofac to take a effect.
Here's how I'm initializing Web API:
public partial class Startup
{
public static void ConfigureWebApi(IAppBuilder app)
{
var config = BuildHttpConfiguration();
var container = AutoFacConfig.BuildContainer();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
private static HttpConfiguration BuildHttpConfiguration()
{
var config = new HttpConfiguration();
// Response formatter config
config.Formatters.Remove(
GlobalConfiguration.Configuration.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
// Setup Web API error reporting
var customErrors = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
IncludeErrorDetailPolicy errorDetailPolicy;
switch (customErrors.Mode)
{
case CustomErrorsMode.RemoteOnly:
errorDetailPolicy
= IncludeErrorDetailPolicy.LocalOnly;
break;
case CustomErrorsMode.On:
errorDetailPolicy
= IncludeErrorDetailPolicy.Never;
break;
case CustomErrorsMode.Off:
errorDetailPolicy
= IncludeErrorDetailPolicy.Always;
break;
default:
throw new ArgumentOutOfRangeException();
}
config.IncludeErrorDetailPolicy = errorDetailPolicy;
config.MapHttpAttributeRoutes();
SwaggerConfig.ConfigureSwagger(config);
return config;
}
}
The BuildContainer() method looks like the following. This method is used to build the container for both MVC and Web API:
public static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// Register your MVC controllers.
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired();
builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
// OPTIONAL: Register model binders that require DI.
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();
// OPTIONAL: Register web abstractions like HttpContextBase.
builder.RegisterModule<AutofacWebTypesModule>();
// OPTIONAL: Enable property injection in view pages.
builder.RegisterSource(new ViewRegistrationSource());
// OPTIONAL: Enable property injection into action filters.
builder.RegisterFilterProvider();
// Bind the core types
Core.Infrastructure.AutoFacConfig.BuildContainer(builder);
builder.RegisterType<Postal.EmailService>().As<Postal.IEmailService>();
// Effectively auto-wires the anything with an interface within Web assembly infrastructure folder
builder.RegisterAssemblyTypes(typeof(IJwtHelper).Assembly)
.Where(t => t.Namespace != null && t.Namespace.StartsWith("MyApp.Web.Infrastructure") && t.GetInterfaces().Any())
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
// Set the dependency resolver to be Autofac.
return builder.Build();
}
I have Web API controllers setup in an area and I had everything working with standard MVC controllers. I would like to use Web API controllers for where it's appropriate, but every time I try to request a controller based on ApiController I get the error:
An error occurred when trying to create a controller of type 'XxxController'. Make sure that the controller has a parameterless public constructor.
-- Edit --
The API area config looks like the following. (Side note: I know this isn't the "proper" way to configure Web API. I include the {action} because I feel there are too many controller files otherwise.)
public class ApiAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Api"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"Api_default",
"Api/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
The authentication controller looks like this:
public class AuthenticationController : MyAppApiJwtController
{
private readonly IUserRepository _userRepository;
private readonly IAppSettingsHelper _appSettingsHelper;
private readonly IJwtHelper _jwtHelper;
private readonly IDeviceRepository _deviceRepository;
private readonly IINLDataService _inlDataService;
public AuthenticationController(IUserRepository userRepository, IAppSettingsHelper appSettingsHelper, IJwtHelper jwtHelper, IDeviceRepository deviceRepository, IINLDataService inlDataService)
{
_userRepository = userRepository;
_appSettingsHelper = appSettingsHelper;
_jwtHelper = jwtHelper;
_deviceRepository = deviceRepository;
_inlDataService = inlDataService;
}
[HttpPost]
[AllowAnonymous]
public LoginResponseModel Login(LoginModel model)
{
...
}
}
The MyAppApiJwtController looks like this:
public class MyAppApiJwtController : ApiController
{
internal IAuthenticationManager AuthenticationManager
{
get { return Request.GetOwinContext().Authentication; }
}
private JwtUserIdentity _currentJwtUser;
public JwtUserIdentity CurrentJwtUser
{
get
{
if (_currentJwtUser != null)
return _currentJwtUser;
if (User == null)
return null;
_currentJwtUser = new JwtUserIdentity((ClaimsIdentity)User.Identity);
return _currentJwtUser;
}
}
}
-- Edit 2 --
The URL I'm attempting to use is http://localhost:20630/api/authentication/login
-- Edit 3 --
The MVC configuration looks like the following. This is called just before the Web API configuration:
public partial class Startup
{
public static void ConfigureMvc()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
MvcConfig.RegisterRoutes(RouteTable.Routes);
MvcConfig.ValueConfig();
BundleConfig.RegisterBundles(BundleTable.Bundles);
AutomapperConfig.Configure();
JsonConfig.Configure();
AutoFacConfig.ConfigureContainer();
}
}
ASP.NET Web API does not support MVC areas by default.
This code:
public class ApiAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Api"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"Api_default",
"Api/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
Will instruct the framework to map any route starting with Api to MVC controllers, but, at the same time, such controllers exists only for Web API. This conflict directly relates to the exception thrown when the Dependency Resolver tries to create an instance of the controller (the exception message may be misleading).
What could be happening:
MVC is executed first, and tries to map your route api/authentication/login. This URI matches your AreaRegistration, so it will try to route the request to the AuthenticationController inside your Api area.
MVC asks the Dependency Resolver to create an instance of the above controller, that must inherit from Controller (that's because we are in the MVC context).
The resolver does not have a registration for a MVC Controller that is called AuthenticationController (the AuthenticationController inherits from ApiController), and it returns null (because that's the expected behavior of the IServiceProvider.GetService method).
MVC then reverts to its default implementation for creating the controller, but finds that AuthenticationController class does not have a parameterless constructor. An exception is thrown.
Please try by removing this area declaration: it is not useful (add your api prefix inside RoutePrefix or Route attributes for your controllers/actions) and works only for MVC, while you are defining Web API as an OWIN middleware.
Reference:
ASP.Net WebAPI area support
I am trying to make sure that my MVC application only uses one DbContext per Request in order to reduce number of times a Db connection is open and so there are no concurrency issues.
This means i will need to use the same context in my Global Action Filters as well as my Controllers.
I have tried something like this
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser loggedInUser = Membership.GetUser();
MyUnitOfWork uow = new MyUnitOfWork();
ViewBag.FullName = uow.UserService.GetUser().FullName
filterContext.ActionParameters["unitOfWork"] = uow;
}
}
However the context is disposed when i try to read it from the controller as shown below
public ActionResult Logout(MyUnitOfWork uow)
{
ViewBag.Something = uow.ExampleService.GetMyObject();
return RedirectToAction("LogIn");
}
I get the same issue with the context being disposed when i try to share the same unitOfWork object by casting a property of a base controller class
public class BaseController : Controller
{
public RequestboxUnitOfWork unitOfWork;
}
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser loggedInUser = Membership.GetUser();
BaseController baseController = (BaseController)filterContext.Controller;
ViewBag.FullName = baseController.unitOfWork.UserService.GetUser().FullName
filterContext.ActionParameters["unitOfWork"] = uow;
}
}
The context is disposed when i try to access it in the controller and i have read in a few places that you should not use base controller class so i am unsure of what i can do.
What are the recommended ways to share a entity context between ActionFilters and Controllers
Create the DBContext as part of the Controller setup, and have it available via an internal property on the controller.
public class MyController : Controller
{
private MyUnitOfWork unitOfWork;
internal MyUnitOfWork UnitOfWork
{
get { return unitOfWork; }
}
}
You will then be able to access the context in the filter attribute like this:
MyController controller = (MyController)filterContext.Controller
MyUnitOfWork uow = controller.UnitOfWork;
There's no need to pass the unit of work back to the action method in the controller, because the controller already has the object, and it can be accessed via the same internal property.
Using Ninject in a MVC4 application with the MVC3 Ninject Extension, when binding to a repository (a DbContext), I want to use InRequestScope for Controllers, and InSingletonScope when the same repository is used within a custom MembershipProvider.
I want to do this:
kernel.Bind<IRepo>().To<Repo>().InRequestScope();
kernel.Bind<IRepo>().To<Repo>().WhenInjectedInto<MembershipHelper>()
.InSingletonScope();
So that each web request to a controller gets a fresh instance of the repo, while the MembershipProvider maintains a re-usable connection to the repo.
This appears to work ok in a development environment, but how do I know which binding is being used? Is there a way to test that the scope is working correctly?
Yes, this is fine. Here's a very easy way to test it:
public interface IRepo { }
public class Repo : IRepo { }
public class MembershipHelper
{
private readonly IRepo _repo;
public MembershipHelper(IRepo repo)
{
_repo = repo;
}
public string GetId()
{
return _repo.GetHashCode().ToString();
}
}
public class HomeController : Controller
{
private readonly IRepo _repo;
private readonly MembershipHelper _helper;
public HomeController(IRepo repo, MembershipHelper helper)
{
_repo = repo;
_helper = helper;
}
public ActionResult Index()
{
return Content(_repo.GetHashCode().ToString() + " " + _helper.GetId());
}
}
Now navigate to /home/index and observe the 2 hashcodes. The first changes on each request whereas the second remains the same.
I'm using Ninject to do some dependancy injection. (Mainly for the DAL), my project consists of 3 aspects and are as follows,
Project.Lib (Everything database,
services and anythign else that is
logic)
Project.Admin (Administration)
Project.Web (Front end what the user
see's)
Now, each of my controllers within my projects inherit from a BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISession session)
{
_Session = session;
}
public ISession _Session { get; private set; }
}
And then and example controller might be like so,
public class ImageController : BaseController
{
private MediaService _mediaService;
public ImageController(ISession session) : base(session)
{
_mediaService = new MediaService(session);
}
[HttpGet]
public ActionResult List()
{
var RetVal = _mediaService.GetAllImages();
return View(RetVal);
}
}
As you can see the "Session" is passed from the controller to the service layer. I'm curious as to if this is good practie? ANy negitives to what we are doing here?
I'd avoid referencing ISession through your controller. A better solution would be to use Ninject to inject your services into your controllers. In this instance you'll need to create an abstraction for your MediaService class e.g.:
public interface IMediaService
{
SomeCollection GetAllImages();
// ...
}
You'd then use Ninject to supply an implementation of the above interface to your controller:
public class ImageController : BaseController
{
private IMediaService _mediaService;
public ImageController(IMediaService mediaService)
{
_mediaService = mediaService
}
[HttpGet]
public ActionResult List()
{
var RetVal = _mediaService.GetAllImages();
return View(RetVal);
}
}