【发布时间】:2011-07-27 18:03:37
【问题描述】:
我正在尝试为抽象类制作活页夹。绑定器决定使用哪个类的实现。
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(在 PetBinder 中的 var name = (string)values.GetValue("name").ConvertTo(typeof(string));
行上)。我做错了什么?
我添加了一个 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<Pet> 中时被命名为 [n].name 和 [n].species,但是在 Person 对象中时它们被命名为 pet.name 和 pet.species 并且当它们在单个 Pet 中时,它们只是命名为name 和species。
解决方案
要获取GetValue 的正确前缀([n] 或pet 或其他任何名称)的参数名称,我使用了以下代码:
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
string prefix = ((hasPrefix)&&(bindingContext.ModelName!="")) ? bindingContext.ModelName + "." : "";
如果有人感兴趣,我最终使用类似于 answer 的东西从 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
}
}
【问题讨论】:
标签: c# asp.net-mvc-3 model-binding custom-model-binder