8

Since I upgraded from MVC 2 to MVC 3 RC, using TryUpdateModel causes a NullReferenceException. This problem only occurs when running my action method as part of a unit test. Running it on the actual server works as expected.

Here's a stack trace of the exception:

System.NullReferenceException: Object reference not set to an instance of an object. at System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.ValueProviderFactoryCollection.<>c_DisplayClassc.b_7(ValueProviderFactory factory) at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext() at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model, String prefix)
... my own code from here on....

In case it matters, my controller has the following signature:

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

My guess is that this has to do with the new way DI works in MVC3, but I can't figure out what I'm doing wrong. Perhaps there is something in terms of DI setup that is required in MVC 3, but wasn't required in MVC 2?

3 Answers 3

15

You should add this code:

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

I have the same problem and this code helps me.

Sign up to request clarification or add additional context in comments.

Comments

2

In case someone else has the same problem and finds this post:

I solved the problem generically based on Ivan Kortym's answer (thanks!), with the following piece of code in my controller base class constructor:

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

2 Comments

This isn't a good idea, because you're adding code to facilitate tests into production code. It also gets executed on every request, and it's completely unnecessary.
Why not just use the "ValueProvider = ..." line in the setup of your controlller in your unit tests.
1

It's probably a change in implementation of System.Web.Mvc.JsonValueProviderFactory.GetValueProvider that is hitting a value in ControllerContext that is null.

You may need to mock an additional value in ControllerContext.

At least that's where I'd look first.

EDIT

Yeah, looks like it's doing a null check on 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);
}

From the stacktrace we can see that TryUpdateModel[TModel](TModel model, String prefix). Using reflector, it is accessing the ControllerBase ValueProvider property. This in turn calls ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) with the current Controllers ControllerContext property.

You should just be able to create a new ControllerContext instance and set the controller's property accordingly...

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

    controller.ControllerContext = controllerContext;

    controller.Edit(...);       
}

Some additional mocking may be required to get it to fully function though. Some info on how to fully mock ControllerContext: Mocking Asp.net-mvc Controller Context

5 Comments

I guesst almost as much, but which value exactly do I need to mock? And how can I do this? This seems to be a pretty common scenario...
A common scenario for brand new code isn't that common. ;) Why don't you include your current mocks and then we could point out likely additions. Are you mocking the header?
Yeah doesn't look like the MVC3 source is available yet... that could make things bit more challenging. I'll load up Reflector and see what I can find.
If you're unit testing a method which calls UpdateModel, you should set the Controller.ValueProvider property manually before running the test. I'd recommend setting it to an instance of SimpleValueProvider, which is just a fancy NameValueCollection.
I had to add both a ValueProvider and a ControllerContext

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.