14

I just converted a bunch of web services to Web API2. Now my C# code blows up when the browser sends an empty string and it enters my code converted to null. I have researched global solutions and none that I have found work for me.

I can of course set it manually for every string in all my Web API models, but I have scores of models so would prefer a global solution.

Been here: string.empty converted to null when passing JSON object to MVC Controller and other pages and attempted to implement each solution, but to no avail.

How can I globally set the default for ConvertEmptyStringToNull to false?

3
  • Did you find a solution to this problem? Commented Jun 4, 2015 at 21:33
  • No. I wound up setting it everywhere manually. Commented Jun 8, 2015 at 20:32
  • 1
    I found this solution: puredotnetcoder.blogspot.co.uk/2013/09/… Commented Oct 28, 2015 at 19:42

2 Answers 2

6

You need to swap out the ModelMetadataProvider with one that sets the ConvertEmptyStringToNull to false

Such as:

public class EmptyStringAllowedModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor)
    {
        var metadata = base.CreateMetadataFromPrototype(prototype, modelAccessor);
        metadata.ConvertEmptyStringToNull = false;
        return metadata;
    }

    protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName)
    {
        var metadata = base.CreateMetadataPrototype(attributes, containerType, modelType, propertyName);
        metadata.ConvertEmptyStringToNull = false;
        return metadata;
    }
}

You would register in your WebApiConfig like:

config.Services.Replace(typeof(ModelMetadataProvider), new EmptyStringAllowedModelMetadataProvider());

This was inspired by https://gist.github.com/nakamura-to/4029706

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

1 Comment

In ASP.NET MVC prior to .NET Core (i.e. System.Web.ModelBinding.DataAnnotationsModelMetadataProvider) you only need to override CreateMetadata btw.
0

you can try Aspect pattern by using Postsharp and declare below Aspect.
It will apply for entire solution. Worked for me.

using System;
using System.Linq;
using System.Reflection;
using Helpers.Aspects;
using PostSharp.Aspects;
using PostSharp.Extensibility;

[assembly: EmptyStringModelBindingAspect(
    AttributeTargetTypes = @"regex:[^\.]*\.Controllers\..*Controller",
    AttributeTargetTypeAttributes = MulticastAttributes.Public,
    AttributeTargetElements = MulticastTargets.Method,
    AttributeTargetMemberAttributes = MulticastAttributes.Public)]

namespace Helpers.Aspects
{
    [Serializable]
    public class EmptyStringModelBindingAspect : MethodInterceptionAspect
    {
        public override void OnInvoke(MethodInterceptionArgs args)
        {
            for (int i = 0; i < args.Arguments.Count; i++)
            {
                FixString(args, i);
                FixStringsInObjects(args.Arguments[i]);
            }

            args.Proceed();
        }

        private static void FixString(MethodInterceptionArgs args, int i)
        {
            if (args.Arguments[i] is string && args.Arguments[i] == null)
            {
                args.Arguments.SetArgument(i, string.Empty);
            }
        }

        private static void FixStringsInObjects(object obj)
        {
            if (obj == null)
            {
                return;
            }

            var type = obj.GetType();
            var properties = (from p in type.GetProperties() 
                                         let paramerers = p.GetIndexParameters() 
                                         where !paramerers.Any() 
                                         where p.PropertyType == typeof (string) && 
                                               p.CanRead && 
                                               p.CanWrite && 
                                               p.GetValue(obj, null) == null 
                                         select p).ToArray();

            foreach (var item in properties)
            {
                item.SetValue(obj, string.Empty, null);
            }
        }

        public override bool CompileTimeValidate(MethodBase method)
        {
            return !(method.Name.StartsWith("get_") || method.Name.StartsWith("set_"));
        }
    }
}

Comments

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.