Reputation: 33318
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.WhereSelectEnumerableIterator
2.MoveNext() at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable
1 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?
Upvotes: 8
Views: 2423
Reputation: 33318
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();
}
Upvotes: 2
Reputation: 1842
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.
Upvotes: 15
Reputation: 2028
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
Upvotes: 1