Question

Pretty new to MVC so hopefully this is a simple question.

I have written a custom binding attribute that requires access to the httpContext. In order to inject a mock httpContext during unit tests, I have written an InjectingMetadataProvider that populates the Context property on any of my custom attributes.

I have managed to get this to work in the following test:

[TestMethod]
public void Marker_ShouldBind_Id()
{
    // Arrange
    var formCollection = new NameValueCollection 
    { 
        { "id", "2" }
    };

    var context = new Mock<HttpContextBase>();
    context.Setup(c => c.User).Returns((IPrincipal)null); 

    var metaProvider = new InjectingMetadataProvider(context.Object);
    ModelMetadataProviders.Current = metaProvider;  //why do I need this?

    var bindingContext = new ModelBindingContext
    {
        ModelName     = string.Empty,
        ValueProvider = new NameValueCollectionValueProvider(formCollection, null),
        ModelMetadata = metaProvider.GetMetadataForType(null, typeof(Marker)),
    };

    var binder = new DefaultModelBinder();

    // Act
    var marker = (Marker)binder.BindModel(new ControllerContext(), bindingContext);

    // Assert
    marker.Id.Should().Be(2);
}

However, if I comment out the line that sets my InjectingMetadataProvider to ModelMetadataProviders.Current, then my InjectingMetadataProvider.CreateMetadata() override gets handed a blank list of attributes, and so the test fails because my custom attributes don't get their context set.

Why do I need to set it to Current when I'm using it explicitly anyway? I don't want to be setting static stuff in my tests.

I may be doing something stupid because I'm feeling in the dark a bit at the moment due to my unfamiliarity with the framework.

Was it helpful?

Solution

Inside the DefaultModelBinder, a new binding context is created when calling BindComplexElementalModel. Notice that it gets the metadata from the ModelMetadataProviders.Current, and not your custom model metadata provider.

  internal ModelBindingContext CreateComplexElementalModelBindingContext(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
        BindAttribute bindAttr = (BindAttribute)GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)];
        Predicate<string> newPropertyFilter = (bindAttr != null)
            ? propertyName => bindAttr.IsPropertyAllowed(propertyName) && bindingContext.PropertyFilter(propertyName)
            : bindingContext.PropertyFilter;

        ModelBindingContext newBindingContext = new ModelBindingContext() {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, bindingContext.ModelType),
            ModelName = bindingContext.ModelName,
            ModelState = bindingContext.ModelState,
            PropertyFilter = newPropertyFilter,
            ValueProvider = bindingContext.ValueProvider
        };

        return newBindingContext;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top