.NET MVC 5.2 Inheriting attribute Routes from Base Controller - asp.net-mvc

I have a base controller that contains several actions that I would like to use attribute routing on, and not have to override these methods in the controllers that inherit from the base.
As of .NET MVC 5.2 this should be possible according to this: http://www.asp.net/mvc/overview/releases/whats-new-in-aspnet-mvc-52
The example provided shows how to use a class level attribute, but I would like to Implment it at the action level. Has anyone successfuly implemented inheritance of attributes on the action level?
I see another answer .NET WebAPI Attribute Routing and inheritance claims this is possible in with web API controllers, but can it be done using the standard MVC controller?
[InheritedRoute("attributerouting/{controller}/{action=Index}/{id?}")]
public abstract class BaseController : Controller
{
}
public class BlogController : BaseController
{
public string Index()
{
return "Hello from blog!";
}
}
public class StoreController : BaseController
{
public string Index()
{
return "Hello from store!";
}
}
[AttributeUsage(AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class InheritedRouteAttribute : Attribute, IDirectRouteFactory
{
public InheritedRouteAttribute(string template)
{
Template=template;
}
public string Name { get; set; }
public int Order { get; set; }
public string Template { get; private set; }
public new RouteEntry CreateRoute(DirectRouteFactoryContext context)
{
// context.Actions will always contain at least one action - and all of the
// actions will always belong to the same controller.
var controllerDescriptor=context.Actions.First().ControllerDescriptor;
var template=Template.Replace("{controller}",
controllerDescriptor.ControllerName);
IDirectRouteBuilder builder=context.CreateBuilder(template);
builder.Name=Name;
builder.Order=Order;
return builder.Build();
}
}
// Custom direct route provider which looks for route attributes of type
// InheritedRouteAttribute and also supports attribute route inheritance.
public class InheritedDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList
GetControllerRouteFactories(ControllerDescriptor controllerDescriptor)
{
return controllerDescriptor
.GetCustomAttributes(typeof(InheritedRouteAttribute), inherit: true)
.Cast()
.ToArray();
}
}

I think I have the action level working with the following code:
public class InheritedDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(ActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes(typeof(IDirectRouteFactory), inherit: true).Cast<IDirectRouteFactory>().ToArray();
}
}
and call:
routes.MapMvcAttributeRoutes(new InheritedDirectRouteProvider());
this lets me inherit the controller and its routines from an abstract controller, simplified example:
// Inherited class needs to define [RoutePrefix("childPrefix")]
public abstract class ChildBaseController<TChildEntity> : BaseController where TChildEntity : ChildEntity
{
public ChildBaseController(IUnitOfWork DAL) : base(DAL) { }
protected abstract GenericChildRepository<TChildEntity> ChildRepository { get; }
protected abstract string[] GetCreateBindFields();
protected abstract string[] GetEditBindFields();
[Route("{valueId}")]
public ActionResult Index(int valueId)
{
ViewBag.ValueId = valueId;
return View(ChildRepository.Get().Where(cr => cr.ValueId == valueId));
}
... bunch more CRUD actions with [Route(...)] ...
}
inherited class:
namespace Web.Frontend.Controllers
{
[RoutePrefix("Fields")]
public class FieldsController : ChildBaseController<Field>
{
public FieldsController(IUnitOfWork DAL) : base(DAL) { }
protected override GenericChildRepository<Field> ChildRepository
{
get
{
return DAL.Fields;
}
}
protected override string[] GetCreateBindFields()
{
return new string[] { ... };
}
protected override string[] GetEditBindFields()
{
return new string[] { ... };
}
}
}

Related

How can I access hte data from the method GET through another controller?

I have two controllers: OcorrenciasAPIController and IgnicoesAPIController. In my OcorrenciasAPIController I need to access the data from the method GET of my IgnicoesAPIController.
Here is what I'm doing to doing in my OcorrenciasAPIController, to get the data from the method GET:
IgnicoesAPIController controller = null;
controller.GetIgnicoes();
I'm not sure if this is the right approach. Am I doing it right or does this have some problems? If so, is there another way of doing this?
Here is a demo to show using a method from another controller:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<AController,AController>();
...
}
A Controller:
public class AController : Controller
{
public IActionResult Index()
{
return View();
}
public string ReturnResult() {
return "success";
}
}
B Controller:
public class BController : Controller
{
private AController aController;
public BController(AController a) {
aController = a;
}
public IActionResult Index()
{
var s = aController.ReturnResult();
return Ok(s);
}
}
result:
If you have a common method in your controllers then you need to have a common class for both of controllers,In the first place you need to have a common base controller
public class BaseController:ApiController
{
public void CommonMethod()
{
}
}
then inherit from than by your controllers
public class ApiControllerA : BaseController
{
public void MethodA()
{
base.CommonMethod();
}
}
And also in second controller
public class ApiControllerB : BaseController
{
public void MethodB()
{
base.CommonMethod();
}
}
Now Put what is in your get method in common method to be accessible in both of of controllers

How do I pass variables to a custom ActionFilter in ASP.NET MVC app

I have a controller in my MVC app for which I'm trying to log details using a custom ActionFilterAttribute, by using the onResultExecuted method.
I read this tutorial to understand and write my own action filter. The question is how do I pass variables from the controller to the action filter?
I want to get the input variables with which a controller is called. Say, the username/user ID.
If (in some situations) an exception is thrown by any controller method, I would want to log the error too.
The controller -
[MyActionFilter]
public class myController : ApiController {
public string Get(string x, int y) { .. }
public string somemethod { .. }
}
The action filter -
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void onActionExecuted(HttpActionExecutedContext actionExecutedContext) {
// HOW DO I ACCESS THE VARIABLES OF THE CONTROLLER HERE
// I NEED TO LOG THE EXCEPTIONS AND THE PARAMETERS PASSED TO THE CONTROLLER METHOD
}
}
I hope I have explained the problem here. Apologies if I'm missing out some basic objects here, I'm totally new to this.
Approach - 1
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
Action Method
[MyActionFilter]
public ActionResult Index()
{
ViewBag.ControllerVariable = "12";
return View();
}
If you pay attention to the screenshot, you can see the ViewBag information
Approach - 2
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
//Your Properties in Action Filter
public string Property1 { get; set; }
public string Property2 { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
Action Method
[MyActionFilter(Property1 = "Value1", Property2 = "Value2")]
public ActionResult Index()
{
return View();
}
I suggest another approach, and it is passing parameters to Action Filter as constractor.
[PermissionCheck(Permissions.NewUser)]
public ActionResult NewUser()
{
// some code
}
Then in the ActionFilter:
public class PermissionCheck : ActionFilterAttribute
{
public Permissions Permission { get; set; }
public PermissionCheck(Permissions permission)
{
Permission = permission;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (/*user doesn't have that permission*/)
{
filterContext.Result = new RedirectToRouteResult
(
new RouteValueDictionary
(
new {
controller = "User",
action = "AccessDeny",
error = "You don't have permission to do this action"
}
)
);
base.OnActionExecuting(filterContext);
}
}
}
Which Permissions is an ENUM like:
enum Permissions {NewUser, Edit, Delete, Update, ...}

How to avoid repositories that duplicate code

I have successfully setup a simple mvc application that lists teams. I'm using Ninject to inject the appropriate repository depending on the controller (thanks to stack overflow ;). All looks good, except that the repository code looks exactly the same. And I know that's wrong. So my TeamRepository has two classes (for now).
public class SwimTeamRepository : ITeamRepository<SwimTeam>
{
private readonly Table<SwimTeam> _teamTable;
public SwimTeamRepository(string connectionString)
{
_teamTable = (new DataContext(connectionString).GetTable<SwimTeam>());
}
public IQueryable<SwimTeam> Team
{
get { return _teamTable; }
}
}
public class SoccerTeamRepository : ITeamRepository<SoccerTeam>
{
private readonly Table<SoccerTeam> _teamTable;
public SoccerTeamRepository(string connectionString)
{
_teamTable = (new DataContext(connectionString).GetTable<SoccerTeam>());
}
public IQueryable<SoccerTeam> Team
{
get { return _teamTable; }
}
}
They look exactly the same except for the Class and Table name, so clearly I need to re-factor this. What would be the best approach here? Singleton? Factory Method?
Thanks in advance!
You could use generics:
public interface ITeamRepository<T>
{
}
public class TeamRepository<TTeam> : ITeamRepository<TTeam>
where TTeam : Team
{
private readonly Table<TTeam> _teamTable;
public TeamRepository(string connectionString)
{
_teamTable = (new DataContext(connectionString).GetTable<TTeam>());
}
public IQueryable<TTeam> Team
{
get { return _teamTable; }
}
}
public class Team
{
}
public class SwimTeam : Team
{
}
Then use it like so...
public void MyMethod()
{
var repository = new TeamRepository<SwimTeam>();
}
...and set up your IoC container w/ Ninject like so...
public class MyModule : NinjectModule
{
public override void Load()
{
Bind<ITeamRepository<SwimTeam>>
.To<TeamRepository<SwimTeam>>();
}
}
public void MyMethod()
{
var repository = kernel.Get<ITeamRepository<SwimTeam>>();
}
If you want to get REAL generic and have a single repository for ALL of your mapped classes, you can do something like this:
public interface IRepository
{
IQueryable<T> Get<T>() where T : class, new();
}
public class Repository : IRepository, IDisposable
{
private DataContext _dataContext;
public Repository(string connectionString)
{
_dataContext = new DataContext(connectionString);
}
public IQueryable<T> Get<T>()
where T : class, new()
{
return _dataContext.GetTable<T>().AsQueryable();
}
public void Dispose()
{
if (_dataContext != null)
{
_dataContext.Dispose();
_dataContext = null;
}
}
}
...which you could call like so (after setting up your Ninject container)...
using (var repository = kernel.Get<IRepository>())
{
var swimTeam = repository.Get<SwimTeam>();
}
Since Ninject takes care of the life-cycle management of your objects, you don't HAVE to wrap the repository in a using statement. In fact, you don't want to use a using statement there at all if you plan to use the repository more than once within the scope of its lifetime. Ninject will automatically dispose of it when it's life-cycle ends.
Here's a good article by Rob Conery on using this kind of technique to reduce the friction of using different ORMs.
EDIT by keeg:
I Think
public class TeamRepository<TTeam> : ITeamRepository<TTeam> where TTeam : Team {}
Should be
public class TeamRepository<TTeam> : ITeamRepository<TTeam> where TTeam : class {}
Please correct if I'm wrong.
Is this what you want?
public class TeamRepository : ITeamRepository<T>
{
private readonly Table<T> _teamTable;
public TeamRepository(string connectionString)
{
_teamTable = (new DataContext(connectionString).GetTable<T>());
}
public IQueryable<T> Team
{
get { return _teamTable; }
}
}

Can i have a baseController that has all my repositories when using StructureMap?

Historically my controllers have repositories declared on each controller which are injected through StructureMap and this is working fine for me.
But my new project will likely be using the same repositories for each controller.
Due to this i created a BaseController and inherit all controllers from here.
My repositories now live in Base but the injection is not working.
Can it work like this or does constructor injection have to take place on each controller?
public static void BootStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
scanner.AddAllTypesOf<IController>().NameBy(type => type.Name.Replace("Controller", "").ToLower());
});
x.For(typeof(IGenericRepository<>)).Use(typeof(GenericRepository<>));
});
}
Working:
public class TransactionController : Controller
{
public IGenericRepository<ITransaction> TransactionRepository { get; set; }
public TransactionController(IGenericRepository<ITransaction> transactionRepository)
{
this.TransactionRepository = transactionRepository;
}
public ActionResult Index()
{
var transactions = this.TransactionRepository.Query.AsEnumerable();
return View(transactions);
}
Not working:
public class BaseController : Controller
{
public IGenericRepository<ITransaction> TransactionRepository { get; set; }
public BaseController(IGenericRepository<ITransaction> transactionRepository)
{
this.TransactionRepository = transactionRepository;
}
protected BaseController()
{
}
}
public class TransactionController : BaseController
{
public ActionResult Index()
{
var transactions = base.TransactionRepository.Query.AsEnumerable();
return View(transactions);
}
}
You have to inject your repository into BaseController somehow. If your last piece of code is the real code you have then it seems that BaseController is initialized through protected parameterless constructor.
Add the constructor to TransactionController:
public TransactionController(IGenericRepository<ITransaction> transactionRepository) : base(transactionRepository)
{
}
Can use Poor Man's Dependency Injection - used in NerdDinner application
public BaseController() : this(new Message())
{
}
Or
Refer Phill's link
tdd-and-dependency-injection-with-asp.net-mvc.aspx
Override DefaultControllerFactory
public class SMControllarFactory : DefaultControllerFactory
In
application start
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new SMControllarFactory());

MVC 3 + IoC + NInject + Repositories + LINQ

I'm trying to work with NInject in my MVC 3 application, and i have one question.
Interface
public interface ITalesRepository
{
IEnumerable<Tale> GetAllTales();
}
Repository
public class TalesRepository : ITalesRepository
{
private FairyTalesMVC3DataContext _dataContext;
public TalesRepository(FairyTalesMVC3DataContext dataContext)
{
_dataContext = dataContext;
}
public IEnumerable<Tale> GetAllTales()
{
return _dataContext.Tales.OrderBy(c => c.NameAn);
}
}
Home controller
public class HomeController : Controller
{
private readonly ITalesRepository _talesRepository;
public HomeController(ITalesRepository talesRepository)
{
_talesRepository = talesRepository;
}
public ActionResult Index()
{
ViewBag.Tales = _talesRepository.GetAllTales();
return View();
}
}
So, i need to initialize my TalesRepository with DataContext, and now it is so:
private void RegisterDependencyResolver()
{
var kernel = new StandardKernel();
kernel.Bind<ITalesRepository>().To<TalesRepository>().WithConstructorArgument("dataContext", new FairyTalesMVC3DataContext(ConfigurationManager.ConnectionStrings["dbFairyTalesConnectionString"].ConnectionString));
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
}
So, my question, is it ok or something wrong?
First of all:
public IEnumerable<Tale> GetAllTales()
{
return _dataContext.Tales.OrderBy(c => c.NameAn);
}
I would add .ToList() to the end. Else you'll get data layer exceptions in your presentation layer which is not fine.
Next, I would recommend that you switch to ViewModels instead of using ViewBag. It's a lot easier to prevent that logic leaks into the views if you are using ViewModels. Since you can add the logic to the ViewModel and thus get the same behaviour in all views using the model.
Your application should inherit from NinjectHttpApplication. It registers dependency resolver, so you don't have to do it.
You should also override CreateKernel in application class and register your own module with bindings:
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
return new StandardKernel(new INinjectModule[] {new MvcModule()});
}
}
public class MvcModule : NinjectModule
{
public override void Load()
{
Bind<ITalesRepository>().To<TalesRepository>();
Bind<FairyTalesMVC3DataContext>().To<FairyTalesMVC3DataContext>().InRequestScope();
}
}

Resources