代码之家  ›  专栏  ›  技术社区  ›  Adrian Grigore

TryUpdateModel在ASP.NET MVC 3单元测试中引发NullReferenceException

  •  8
  • Adrian Grigore  · 技术社区  · 14 年前

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

    以下是异常的堆栈跟踪:

    System.NullReferenceException:对象 引用未设置为 反对。在 System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(控制器上下文 controllerContext)在 System.Web.Mvc.ValueProviderFactoryCollection.<>c_ 显示类C.b _7(ValueProviderFactory公司 工厂)在 System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext() at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.移动下一步() 在 System.Collections.Generic.List系统 1..ctor(IEnumerable 1个 收藏)在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 来源)在 System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(控制器上下文 controllerContext)在 System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel) 型号,字符串前缀)
    ... 从现在开始我自己的代码。。。。

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

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

    我的猜测是,这与DI在MVC3中的新工作方式有关,但我不知道我做错了什么。也许在MVC 3中有一些DI设置是必需的,但在MVC 2中不是必需的?

    3 回复  |  直到 14 年前
        1
  •  15
  •   Ivan Korytin    14 年前

    您应该添加以下代码:

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

    我有同样的问题,这个代码帮助我。

        2
  •  2
  •   gdoron    13 年前

    如果其他人也有同样的问题,并发现这篇文章:

    我一般都是根据伊凡·科蒂姆的回答来解决这个问题的(谢谢!),在我的控制器基类构造函数中包含以下代码:

    if (Request!=null && Request.Form != null && Request.Form.HasKeys() && ValueProvider == null)
    {
        ValueProvider = new FormCollection(Request.Form).ToValueProvider();
    }
    
        3
  •  1
  •   Community CDub    8 年前

    这可能是 System.Web.Mvc.JsonValueProviderFactory.GetValueProvider 这是一个价值 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);
    }
    

    从stacktrace我们可以看到 TryUpdateModel[TModel](TModel model, String prefix) . 使用反射器,它正在访问 ControllerBase ValueProvider 财产。这又叫 ValueProviderFactoryCollection.GetValueProvider(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