我正在尝试为抽象类制作活页夹。绑定器决定使用哪个类的实现。
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<Pet>
或另一个对象)中,我就会得到一个 NullReferenceException(在var name = (string)values.GetValue("name").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 的原因是它无法从列表中获取值。
我看到它们被命名[n].name
并且[n].species
在 a 中List<Pet>
,但是在Person
对象中它们被命名pet.name
并且pet.species
当它们在单个 中时Pet
,它们只是被命名为name
and species
。
解决方案
为了获得具有正确前缀([n]
或pet
其他任何东西)的参数名称GetValue
,我使用了以下代码:
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
}
}