使用自定义模型绑定程序时出现 NullReferenceException
我正在尝试为抽象类制作活页夹。绑定器决定使用类的哪个实现。
public abstract class Pet
{
public string name { get; set; }
public string species { get; set; }
abstract public string talk { get; }
}
public class Dog : Pet
{
override public string talk { get { return "Bark!"; } }
}
public class Cat : Pet
{
override public string talk { get { return "Miaow."; } }
}
public class Livestock : Pet
{
override public string talk { get { return "Mooo. Mooo. Fear me."; } }
}
所以我有一个控制宠物的控制器,绑定器决定(取决于物种字符串)它是狗、猫还是牲畜。
public class PetBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var values = (ValueProviderCollection)bindingContext.ValueProvider;
var name = (string)values.GetValue("name").ConvertTo(typeof(string));
var species = (string)values.GetValue("species").ConvertTo(typeof(string));
if (species == "dog")
{
return new Dog { name = name, species = "dog" };
}
else if (species == "cat")
{
return new Cat { name = name, species = "cat" };
}
else
{
return new Livestock { name = name, species = species };
}
}
}
public class HomeController : Controller
{
public JsonResult WorksFine(Pet pet)
{
return Json(pet);
}
public JsonResult DoesntWork(List<Pet> pets)
{
return Json(pets);
}
}
这很好用,但是一旦宠物处于另一个结构中(例如 List
或另一个对象),我就会得到一个 NullReferenceException (在 var name = (string)values 行上.GetValue("名称").ConvertTo(typeof(string));
在 PetBinder 中)。我做错了什么?
我添加了一个 Person 类来测试。它还给了我一个 NullReferenceException。
public class Person
{
public string name { get; set; }
public Pet pet { get; set; }
}
public class HomeController : Controller
{
public JsonResult PersonAction(Person p)
{
return Json(p);
}
}
ccurrens说的原因 var name = (string)values.GetValue("name").ConvertTo(typeof(string));
返回 null 是因为它无法从列表中获取值。
我看到它们在 List
中时被命名为 [n].name
和 [n].species
,但在 < code>Person 对象,它们被命名为 pet.name
和 pet.species
,当它们位于单个 Pet
中时,它们是刚刚命名为name
并且物种
。
解决方案
为了获取 GetValue
具有正确前缀([n]
或 pet
或其他任何内容)的参数名称,我使用了以下代码:
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
string prefix = ((hasPrefix)&&(bindingContext.ModelName!="")) ? bindingContext.ModelName + "." : "";
如果有人感兴趣,我最终使用类似于此的东西从 DefaultModelBinder
继承 答案。这是我使用的完整代码:
public class DefaultPetBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,ModelBindingContext bindingContext,Type modelType)
{
//https://stackoverflow.com/questions/5460081/asp-net-mvc-3-defaultmodelbinder-inheritance-problem
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
string prefix = ((hasPrefix)&&(bindingContext.ModelName!="")) ? bindingContext.ModelName + "." : "";
// get the parameter species
ValueProviderResult result;
result = bindingContext.ValueProvider.GetValue(prefix+"species");
if (result.AttemptedValue.Equals("cat"))
return base.CreateModel(controllerContext,bindingContext,typeof(Cat));
else if (result.AttemptedValue.Equals("dog"))
return base.CreateModel(controllerContext,bindingContext,typeof(Dog));
return base.CreateModel(controllerContext, bindingContext, typeof(Livestock)); // livestock
}
}
I am trying to make a binder for an abstract class. The binder decides which implementation of the class to use.
public abstract class Pet
{
public string name { get; set; }
public string species { get; set; }
abstract public string talk { get; }
}
public class Dog : Pet
{
override public string talk { get { return "Bark!"; } }
}
public class Cat : Pet
{
override public string talk { get { return "Miaow."; } }
}
public class Livestock : Pet
{
override public string talk { get { return "Mooo. Mooo. Fear me."; } }
}
So I have a controller which takes a Pet, the binder decides (depending on the species string) if it is a Dog, Cat or Livestock.
public class PetBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var values = (ValueProviderCollection)bindingContext.ValueProvider;
var name = (string)values.GetValue("name").ConvertTo(typeof(string));
var species = (string)values.GetValue("species").ConvertTo(typeof(string));
if (species == "dog")
{
return new Dog { name = name, species = "dog" };
}
else if (species == "cat")
{
return new Cat { name = name, species = "cat" };
}
else
{
return new Livestock { name = name, species = species };
}
}
}
public class HomeController : Controller
{
public JsonResult WorksFine(Pet pet)
{
return Json(pet);
}
public JsonResult DoesntWork(List<Pet> pets)
{
return Json(pets);
}
}
This works well, but as soon as the pet is in another structure (like List<Pet>
or another object), I get a NullReferenceException (on the line var name = (string)values.GetValue("name").ConvertTo(typeof(string));
in the PetBinder). What am I doing wrong?
I added a Person class to test. It also gave me a NullReferenceException.
public class Person
{
public string name { get; set; }
public Pet pet { get; set; }
}
public class HomeController : Controller
{
public JsonResult PersonAction(Person p)
{
return Json(p);
}
}
ccurrens said the reason var name = (string)values.GetValue("name").ConvertTo(typeof(string));
returned null was because it couldn't get the values from a list.
I see they are named [n].name
and [n].species
when in a List<Pet>
, but when in the Person
object they are named pet.name
and pet.species
and when they are in a single Pet
, they are just named name
and species
.
Solution
To get the parameter names with the right prefix ([n]
or pet
or anything else) for GetValue
, I used the following code:
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
string prefix = ((hasPrefix)&&(bindingContext.ModelName!="")) ? bindingContext.ModelName + "." : "";
If anyone is interested, I ended up inheriting from DefaultModelBinder
using something similar to this answer. Here is the full code I used:
public class DefaultPetBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,ModelBindingContext bindingContext,Type modelType)
{
//https://stackoverflow.com/questions/5460081/asp-net-mvc-3-defaultmodelbinder-inheritance-problem
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
string prefix = ((hasPrefix)&&(bindingContext.ModelName!="")) ? bindingContext.ModelName + "." : "";
// get the parameter species
ValueProviderResult result;
result = bindingContext.ValueProvider.GetValue(prefix+"species");
if (result.AttemptedValue.Equals("cat"))
return base.CreateModel(controllerContext,bindingContext,typeof(Cat));
else if (result.AttemptedValue.Equals("dog"))
return base.CreateModel(controllerContext,bindingContext,typeof(Dog));
return base.CreateModel(controllerContext, bindingContext, typeof(Livestock)); // livestock
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在您收到错误的行中,
values
可能为 null,或者GetValue("name")
返回的内容可能为 null。我假设当您调用
List
方法时,ValueProvider 返回整个List
而不是每个单独的Pet
,因此它无法获取值“name”
,因为它不存在于List
类中。如果没有看到更多代码,我无法更加确定。
In the line you're getting your error,
values
could be null or whatGetValue("name")
returns could be null.I'm assuming when you're calling the
List<Pet>
method, ValueProvider is returning the entireList
instead of each individualPet
, so it can't get the value"name"
since it doesn't exist in theList
class.I can't be more sure without seeing more code.