Search code examples
asp.net-mvcasp.net-web-apimodelmodel-bindingaction-filter

Changing the type of action parameter at runtime depending on current user in aspnet webapi


How to alter the TViewModel from within a action filter or a model binder?

   [HasPriviliege]
   public IHttpActionResult Get(long id)
    {
        var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
        repo.Update(id, entity);
        repo.Save();
        return Ok(model);
    }

   [HasPriviliege]
   public IHttpActionResult Edit(long id, TViewModel model)
    {
        var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
        repo.Update(id, entity);
        repo.Save();
        return Ok(model);
    }

the filter should be

public class HasPriviliege:ActionFilterAttribute
{ 
    public override void OnActionExecuting(HttpActionContext actionContext)
    { 
        if(getPrivileges()=="doctor"){
           //the TViewModel(view model type to bind to) should be
           // DoctorPatientViewModel should be;
        }else{
           //the TViewModel(view model type to bind to) should be 
            //ExaminationPatientViewModel
     }
        //base.OnActionExecuting(actionContext);
    }
}

or alternativaly, the model binder

 public class IPrivilegeableModelBinder: IModelBinder
{
      public object BindModel(ControllerContext controllerContext, 
                          ModelBindingContext bindingContext)
      { 
    //return (hasPriviliege()?DoctorPatientViewModel:ExaminationPatientViewModel) ;
}

}


Solution

  • Rather than write an over-bloated comment, I'll post my suggestion on how we accomplished something similar to this using a generic controller.

    Controller factory:

    public class ControllerFactory : IControllerFactory
    {
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            Type controllerType = typeof(GenericController<>);
            Type genericType = controllerType.MakeGenericType(GetPrivilegeType());
            ConstructorInfo ctor = genericType.GetConstructor(new Type[]{});
            return (IController)ctor.Invoke(new object[] { });
        }
    
        public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
        {
            ...
            return SessionStateBehavior.ReadOnly;
        }
    
        public void ReleaseController(IController controller)
        {
            if (controller is IDisposable)
            {
                ((IDisposable)controller).Dispose();
            }
        }
    
        private string GetPrivilegeType()
        {
            if (getPrivileges() == "doctor") {
                return typeof(DoctorPatientViewModel);
            } else {
                return typeof(ExaminationPatientViewModel);
            }
        }
    }
    

    Register it like this:

    ControllerBuilder.Current.SetControllerFactory(new ControllerFactory());
    

    ...and finally what your controller might look like

    public class GenericController<TViewModel> // TViewModel will be the privilege type from the factory
        where TViewModel : IPrivilege
    {
        [HasPriviliege]
        public IHttpActionResult Edit(long id, TViewModel model)
        {
            var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
            repo.Update(id, entity);
            repo.Save();
            return Ok(model);
        }
    }
    

    That's the most basic example to get a generic controller working for mvc which might go some way to what you're trying to accomplish.