C# 控制器参数不会变为null

C# 控制器参数不会变为null,c#,asp.net-mvc,controller,C#,Asp.net Mvc,Controller,我遇到了以下问题: 我有一个asp.net mvc 5控制器,其参数为引用类型: [Route("")] [HttpGet] public ActionResult GetFeeds(Location location) { if (location == null) { // init location or smth } // Do smth with location

我遇到了以下问题:

我有一个asp.net mvc 5控制器,其参数为引用类型:

    [Route("")]
    [HttpGet]
    public ActionResult GetFeeds(Location location)
    {
        if (location == null)
        {
            // init location or smth
        }

        // Do smth with location

        return new EmptyResult();
   }
您会注意到我正在使用AttributeRouting。没有具有此操作名称的其他方法

但是-这是我的位置类:

    public class Location : ILocation
    {
       public DbGeography Coordinates { get; set; }

       public double Latitude { get; set; }

       public double Longitude { get; set; }
    }
这里没有什么特别之处(接口定义了所有这些属性)

如果我正在访问控制器操作(实际上使用powershell)并传递以下内容:

http://localhost:2000/Feed?latitude=23.1&longitude=37
一切正常,但如果我使用

http://localhost:2000/Feed
location参数不为null(它是一个具有默认值的新位置),这是我想要的行为:(

有人知道为什么会这样吗


提前感谢

MVC模型绑定器已经接管。我最初发布的内容将在不通过模型绑定器的实例中起作用。但是,通过我自己的快速测试,由于绑定器的工作方式,viewmodel参数似乎永远不会为空,并将属性与表单值联系起来

在您的实例中,我将检查纬度和经度是否都为null,以查看是否未传递任何内容。这意味着您需要在ViewModel上使它们为null

public class Location : ILocation
    {
       public DbGeography Coordinates { get; set; }

       public double? Latitude { get; set; }

       public double? Longitude { get; set; }
    }
更新控制器代码

if (location.Latitude == null && location.Longitude == null)
    {
        // init location or smth
    }

ModelBinder创建对象的新实例,因此您在此处有两个选项:

在“必需属性”上有一个
[必需的]
数据批注,并将它们标记为可为空,然后检查
ModelState.IsValid
(推荐)

将纬度和经度设为空
double?
您可以检查
Latitude.HasValue&&longide.HasValue

更新:

public class Location : ILocation
    {
       public DbGeography Coordinates { get; set; }

       public double? Latitude { get; set; }

       public double? Longitude { get; set; }
    }

public class LocationGetFeedsViewModel : LocationGetFeedsBinderModel {
       // change coordinates to string because maybe that's easier to handle on the view.
       public string Coordinates { get; set; }
       // added to sum to the example
       public IEnumerable<SelectListItem> Zones { get; set; } 
}

public class LocationGetFeedsBinderModel {
       [Required]
       public double? Latitude { get; set; }
       [Required]
       public double? Longitude { get; set; }
}
public ActionResult GetFeeds(LocationGetFeedsBinderModel location) {
    if (!ModelState.IsValid)
        // redirect or display some error 
    return new EmptyResult();
}

默认的模型绑定器将始终实例化一个复杂对象,因此不幸的是,它永远不会为null,并且将跳过任何可选的赋值

这种行为应用于您的系统的最终结果是,您必须检测默认构造函数的使用。反射或隐式检查无法做到这一点

必须明确地做到这一点

它可以通过在默认构造函数中设置类中的标志、接受答案中建议的可为空的属性、使用Bart建议的数据注释、使用属性的自定义get和set方法或各种其他方式来实现。

我一直在测试使用angularjs发布数据。

我发现,如果强制将值设置为null而不是undefined,ModelBinder不会实例化复杂对象

if (!location)
    location = null;

$http({ method: "POST", url: "/Location/Create", data: { location } })

活页夹按设计工作-它实例化一个
位置
对象,并用默认值填充其值,除非您提供覆盖值(第二个示例中没有)。它不会仅仅因为路由未提供任何值而创建
位置
对象的空实例。@ChristopherKellner-由于“模型绑定器如何工作”,它似乎不可能出现行得通。请看我答案中的链接,了解更多的解释。我尝试了这个方法,但位置仍然是位置的新实例。还有什么建议吗?@ChristopherKellner-是的,你说得对,更新了我对youlocation.Latitude.HasValue的答案会更好看:DIt很奇怪,它不会映射为null。如果你标记为它是可空的?
(Location?Location=null)
将特定于模型的验证放在控制器中是一个坏主意(控制器很难测试,而且很可能会重复自己的测试).ASP.Net MVC附带数据注释验证器有一个很好的理由。这是我最初的答案。当您进入ActionResult时,变量仍然是非空的。我在VS2013上测试了自己。猜测在MVC中的某个点对DefaultModelBinder进行了更新。@Tommy-我删除了使用可选属性集t的建议o null。正如您准确声明的那样,它将被实例化,并且永远不会分配可选值。请查看编辑。遗憾的是,我无法将这些属性更改为可空值,因为它们是由ILocation继承的。我看到了实现目标的3种可能性:创建一个具有可空属性的新VM类,并将它们解析到新位置n或检查它们是否都为0,因为这是非常罕见的。两者都感觉不正确:/第三个可以为空,作为参数,这似乎更好。或者你有其他想法吗?这就是我所说的
BinderModel
而不是ViewModel。这是非常有用的,因为ViewModel通常比只接受2或3。两种方法都是最好的。事实上,我认为有一个控制器来接受全视图模型是很少见的,甚至是很糟糕的。所以你会推荐使用“BinderModel”的第一种方法?是的,是的,是的。当然。在真实的word场景中,当您只有一个表单时,我会让ViewModel继承BinderModel,如:LocationEditViewModel:LocationEditBinderModel。您将可以使用完全有效的数据批注访问视图上的所有属性,并且控制器级别的Post签名将收到LocationEditBinderModel。我为所有POST都这样做。对我来说,完全控制暴露和可供用户使用的属性的数量是非常重要的,特别是为了避免在模型级别上过度伪装和保持控制器签名干净。在您的示例中,我可以发布坐标属性,我认为这不是有意的,所以您要公开它er发帖。这可能是安全的,也可能是糟糕的。最好是通过设计来防止。我在2020年使用React,效果非常好。