用于绑定嵌套属性值的自定义模型绑定器

发布于 2025-01-08 11:57:31 字数 1794 浏览 0 评论 0原文

我需要这个的原因是:在我的一个控制器中,我想以与应用程序的其余部分不同的方式绑定所有 Decimal 值。我不想在 Global.asax 中注册模型绑定器(通过 ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());)

我尝试从 DefaultModelBinder 派生 类并重写其 BindProperty 方法,但这仅适用于模型实例的直接(而非嵌套)Decimal 属性。

我有以下示例来演示我的问题:

namespace ModelBinderTest.Controllers
{
    public class Model
    {
        public decimal Decimal { get; set; }
        public DecimalContainer DecimalContainer { get; set; }
    }

    public class DecimalContainer
    {
        public decimal DecimalNested { get; set; }
    }

    public class DecimalModelBinder : DefaultModelBinder
    {
        protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.PropertyType == typeof (decimal))
            {                
                propertyDescriptor.SetValue(bindingContext.Model,  999M);
                return;
            }

            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }

    public class TestController : Controller
    {

        public ActionResult Index()
        {
            Model model = new Model();
            return View(model);
        }

        [HttpPost]
        public ActionResult Index([ModelBinder(typeof(DecimalModelBinder))] Model model)
        {
            return View(model);
        }

    }
}

此解决方案仅将 ModelDecimal 属性设置为 999,但不对 DecimalContainer 执行任何操作code> 的 DecimalNested 属性。我意识到这是因为 DecimalModelBinderBindProperty 覆盖中调用了 base.BindProperty ,但我不知道如何说服基础处理小数属性时使用我的模型绑定器的类。

The reason I need this: In one of my controllers I want to bind all Decimal values in a different way than the rest of my application. I do not want to register a Model Binder in Global.asax (via ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());)

I have tried deriving from the DefaultModelBinder class and override its BindProperty method, but that only works for the model instance's immediate (not nested) Decimal properties.

I have the following example to demonstrate my problem:

namespace ModelBinderTest.Controllers
{
    public class Model
    {
        public decimal Decimal { get; set; }
        public DecimalContainer DecimalContainer { get; set; }
    }

    public class DecimalContainer
    {
        public decimal DecimalNested { get; set; }
    }

    public class DecimalModelBinder : DefaultModelBinder
    {
        protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.PropertyType == typeof (decimal))
            {                
                propertyDescriptor.SetValue(bindingContext.Model,  999M);
                return;
            }

            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }

    public class TestController : Controller
    {

        public ActionResult Index()
        {
            Model model = new Model();
            return View(model);
        }

        [HttpPost]
        public ActionResult Index([ModelBinder(typeof(DecimalModelBinder))] Model model)
        {
            return View(model);
        }

    }
}

This solution only sets the Model's Decimal property to 999, but doesn't do anything to DecimalContainer's DecimalNested property. I realize this is because base.BindProperty is called in my DecimalModelBinder's BindProperty override, but I don't know how to convince the base class to use my Model Binder when dealing with decimal properties.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋千易 2025-01-15 11:57:31

您可以在 Application_Start 中无条件应用模型绑定器:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

然后有一个自定义授权过滤器(是的,授权过滤器,因为它在模型绑定器之前运行),它将向 HttpContext 注入一些稍后可能会被使用的值。由模型绑定器使用:

[AttributeUsage(AttributeTargets.Method)]
public class MyDecimalBinderAttribute : ActionFilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        filterContext.HttpContext.Items["_apply_decimal_binder_"] = true;
    }
}

然后在模型绑定器中测试 HttpContext 在应用它之前是否包含自定义值:

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext.HttpContext.Items.Contains("_apply_decimal_binder_"))
        {
            // The controller action was decorated with the [MyDecimalBinder]
            // so we can proceed
            return 999M;
        }

        // fallback to the default binder
        return base.BindModel(controllerContext, bindingContext);
    }
}

现在剩下的就是使用自定义过滤器装饰控制器操作以启用十进制绑定器:

[HttpPost]
[MyDecimalBinder]
public ActionResult Index(Model model)
{
    return View(model);
}

You could apply the model binder unconditionally in your Application_Start:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

and then have a custom authorization filter (yes, authorization filter as it runs before the model binder) that will inject into the HttpContext some value that could later be used by the model binder:

[AttributeUsage(AttributeTargets.Method)]
public class MyDecimalBinderAttribute : ActionFilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        filterContext.HttpContext.Items["_apply_decimal_binder_"] = true;
    }
}

and then in your model binder test if the HttpContext contains the custom value befoire applying it:

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext.HttpContext.Items.Contains("_apply_decimal_binder_"))
        {
            // The controller action was decorated with the [MyDecimalBinder]
            // so we can proceed
            return 999M;
        }

        // fallback to the default binder
        return base.BindModel(controllerContext, bindingContext);
    }
}

Now all that's left is to decorate your controller action with the custom filter to enable the decimal binder:

[HttpPost]
[MyDecimalBinder]
public ActionResult Index(Model model)
{
    return View(model);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文