使用自定义模型绑定程序时出现 NullReferenceException

发布于 2024-11-26 16:00:40 字数 4022 浏览 3 评论 0原文

我正在尝试为抽象类制作活页夹。绑定器决定使用类的哪个实现。

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.namepet.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱的那么颓废 2024-12-03 16:00:40

在您收到错误的行中,values 可能为 null,或者 GetValue("name") 返回的内容可能为 null。

我假设当您调用 List 方法时,ValueProvider 返回整个 List 而不是每个单独的 Pet,因此它无法获取值“name”,因为它不存在于List类中。

如果没有看到更多代码,我无法更加确定。

In the line you're getting your error, values could be null or what GetValue("name") returns could be null.

I'm assuming when you're calling the List<Pet> method, ValueProvider is returning the entire List instead of each individual Pet, so it can't get the value "name" since it doesn't exist in the List class.

I can't be more sure without seeing more code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文