【问题标题】:How to bind URL parameters to model properties with different names如何将 URL 参数绑定到具有不同名称的模型属性
【发布时间】:2011-12-14 16:09:35
【问题描述】:

好吧,假设我有一个这样的 URL,它通过 HTTP 动词 GET 映射到我在下面的控制器操作:

GET /foo/bar?sort=asc&r=true

如何在下面的控制器操作中将此绑定到我的模型 Bar

class Bar {
    string SortOrder { get; set; }
    bool Random { get; set; }
}

public ActionResult FooBar(Bar bar) {
    // Do something with bar
    return null;
}

请注意,属性名称不会也不一定与 URL 参数的名称匹配。此外,这些是可选的 url 参数。

【问题讨论】:

  • 我只是添加一个具有匹配属性名称的类,但我很想看看您是否可以将模型绑定器配置为绑定到任意属性名称。也许有一些属性可以使用......
  • 看不到,为什么属性和url参数不匹配?
  • @gdoron,他们可以,我只是强烈希望他们不匹配......我希望在属性上有一个属性,以明确说明正在绑定哪个 URL 参数到哪个属性。
  • @gdoron,是的,我可以让它们匹配。然而,所有其他优秀的 Web MVC 框架都有属性/注释,允许您指定应该绑定到属性的参数......这使得重构/维护更加简单。我很失望 ASP.NET MVC 没有这个而不必自己创建它。
  • 您有一个面向外部的 API/合同,它隐含地依赖于内部类的结构/命名方式。按照约定进行编码非常棒,但框架应该让您可以选择在外部合同中明确表示。如果 Joe Nooby Developer 进来并重命名/删除模型上的属性,现在我的外部合同由于隐式合同而破裂。

标签: asp.net-mvc model-binding


【解决方案1】:

不支持开箱即用,但您可以这样做:

class BarQuery : Bar { 

   public string sort { get { return SortOrder; } set { SortOrder = value; } }
   public bool r { get { return Random; } set { Random = value; } }
}

public ActionResult FooBar(BarQuery bar) {
    // Do something with bar
}

您可以实现自定义IModelBinder,但手动映射要容易得多。


如果您可以更改 Bar 类,则可以使用此属性:
class FromQueryAttribute : CustomModelBinderAttribute, IModelBinder { 

   public string Name { get; set; }

   public FromQueryAttribute() { }

   public FromQueryAttribute(string name) { 
      this.Name = name;
   }

   public override IModelBinder GetModelBinder() { 
      return this;
   }

   public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
      return controllerContext.HttpContext.QueryString[this.Name ?? bindingContext.ModelName];
   }
}

class Bar {

    [FromQuery("sort")]
    string SortOrder { get; set; }

    [FromQuery("r")]
    bool Random { get; set; }
}

public ActionResult FooBar(Bar bar) {
    // Do something with bar
    return null;
}

【讨论】:

  • 我喜欢 Query 属性,但这不是最佳做法(您可能同意)
  • @gdoron 不明白你的意思。
  • 我的意思是,虽然这是一个不错的“hack”,但 MVC 团队确实建议避免编写自定义模型绑定器。 “一般来说,我们建议人们不要编写自定义模型绑定器,因为它们很难正确使用,而且很少需要”
  • @gdoron 你想听从别人的意见,而不是好好思考,但不要称其为 hack,因为它不是。
  • 这不是黑客。 IMO ASP.NET MVC 没有开箱即用地做这件事是愚蠢的。
【解决方案2】:

模型绑定器通过名称将它从视图中获取的参数与您在操作中拥有的模型相匹配,因此如果它们不匹配,则绑定将不起作用。

你得到的选项:

  1. 将输入名称与模型属性名称匹配...但是您说您不能这样做,(出于某种未知原因)。
  2. 编写自定义模型绑定器。 *
  3. 使用带前缀的 Bind 属性 - 尽管它仍会强制您使用接近模型属性名称的输入名称。

所以基本上,你不能完全按照你的意愿去做。


更新:

您在评论中写道,属性可以匹配参数名称,因此不要编写可能会成功进行绑定的自定义属性,只需编写一个 ViewModel(来自MVC 的 VM ...)来调整 url 参数名称.

MVC 团队不建议编写自定义模型绑定器:

一般而言,我们建议人们不要编写自定义模型绑定器,因为它们很难正确使用,而且很少需要
来自here

【讨论】:

    【解决方案3】:

    很久以前我也遇到过同样的问题,并使用了 Andras Zoltan 的这个堆栈解决方案:Asp.Net MVC 2 - Bind a model's property to a different named value

    我在我的类上设置了 ModelBinder 属性,在属性上设置了 BindAlias:

      [ModelBinder(typeof(DefaultModelBinderEx))]
      public class MyModel
      {
            [Required]
            [BindAlias("new")]
            public int? Amount { get; set; }
    

    如果您无法更改或没有访问您的模型文件来设置属性,您仍然可以创建自定义模型绑定器,或者,制作一个特定对象而不是映射到您的模型(AutoMapper 很有用)

    【讨论】:

      【解决方案4】:

      如果定义了传入参数名称,您可以实现 IModelBinder 以将传入参数映射到您选择的对象。否则,您将不得不依赖参数顺序和/或类型来推断正确的绑定,这似乎是一个非常糟糕的选择。

      【讨论】:

        【解决方案5】:

        没有免费的 MVC.Net 模型绑定功能可以为您提供开箱即用的功能。当我需要做这样的事情时,它真的让我想到了我的建模,这几乎总是让我为我的视图绑定创建一个 ViewModel 并为我的存储库存储创建一个 EntityModel。

        我喜欢使用 AutoMapper 在这些不同类型之间进行转换。使用 AutoMapper 最好的部分是它使您不必为控制器中的每个动作一遍又一遍地编写映射逻辑。只需在初始化部分中使用 AutoMapper 设置一次,然后再执行类似的操作。

        Mapper.Map<Bar>(barViewModel);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-01-19
          • 2023-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-06
          • 1970-01-01
          相关资源
          最近更新 更多