Я пытаюсь сделать переплет для абстрактного класса.Связыватель решает, какую реализацию класса использовать.
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
, когда в List<Pet>
, но когда в объекте Person
они называются pet.name
и pet.species
и когда они находятся в одномPet
, они просто названы name
и 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
}
}