Passing parameters to modelbinder on action parameter - asp.net

I wish to use a modelbinder I've made directly on an action method parameter. Such as:
public ActionResult MyAction([ModelBinder(typeof(MyBinder))] string param1)
However, I need to pass a string into the binder itself, so I was wondering if you could do something along the lines of:
public ActionResult MyAction([MyBinder("mystring")] string param1)
Is it possible?

No, you cannot pass parameters via the attribute declaration. If you look at the source code for ModelBinderAttribute, you will see that its constructor takes only a type argument, that it has no other properties, and that it is a sealed class. So this road is a dead end.
The only way to get information into the ModelBinder that I know of is the FormCollection itself.
You could, however, make a parent binder type and subtype it for each param value which you intend to use. It's messy, but it would work in the example you give.

Yes, it's possible. You should a create class drived from System.Web.Mvc.CustomModelBinder, and override the method GetBinder. For example, here implementation that takes in addition to a Type object, also an object array for model binder constructor parameters:
public sealed class MyModelBinderAttribute : CustomModelBinderAttribute
{
/// <summary>
/// Gets or sets the type of the binder.
/// </summary>
/// <returns>The type of the binder.</returns>
public Type BinderType { get; }
/// <summary>
/// Gets or sets the parameters for the model binder constructor.
/// </summary>
public object[] BinderParameters { get; }
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Web.Mvc.ModelBinderAttribute" /> class.
/// </summary>
/// <param name="binderType">The type of the binder.</param>
/// <param name="binderParameters">The parameters for the model binder constructor.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="binderType" /> parameter is null.</exception>
public MyModelBinderAttribute(Type binderType, params object[] binderParameters)
{
if (null == binderType)
{
throw new ArgumentNullException(nameof(binderType));
}
if (!typeof(IModelBinder).IsAssignableFrom(binderType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"An error occurred when trying to create the IModelBinder '{0}'. Make sure that the binder has a public parameterless constructor.",
binderType.FullName), nameof(binderType));
}
this.BinderType = binderType;
this.BinderParameters = binderParameters ?? throw new ArgumentNullException(nameof(binderParameters));
}
/// <summary>Retrieves an instance of the model binder.</summary>
/// <returns>A reference to an object that implements the <see cref="T:System.Web.Mvc.IModelBinder" /> interface.</returns>
/// <exception cref="T:System.InvalidOperationException">An error occurred while an instance of the model binder was being created.</exception>
public override IModelBinder GetBinder()
{
IModelBinder modelBinder;
try
{
modelBinder = (IModelBinder)Activator.CreateInstance(this.BinderType, this.BinderParameters);
}
catch (Exception ex)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"An error occurred when trying to create the IModelBinder '{0}'. Make sure that the binder has a public parameterless constructor.",
this.BinderType.FullName), ex);
}
return modelBinder;
}
}

Just a thought, I am looking at needing a similar requirement and I wanted to pass in a castled service.
I wonder if you could use the ControllerContext passed into the Binder and expose the service/property or whever via a property?
Just a thought, I am going to try this now and I will feed back
Rich

Related

When do you need a parameterless constructor on a Command/DTO object for an API call?

I am getting the following exception thrown: "System.NotSupportedException: Deserialization of reference
types without parameterless constructor is not supported."
It is JSON being passed into the controller.
When do you need a parameterless constructor vs. one with parameters?
I am using ASP.NetCore.App.Ref 3.1.3 and NetCore.App.Ref 3.1.0.
Here is the class that it is saying needs a parameterless constructor:
public class JobApplicationStatusModel : BaseEntityModel
{
/// <summary>
///JobApplicationStatus ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// Description/Name of Status.
/// </summary>
public string StatusDescription { get; set; }
/// <summary>
/// Main constructor
/// </summary>
/// <param name="id">JobApplication StatusID</param>
/// <param name="statusDescription">Status Description</param>
public JobApplicationStatusModel(int id, string statusDescription)
{
Id = id;
StatusDescription = statusDescription ?? throw new ArgumentNullException(nameof(statusDescription));
}
/// <summary>
/// Returns an enumeration of all atomic values.
/// </summary>
/// <returns>An enumeration of all atomic values.</returns>
protected override IEnumerable<object> GetAtomicValues()
{
// Using a yield return statement to return each element one at a time
yield return Id;
yield return StatusDescription;
}
}

ASP.NET Inject Identity

I am trying to inject UserManager and UserManager with Ninject. But it gives an error.
What is wrong with the binding?
UserStore
public class UserStoreModule : NinjectModule
{
/// <summary>
/// The load.
/// </summary>
public override void Load()
{
Bind<IUserStore<Account>>()
.To<UserStore<Account>>()
.InRequestScope().WithConstructorArgument("context", context => Kernel.Get<RovinMediaContext>());
}
}
UserManager
public class UserManagerModule : NinjectModule
{
/// <summary>
/// The load.
/// </summary>
public override void Load()
{
Kernel.Bind<UserManager<Account>>().ToSelf();
}
}
Result to the following error
Error activating string No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency string into parameter connectionString of constructor of type RovinMediaContext
1) Request for MyDbContext

Testing ASP.NET Web API POST and Parameterless GET Routes with WebApiContrib.Testing

I am trying to set up some route tests using the WebApiContrib.Testing library. My get tests (like this) work fine...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"~/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>(c => c.Get("sjkfhiuehfkshjksdfh"));
}
I am rather lost on the post test - I currently have the following which fails with a NotImplementedException...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"~/auth".ShouldMapTo<AuthController>(c => c.Post(new AuthenticationCredentialsModel()), "POST");
}
Here's the setup and teardown for completeness...
[SetUp]
public void SetUpTest()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
[TearDown]
public void TearDownTest()
{
RouteTable.Routes.Clear();
GlobalConfiguration.Configuration.Routes.Clear();
}
The route I am trying to test is the default POST route, which maps to this method call...
[AllowAnonymous]
public HttpResponseMessage Post([FromBody] AuthenticationCredentialsModel model)
{ *** Some code here that doesn't really matter *** }
I am also getting a failure on this test that tests the standard GET route without parameters returns all of the items...
[Test]
[Category("VersionInfo Api Tests")]
public void TheVersionInfoControllerAcceptsAMultipleItemGetRouteForAllItems()
{
"~/versioninfo".ShouldMapTo<VersionInfoController>(c => c.Get());
}
Which is testing this method...
public HttpResponseMessage Get()
{ *** Some code here that doesn't really matter *** }
This library was recommended by several articles I read, but I'm not sure now if I'm doing something wrong or if it's just quite limited and I'm better off rolling my own.
There seems to be some nasty issues here - after debugging through the WebApiContrib.Testing library, I've found the following...
In the route mapping code used for the libraries own tests, the route mappings look like this...
// GET /api/{resource}
routes.MapHttpRoute(
name: "Web API Get All",
routeTemplate: "api/{controller}",
defaults: new {action = "Get"},
constraints: new {httpMethod = new HttpMethodConstraint("GET")}
);
My route mapping looks like this for essentially the same route...
config.Routes.MapHttpRoute("DefaultApiGet", "{controller}",
new { action = "Get" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
Notice the different syntax for constraint building. My route mapping code won't allow strings. Maybe this is something that's changed in the WebAPI version since the library was written, I'm not sure, but it seems that any route that I'm trying to test with an HttpMethodConstraint will fail.
I'm continuing to investigate this one; I now have a 'why', but not a 'how to fix' yet.
UPDATE: The WebAPI routing takes two forms of constraint, one of which is just a string (more details here - http://forums.asp.net/p/1777747/4904556.aspx/1?Re+System+Web+Http+Routing+IHttpRouteConstraint+vs+System+Web+Routing+IRouteConstraint). I tried changing the routing table to see if that would make any difference, but it didn't - just to let you know!
I fixed this in the end by writing my own, after reading a post by whyleee on another question here - Testing route configuration in ASP.NET WebApi (WebApiContrib.Testing didn't seem to work for me)
I merged his post with some of the elements I liked syntactically from the WebApiContrib.Testing library to generate the following helper class.
This allows me to write really lightweight tests like this...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"http://api.siansplan.com/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>("Get", "hash");
}
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"http://api.siansplan.com/auth".ShouldMapTo<AuthController>("Post", HttpMethod.Post);
}
The helper class looks like this...
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Hosting;
using System.Web.Http.Routing;
namespace SiansPlan.Api.Tests.Helpers
{
public static class RoutingTestHelper
{
/// <summary>
/// Routes the request.
/// </summary>
/// <param name="config">The config.</param>
/// <param name="request">The request.</param>
/// <returns>Inbformation about the route.</returns>
public static RouteInfo RouteRequest(HttpConfiguration config, HttpRequestMessage request)
{
// create context
var controllerContext = new HttpControllerContext(config, new Mock<IHttpRouteData>().Object, request);
// get route data
var routeData = config.Routes.GetRouteData(request);
RemoveOptionalRoutingParameters(routeData.Values);
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
controllerContext.RouteData = routeData;
// get controller type
var controllerDescriptor = new DefaultHttpControllerSelector(config).SelectController(request);
controllerContext.ControllerDescriptor = controllerDescriptor;
// get action name
var actionMapping = new ApiControllerActionSelector().SelectAction(controllerContext);
var info = new RouteInfo(controllerDescriptor.ControllerType, actionMapping.ActionName);
foreach (var param in actionMapping.GetParameters())
{
info.Parameters.Add(param.ParameterName);
}
return info;
}
#region | Extensions |
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, params string[] parameterNames)
{
return ShouldMapTo<TController>(fullDummyUrl, action, HttpMethod.Get, parameterNames);
}
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
/// <exception cref="System.Exception"></exception>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, HttpMethod httpMethod, params string[] parameterNames)
{
var request = new HttpRequestMessage(httpMethod, fullDummyUrl);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var route = RouteRequest(config, request);
var controllerName = typeof(TController).Name;
if (route.Controller.Name != controllerName)
throw new Exception(String.Format("The specified route '{0}' does not match the expected controller '{1}'", fullDummyUrl, controllerName));
if (route.Action.ToLowerInvariant() != action.ToLowerInvariant())
throw new Exception(String.Format("The specified route '{0}' does not match the expected action '{1}'", fullDummyUrl, action));
if (parameterNames.Any())
{
if (route.Parameters.Count != parameterNames.Count())
throw new Exception(
String.Format(
"The specified route '{0}' does not have the expected number of parameters - expected '{1}' but was '{2}'",
fullDummyUrl, parameterNames.Count(), route.Parameters.Count));
foreach (var param in parameterNames)
{
if (!route.Parameters.Contains(param))
throw new Exception(
String.Format("The specified route '{0}' does not contain the expected parameter '{1}'",
fullDummyUrl, param));
}
}
return true;
}
#endregion
#region | Private Methods |
/// <summary>
/// Removes the optional routing parameters.
/// </summary>
/// <param name="routeValues">The route values.</param>
private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValues)
{
var optionalParams = routeValues
.Where(x => x.Value == RouteParameter.Optional)
.Select(x => x.Key)
.ToList();
foreach (var key in optionalParams)
{
routeValues.Remove(key);
}
}
#endregion
}
/// <summary>
/// Route information
/// </summary>
public class RouteInfo
{
#region | Construction |
/// <summary>
/// Initializes a new instance of the <see cref="RouteInfo"/> class.
/// </summary>
/// <param name="controller">The controller.</param>
/// <param name="action">The action.</param>
public RouteInfo(Type controller, string action)
{
Controller = controller;
Action = action;
Parameters = new List<string>();
}
#endregion
public Type Controller { get; private set; }
public string Action { get; private set; }
public List<string> Parameters { get; private set; }
}
}

Switch between UserControls in View based on property of ViewModel

How can I change between UserControls in the View, based on a property of the ViewModel, in a Windows 8 store app?
Say my ViewModel has a property which looks something like this:
class MyViewModel
{
public string CurrentStatus
{
get { return (string)GetValue(CurrentStatusProperty); }
set { SetValue(CurrentStatusProperty, value); }
}
public static readonly DependencyProperty CurrentStatusProperty =
DependencyProperty.Register("CurrentStatus", typeof(string), typeof(MyViewModel), new PropertyMetadata("default", CurrentStatusChanged));
...
}
How do I make my View change an UserControl according to the value of the CurrentStatus from the ViewModel?
The strightforward solution for me would have been to create a binding between the CurrentStatus from the ViewModel and another string from the View, but apparently data binding can only be used for a DependencyObject (which a string isn't).
Edit:
The xaml file contains just a StackPanel in which I want to put a UserControl, based on the CurrentStatus. So, if the CurrentStatus is "one" I want the StackPanel to contain UserControlOne and so on...
Any ideas or nice solutions to this problem?
Thanks a lot!
I usually use a ContentControl and set it's ContentTemplate based on a DataTrigger
I have an example in my blog post Switching between Views/UserControls using MVVM, but here's a sample of what that may look like using your scenario:
<DataTemplate x:Key="DefaultTemplate">
<local:DefaultUserControl />
</DataTemplate>
<DataTemplate x:Key="ClosedTemplate">
<local:ClosedUserControl />
</DataTemplate>
<Style x:Key="MyContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentStatus}" Value="Closed">
<Setter Property="ContentTemplate" Value="{StaticResource ClosedTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ContentControl Style="{StaticResource MyContentControlStyle}" />
EDIT
Apparently DataTriggers are not supported in WinRT, and have been replaced with the VisualStateManager. I haven't had the chance to use this yet, however from what I'm reading they used the same approach for WinRT as they did Silverlight (which also didn't support DataTriggers until v5), and my solution for Silverlight was to use a DataTemplateSelector
I hope that can point you in the right direction :)
Not sure I fully understand what you are trying to do. Can you post the xaml?
If what you are wanting is to present the controls differently based on the status, then use a converter and you can present a different template based upon the status:
public class StatusToTemplateConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((string) value)
{
case "Status1":
return Application.Current.Resources["Status1Template"];
case "Status2":
return Application.Current.Resources["Status2Template"];
default:
return Application.Current.Resources["Status3Template"];
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
#endregion
}
the above assumes you have your templates defined in a resource file
If all you ae wanting to do is something simpler such as red text for Status1 or Green text for status2 you could have a converter that just converts the status to a colour and bind the FontColor to the status and use the converter.
But like I said, without more code being posted, I'm not 100% clear on what you are trying to achieve
While I've used the DataTemplateSelector and IValueConverter in such cases before - now my favorite approach is using the VisualStateManager. I have created a most basic attached property that implements the attached behavior pattern in WinRT XAML Toolkit here - that looks like this:
/// <summary>
/// Defines an attached property that controls the visual state of the element based on the value.
/// </summary>
public static class VisualStateExtensions
{
#region State
/// <summary>
/// State Attached Dependency Property
/// </summary>
public static readonly DependencyProperty StateProperty =
DependencyProperty.RegisterAttached(
"State",
typeof(string),
typeof(VisualStateExtensions),
new PropertyMetadata(null, OnStateChanged));
/// <summary>
/// Gets the State property. This dependency property
/// indicates the VisualState that the associated control should be set to.
/// </summary>
public static string GetState(DependencyObject d)
{
return (string)d.GetValue(StateProperty);
}
/// <summary>
/// Sets the State property. This dependency property
/// indicates the VisualState that the associated control should be set to.
/// </summary>
public static void SetState(DependencyObject d, string value)
{
d.SetValue(StateProperty, value);
}
/// <summary>
/// Handles changes to the State property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnStateChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var stateName = (string)e.NewValue;
var ctrl = (Control)d;
VisualStateManager.GoToState(ctrl, stateName, true);
}
#endregion
}
You should define an enum-style class like the VisualStates class in Silverlight Toolkit that lists all your visual states (so you don't have duplicates), e.g.
internal static class VisualStates
{
#region GroupCommon
/// <summary>
/// Common state group.
/// </summary>
public const string GroupCommon = "CommonStates";
/// <summary>
/// Normal state of the Common state group.
/// </summary>
public const string StateNormal = "Normal";
/// <summary>
/// Normal state of the Common state group.
/// </summary>
public const string StateReadOnly = "ReadOnly";
/// <summary>
/// MouseOver state of the Common state group.
/// </summary>
public const string StateMouseOver = "MouseOver";
/// <summary>
/// Pressed state of the Common state group.
/// </summary>
public const string StatePressed = "Pressed";
/// <summary>
/// Disabled state of the Common state group.
/// </summary>
public const string StateDisabled = "Disabled";
#endregion GroupCommon
#region GroupFocus
/// <summary>
/// Focus state group.
/// </summary>
public const string GroupFocus = "FocusStates";
/// <summary>
/// Unfocused state of the Focus state group.
/// </summary>
public const string StateUnfocused = "Unfocused";
/// <summary>
/// Focused state of the Focus state group.
/// </summary>
public const string StateFocused = "Focused";
#endregion GroupFocus
}
Once you have these - you can have a property in your view model like
public string VisualState { get; set; /* Raise PropertyChange event your preferred way here */ }
And in your view say
<Page
...
xmlns:extensions="using:WinRTXamlToolkit.Controls.Extensions"
extensions:VisualStateExtensions.State="{Binding VisualState}">...
Then your view model can easily drive the change of the visual state and you can use Blend to define what that state looks like - e.g. change the Content or ContentTemplate or simply visibility of various elements in the layout. I think this method has best support of the designer tools since you can flip between views with a click of a button and make updates to these views in Blend.

Custom Authorize Attribute additional Param?

im looking for a way to customize my Authorize Attribute so that i can implement it correctly with my own MembershipProvider.
What i need is to have the IsInRoles(string role, int perm) for example, in other word, i want to have it replace with a new IsinRoles or maybe create another method to archive this result.
Is it possible? or i need to write a different authorize attribute?
Thank you very much for your concern...
PS: im working on ASP.net MVC, so i need to have the [Authorize] filter up and running.
I think you can just add a public property to your custom AuthorizeAttribute.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// Add the allowed roles to this property.
/// </summary>
public YourCustomRoles RequiredRole;
public int YourCustomValue;
/// <summary>
/// Checks to see if the user is authenticated and has the
/// correct role to access a particular view.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
// Make sure the user is authenticated.
if (httpContext.User.Identity.IsAuthenticated == false) return false;
// Can use your properties if needed and do your checks
bool authorized = DoSomeCustomChecksHere();
return authorized;
}
}
Usage I think would be (haven't tried it though):
[CustomAuthorizeAttribute (RequiredRole=MyCustomRole.Role1 | MyCustomRole.Role2, YourCustomValue=1234)]

Resources