TryUpdateModel在ASP.NET MVC 3单元测试中抛出NullReferenceException

时间:2010-11-12 15:21:40

标签: asp.net-mvc dependency-injection asp.net-mvc-3 model-binding

由于我从MVC 2升级到MVC 3 RC,因此使用TryUpdateModel会导致NullReferenceException。仅当将我的操作方法作为单元测试的一部分运行时,才会出现此问题。在实际服务器上运行它可以按预期工作。

这是异常的堆栈跟踪:

  

System.NullReferenceException:Object   引用未设置为的实例   宾语。在   System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext   controllerContext)at   System.Web.Mvc.ValueProviderFactoryCollection<> C_ DisplayClassc.b _7(ValueProviderFactory   工厂)   System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext() at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()   在   System.Collections.Generic.List 1..ctor(IEnumerable 1   收藏)   System.Linq.Enumerable.ToList [TSource](IEnumerable`1   来源)   System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext   controllerContext)at   System.Web.Mvc.Controller.TryUpdateModel [TModel的](TModel的   model,String prefix)
  ......我自己的代码来自......

如果重要,我的控制器有以下签名:

[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult Edit(int id, FormCollection collection)
{
}

我的猜测是,这与DI在MVC3中工作的新方式有关,但我无法弄清楚我做错了什么。也许在MVC 3中需要一些DI设置,但在MVC 2中不需要它?

3 个答案:

答案 0 :(得分:15)

您应该添加以下代码:

 FormCollection formValues = new FormCollection() 
        {
            { "Test", "test" },
            { "FirstName", "TestName" } 
        };
        rootController.ValueProvider = formValues.ToValueProvider();

我有同样的问题,这段代码可以帮助我。

答案 1 :(得分:2)

如果其他人遇到同样的问题并发现此帖:

我根据Ivan Kortym的回答(谢谢!)解决了这个问题,在我的控制器基类构造函数中使用了以下代码:

if (Request!=null && Request.Form != null && Request.Form.HasKeys() && ValueProvider == null)
{
    ValueProvider = new FormCollection(Request.Form).ToValueProvider();
}

答案 2 :(得分:1)

System.Web.Mvc.JsonValueProviderFactory.GetValueProvider的实施可能会发生变化,导致ControllerContext中的值为空。

您可能需要在ControllerContext中模拟其他值。

至少那是我先看的地方。

修改

是的,看起来它正在对controllerContext进行空检查。

public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
    if (controllerContext == null)
    {
        throw new ArgumentNullException("controllerContext");
    }
    object deserializedObject = GetDeserializedObject(controllerContext);
    if (deserializedObject == null)
    {
        return null;
    }
    Dictionary<string, object> backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    AddToBackingStore(backingStore, string.Empty, deserializedObject);
    return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
}

从堆栈跟踪中我们可以看到TryUpdateModel[TModel](TModel model, String prefix)。使用反射器,它正在访问ControllerBase ValueProvider属性。这反过来使用当前的控制器ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)属性调用ControllerContext

您应该能够创建一个新的ControllerContext实例并相应地设置控制器的属性...

[TestMethod]
public void EditTest
{
    var controller = new Controller();         
    var controllerContext = new ControllerContext();

    controller.ControllerContext = controllerContext;

    controller.Edit(...);       
}

可能需要一些额外的模拟来让它完全发挥作用。有关如何完全模拟ControllerContext的一些信息:Mocking Asp.net-mvc Controller Context