ASP.NET MVC 2 - 绑定到抽象模型

发布于 2024-09-28 20:36:24 字数 1033 浏览 8 评论 0原文

如果我有以下强类型视图:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %>

其中 Location 是一个抽象类。

我有以下控制器,它通过 POST: 接受强类型模型:

[HttpPost]
public ActionResult Index(Location model)

我收到一个运行时错误,指出“无法创建抽象类

这当然是有道理的。但是- 我不确定这里最好的解决方案是什么。

我有很多具体类型(大约 8 个),在这个视图中您只能编辑

尝试过 的属性。 > 要做的是为所有不同的具体类型创建重载,并在通用方法中执行我的逻辑

[HttpPost]
public ActionResult Index(City model)
{
   UpdateLocationModel(model);
   return View(model);
}

[HttpPost]
public ActionResult Index(State model)
{
   UpdateLocationModel(model);
   return View(model);
}

等等

然后:

[NonAction]
private void UpdateLocationModel (Location model)
{
   // ..snip - update model
}

但这也不起作用,MVC 抱怨操作方法不明确(。也有道理)

我们能做什么?

If i have the following strongly-typed view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %>

Where Location is an abstract class.

And i have the following Controller, which accepts a strongly-typed Model via a POST:

[HttpPost]
public ActionResult Index(Location model)

I get a runtime error stating "Cannot Create Abstract Class

Which of course makes sense. However - i'm not sure what the best solution is here.

I have many concrete types (around 8), and this is a view where you can only edit properties of the abstract class.

What i've tried to do is create overloads for all the different concrete types, and perform my logic in a common method.

[HttpPost]
public ActionResult Index(City model)
{
   UpdateLocationModel(model);
   return View(model);
}

[HttpPost]
public ActionResult Index(State model)
{
   UpdateLocationModel(model);
   return View(model);
}

etc etc

And then:

[NonAction]
private void UpdateLocationModel (Location model)
{
   // ..snip - update model
}

But this doesn't work either, MVC complains the action methods are ambiguous (also makes sense).

What do we do? Can we simply not bind to an abstract model?

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

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

发布评论

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

评论(3

兲鉂ぱ嘚淚 2024-10-05 20:36:24

为这个抽象类编写一个自定义模型绑定器怎么样:

public class CustomBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        // TODO: based on some request parameter choose the proper child type
        // to instantiate here
        return new Child();
    }
}

只有当您有一个根据某些用户操作动态插入输入元素的表单时,这才有意义。在这种情况下,您需要传递一些附加参数来指示您需要哪个具体类。否则我会坚持使用具体的视图模型作为操作参数。

How about writing a custom model binder for this abstract class:

public class CustomBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        // TODO: based on some request parameter choose the proper child type
        // to instantiate here
        return new Child();
    }
}

This make sense only if you have a form where input elements are inserted dynamically based on some user action. In this case you need to pass some additional parameter to indicate which concrete class you need. Otherwise I would stick to concrete view models as action parameters.

颜漓半夏 2024-10-05 20:36:24

您还可以构建适用于所有抽象模型的通用 ModelBinder。我的解决方案要求您向视图添加一个名为“ModelTypeName”的隐藏字段,并将值设置为所需的具体类型的名称。
但是,应该可以使这个东西变得更智能,并通过将类型属性与视图中的字段匹配来选择具体类型。

Application_Start() 中的 Global.asax.cs 文件中:

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

CustomModelBinder:

public class CustomModelBinder2 : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelType = bindingContext.ModelType;
        if (modelType.IsAbstract)
        {
            var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ModelTypeName");
            if (modelTypeValue == null)
                throw new Exception("View does not contain ModelTypeName");

            var modelTypeName = modelTypeValue.AttemptedValue;

            var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);

            if (type != null)
            {
                var instance= bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
                bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
            }
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

You can also build a generic ModelBinder that works for all of your abstract models. My solution requires you to add a hidden field to your view called 'ModelTypeName' with the value set to the name of the concrete type that you want.
However, it should be possible to make this thing smarter and pick a concrete type by matching type properties to fields in the view.

In your Global.asax.cs file in Application_Start():

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

CustomModelBinder:

public class CustomModelBinder2 : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelType = bindingContext.ModelType;
        if (modelType.IsAbstract)
        {
            var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ModelTypeName");
            if (modelTypeValue == null)
                throw new Exception("View does not contain ModelTypeName");

            var modelTypeName = modelTypeValue.AttemptedValue;

            var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);

            if (type != null)
            {
                var instance= bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
                bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
            }
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}
彼岸花似海 2024-10-05 20:36:24

只是把它扔在那里 - 我对其他人可能会回答的内容非常感兴趣,但这就是我在遇到类似情况时最终所做的事情;

基本上,我没有使用模型类作为 Action 方法中的参数,而是传入 FormCollection 并测试几个已知的鉴别器以找出要创建/编辑的类型,然后使用 TryUpdateModel 从那里开始。

似乎可能有更好的方法,但我从来没有时间更多地思考它。

Just to throw it out there - I'm very much interested in what others might answer, but this is what I ended up doing in the case where I had a similar situation;

Basically, I did not use the model class as a parameter in the Action method, instead passing in FormCollection and testing a couple known discriminators to figure out which type to create/edit, then used TryUpdateModel from there.

It seemed there might be a better way, but I'd never gotten around to thinking about it more.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文