I have 2 controllers CustomerController and PrivatemessageController
Customers has a nonaction method
private readonly ICustomerService _customerService;
public Customer(....) << autofac Ioc
{
}
[NonAction]
protected CustomerNavigationModel GetCustomerNavigationModel(Customer customer)
{
var model = new CustomerNavigationModel();
.... _customerSerice...
return model;
}
I'd like to get GetCustomerNavigationModel value from CustomerController as I don't want to recreate the same method for PrivateMessageController, is that possible in ASP.NET MVC 3.0 ?
Two options come to mind here.
Make the method public static:
[NonAction]
public static CustomerNavigationModel GetCustomerNavigationModel(Customer customer)
{
var model = new CustomerNavigationModel();
.... _customerSerice...
return model;
}
Create a base controller and implement the method there. Have both your controllers derive from your base controller:
public abstract class MyBaseController : Controller
{
[NonAction]
protected CustomerNavigationModel GetCustomerNavigationModel(Customer customer)
{
var model = new CustomerNavigationModel();
.... _customerSerice...
return model;
}
}
public class CustomerController : MyBaseController
{
....
}
public class PrivatemessageController : MyBaseController
{
....
}
Refactor the method into a separate class and use that class from your controllers. You can also just make the method static, and call it from PrivatemessageController using:
CustomerController.GetCustomerNavigationModel(customer);
Related
I have the follow base controller-
public abstract class BaseController : Controller
{
protected string BaseUrl = "URL";
}
All other controller inherit the above base controller-
public class MyController : BaseController
{
public ActionResult Something()
{
ViewBag.BaseUrl = base.BaseUrl;
return View();
}
}
I don't want to write ViewBag.BaseUrl = base.BaseUrl; in every controller action method. Rather, I would to automatically pass this base url to the related view. Is it possible by overriding View()?
An example would be better for me.
If all controllers derive this then just put it in here:
public abstract class BaseController : Controller
{
protected string BaseUrl = "URL";
public BaseController()
{
ViewBag.BaseUrl = base.BaseUrl;
}
}
I would even make it private if I do not want inheriting classes to overwrite it.
I have 2 controllers, one inheriting the other. I need to override an ActionResult from the base controller because I need to change to code to implement pagination in the plugin for nopCommerce. However, I get an AmbiguousMatchException because of the new ActionResult.
Base Controller:
public class BaseController : Controller {
public ActionResult Category(int categoryId, CatalogPagingFilteringModel command)
{
//original action result code
}
}
Customer Controller w/ inheritance
public class CustomController : BaseController {
public new ActionResult Category(int categoryId, CatalogPagingFilteringModel command)
{
// my pagination code with different model/view
}
}
Route Info:
Here I remove the route for the base controller and add a new route to use the CustomCatalog controller.
routes.Remove(routes["OriginalCategory"]);
routes.MapLocalizedRoute(
"OriginalCategory",
"Category/{categoryId}/{SeName}",
new { controller = "CustomCatalog", action = "Category", SeName = UrlParameter.Optional },
new { categoryId = #"\d+" },
new[] { "Nop.Plugin.Common.Web.Controllers" });
I then get an AmbiguousMatchException
[AmbiguousMatchException: The current request for action 'Category' on
controller type 'CustomCatalogController' is ambiguous between the
following action methods: System.Web.Mvc.ActionResult Category(Int32,
Nop.Web.Models.Catalog.CatalogPagingFilteringModel) on type
Nop.Plugin.Common.Web.Controllers.CustomCatalogController
System.Web.Mvc.ActionResult Category(Int32,
Nop.Web.Models.Catalog.CatalogPagingFilteringModel) on type
Nop.Web.Controllers.CatalogController]
EDIT
The base controller resides in the core of the application where as the CustomController is in the plugin meaning that I cannot modify the base controller's type.
How about using virtual in the base controller and override in the derived controller instead of new?
base:
public virtual ActionResult Category(...) { }
derived:
public override ActionResult Category(...) { }
You cannot override a method which has not been declared virtual.
You can either define a new method with a different signature or you can encapsulate the functionality of the original class by maintaining a private reference to it in a wrapper class. It helps if you have an interface that you can implement from the base library (because this allows you to substitute the wrapper class where the base class that implements the same interface would be used), but you can do it without the interface, too.
// Base class source code is not modifiable
class BaseClass {
public ActionResult Category(...) {}
public ActionResult Other() {}
}
// Wrapper class can modify the behavior
class Wrapper {
private BaseClass baseClass = new BaseClass(); // Instantiate appropriately
public ActionResult Category(...) {
// do some stuff
}
public ActionResult Other() {
return baseClass.Other();
}
}
I am using trying to get my head round unit testing with DI/Mocks (Ninject/Moq) to inject a Product repository into my controller which I then pass to my view model in order to allow testing of that.
It is working great and is allowing me to unit test the controller action and the view model. However, when I run the application I get "No parameterless constructor defined for this object"....now I know this is due to the controller trying to initialise the View Model which doesn't have a parameterless constructor.
I could create the constructor and call my concrete repository from that (so unit tests still use the injected/mocked one).
Is this the correct approach? Any advice would be greatly appreciated!
Controller:
public class ProductsController : Controller
{
private readonly IProductRepository productRepository;
public ProductsController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
//
// GET: /Products/
public ActionResult Index(ViewModels.ProductsIndex vm)
{
return View(vm);
}
}
View model:
public class ProductsIndex
{
public List<CTEntities.Product> ProductList { get; set; }
public ProductsIndex(IProductRepository prods)
{
ProductList = prods.List().ToList();
}
//Adding this constructor would fix my issue but is there a cleaner way?
public ProductsIndex()
{
var prod = new CTDAL.Product();
ProductList = prod.List().ToList();
}
}
Your View Model should be a DTO (Data Transfer Object) ... in that it should only contain properties, and it should NOT be responsible for getting data. Your controller should get the data for the View Model, like so:
New View Model:
public class ProductsIndex
{
public List<CTEntities.Product> ProductList { get; set; }
}
New Controller:
public class ProductsController : Controller
{
private readonly IProductRepository productRepository;
public ProductsController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
//
// GET: /Products/
public ActionResult Index()
{
var products = productRepository.List().ToList();
return View(products);
}
}
One approach would be to let Ninject resolve your ProductsIndex instances. If you do that, it will automatically fill in the constructor arguments as required, provided IProductRepository is resolved using Ninject as well.
In your case could be something like:
Kernel.Bind<IProductRepository>().To<CTDAL.Product>();
Kernel.Bind<ProductsIndex>().ToSelf();
Now your ProductsController could look like this:
public class ProductsController : Controller
{
private readonly IProductRepository productRepository;
private readonly ProductsIndex productsIndex;
public ProductsController(IProductRepository productRepository,
ProductsIndex productsIndex)
{
this.productRepository = productRepository;
this.productsIndex = productsIndex;
}
//
// GET: /Products/
public ActionResult Index()
{
return View(productsIndex);
}
}
Keep in mind that this would not allow for request values to be mapped to your Viewmodel, so the data in your Viewmodel would only be dependent on the parameters you pass into it at the time you resolve it (in this case the product repository).
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);
}
}
In asp.net MVC, dependency injection with controllers is simple and straightforward. Now, I'd like to remove most of the logic from views by using helpers. The problem is that these helpers use some of the objects that are injected.
Let me write an example:
public interface ISessionData
{
List<string> IdList {get;}
}
public MyController : Controller
{
public MyController(ISessionData sessionData)
{
...
}
}
session data is injected into controller. So far so good. But now I have a helper. Let's say it looks like this:
public class MyHelper
{
private readonly ISessionData sessionData;
public MyHelper(ISessionData sessionData)
{
this.sessionData = sessionData;
}
public bool CheckSomethingExistsInSession(string id)
{
return sessionData.IdList.Any(x => x.Id.Equals(id));
}
}
Now what? I'd like MyHelper to be injected into view. Only way I can see is adding this helper to model and passing it to view every time. Any other ideas?
In MVC it is better to pass ISessionData data from Controller to View (using ViewModel or ViewData):
ViewData["Session"] = sessionData.IdList.ToList();
And remove ISessionData dependency from the helper. Something like this:
public class MyHelper
{
//private readonly ISessionData sessionData;
public MyHelper(/*ISessionData sessionData*/)
{
//this.sessionData = sessionData;
}
public bool CheckSomethingExistsInSession(string id, IList<...> sessionData)
{
return sessionData.Any(x => x.Id.Equals(id));
}
}
In View:
<% var somethingExists = new MyHelper().CheckSomethingExistsInSession(
1, ViewData["Session"] as IList<...>); %>
UPDATED:
public static class MyHelper
{
public static bool CheckSomethingExistsInSession(string id, IList<...> sessionData)
{
return sessionData.Any(x => x.Id.Equals(id));
}
}
<% var somethingExists = MyHelper.CheckSomethingExistsInSession(
1, ViewData["Session"] as IList<...>); %>
You should remove session logic from your controller's constructor and insert it into the controllers action method by using an IModelBinder. See below:
public class SessionDataModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Get/create session data implementating ISeesionData or whatever here. This will be return to the controller action method.
return new SessionData()
}
}
On you controller you would do something like:
public MyController : Controller
{
public MyController()
{
....
}
public ActionResult Index(ISessionData sessionData)
{
// do stuff with ISessionData.
// Redirect or whatever.
return this.RedirectToAction("Index");
}
}
You need to add your IModelBinder like below for it to be called. You can do this in the http application's startup.
System.Web.Mvc.ModelBinders.Binders[typeof(ISessionData)] = new SessionDataModelBinder();