简体   繁体   中英

How to get a property value from model in OnActionExecuting

I'm working on an Asp.Net core 5 project targeted .Net 5 ,

In many cases in views I used select lists and selected value from the list will be affected to one of model properties .

I'm familiar with Action filters but beginner in my level, so I tried to create a custom filter that will check if any property 's value in the model equal default_option I will add an error model state , all this code should happen in OnActionExecuting method.

What I tried:

public class CheckDefaultOptionFromSelect : ActionFilterAttribute
    {

        /// <inheritdoc />
        public override void OnActionExecuting( ActionExecutingContext context )
        {
            var model = ( (Controller) context.Controller ).ViewData.Model;

            if ( model is not null )
            {
                foreach ( var property in model.GetType().GetProperties() )
                {

                        if ( (string) model.GetType().GetProperty( property.Name ).GetValue( model ).ToString() == "default_option" )
                        {
                            context.ModelState.AddModelError( "" , "Please select an option" );

                            base.OnActionExecuting( context );
                            break;
                        }
                }
            }
        }

    }

In those actions views will have this piece:

 <div class="col-lg-4 col-sm-6"> <div class="form-group"> <div class="form-control-wrap"> <select asp-for="PropertyName" asp-items="@(new SelectList(myCollection,"Id","Title") )" class="form-select form-control form-control-lg form-control-outlined" data-ui="lg" data-search="on"> <option value="default_option">Default Option</option> </select> <label asp-for="PropertyName" class="form-label-outlined"></label> <span asp-validation-for="PropertyName" class="text-danger"></span> </div> </div> </div>

the most important piece is that:

 <select asp-for="PropertyName" asp-items="@(new SelectList(myCollection,"Id","Title") )" class="form-select form-control form-control-lg form-control-outlined" data-ui="lg" data-search="on"> <option value="default_option">Default Option</option> </select>

The problem: When I debug using break points I saw that the ( (Controller) context.Controller ).ViewData.Model always null .

The Question: Please how can I iterate for all model properties and check values if not equal default_option ?

You need get the model data like below:

public class CheckDefaultOptionFromSelect : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        context.ActionArguments.TryGetValue("model", out object model);

        if (model is not null)
        {
            foreach (var property in model.GetType().GetProperties())
            {
                if ((string)model.GetType().GetProperty(property.Name).GetValue(model).ToString() == "default_option")
                {
                    context.ModelState.AddModelError("", "Please select an option");
                    base.OnActionExecuting(context);
                    break;
                }
            }
        }
    }
}

Whole testing code:

Model:

public class Test2
{
    public int Id { get; set; }
    public string Title { get; set; }
}
public class TestViewModel
{
    public string PropertyName { get; set; }
}

View:

Note : Because you add the modelstate with empty key, so asp-validation-for="PropertyName" will not display the error. Be sure add <div asp-validation-summary="ModelOnly" class="text-danger"></div> to display all the model error.

@model TestViewModel 
@{ 
    var myCollection = new List<Test2>()
    {
        new Test2(){ Id=1,Title="aaa"},
        new Test2(){Id=2,Title="bbb"},
        new Test2(){Id=3,Title="ccc"}
    };
}
<form method="post" asp-action="Index">
    <div class="col-lg-4 col-sm-6">
        <div class="form-group"> 

            <div asp-validation-summary="ModelOnly" class="text-danger"></div> 

            <div class="form-control-wrap">
                <select asp-for="PropertyName" asp-items="@(new SelectList(myCollection,"Id","Title") )" class="form-select form-control form-control-lg form-control-outlined" data-ui="lg" data-search="on">
                    <option value="default_option">Default Option</option>
                </select>
                <label asp-for="PropertyName" class="form-label-outlined"></label>
                <span asp-validation-for="PropertyName" class="text-danger"></span>
            </div>
        </div>
    </div>
    <input value="Create" type="submit" />
</form>

Controller:

public IActionResult Index()
{
    return View();
}

[HttpPost]
[ServiceFilter(typeof(CheckDefaultOptionFromSelect))]

public IActionResult Index(TestViewModel model)
{
    if (ModelState.IsValid)
    {
        return View();
    }
    else
    {
        return View(model);
    }
}

Register ActionFilterAttribute in Startup.cs:

services.AddScoped<CheckDefaultOptionFromSelect>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM