NullReferenceException при использовании пользовательского связывателя модели - PullRequest
3 голосов
/ 27 июля 2011

Я пытаюсь сделать переплет для абстрактного класса.Связыватель решает, какую реализацию класса использовать.

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
    }  

}

1 Ответ

0 голосов
/ 27 июля 2011

В строке, где вы получаете сообщение об ошибке, values может быть нулевым или то, что GetValue("name") может быть нулевым.

Я предполагаю, что когда вы вызываете метод List<Pet>, ValueProvider возвращает все List вместо каждого отдельного Pet, поэтому он не может получить значение "name", так как он не существуют в List классе.

Я не могу быть более уверенным, не увидев больше кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...