Uppercase attribute that converts the input to uppercase

23,290

Solution 1

I have managed to get this working, to a point, so here's my solution for others to appraise.

Once point to note was that the full solution couldn't be achieved because I couldn't get the Modelmetadata inside the StringValidation.IsValid() attribute. The particular issue I had here was that I could get the Metadata, however I could not get the PropertyName from it, only the DisplayName. There were multiple options out there, but the fact that some of my properties have the same DisplayName means that I couldn't be sure that the ProprtyName was the one I was actually validating.

Here's the code for the ValidationAttribute:

public class StringValidationAttribute : ValidationAttribute, IClientValidatable, IMetadataAware {

    private bool _uppercase;

    public StringValidationAttribute(bool uppercase = false) {
         _uppercase = uppercase;
    }

    ...

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["Uppercase"] = _uppercase;
    }
}

I then created a new IModelBinder implementation:

public class StringBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {           
        ValueProviderResult result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (result == null)
            return null;

        if (bindingContext.ModelMetadata.AdditionalValues.ContainsKey("Uppercase")) {
            if ((bool)bindingContext.ModelMetadata.AdditionalValues["Uppercase"]])
                return result.AttemptedValue.ToUpper();
        }

        return result.AttemptedValue;
    }
}

And registered that in myGlobal.asax file:

ModelBinders.Binders.Add(typeof(string), new StringBinder());

The code so far will cause any string input coming into MVC to be converted to Uppercase if it has StringValidationAttribute attached to it on the model, and where the uppercase indicator has been set.

Next, to achieve my desire of making the html forms be uppercase too, I implemented a new EditorTemplate named string.cshtml. In this view I added:

RouteValueDictionary htmlAttributes = new RouteValueDictionary();
if ((bool)ViewData.ModelMetadata.AdditionalValues["Uppercase"]) {
    htmlAttributes.Add("class", "Uppercase");
}
@Html.TextBox("", Model, htmlAttributes)

With the CSS as;

.Uppercase {
    text-transform: uppercase;
}

Hope this post helps some others out there.

Solution 2

For Web API purpose it is better to convert the incoming json to uppercase or lowercase.

    public class ToUpperCase : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(string);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return reader.Value.ToString().ToUpper();
        }            
    }



    [Display(Name = "PNR NAME")]
    [JsonConverter(typeof(Annotations.ToUpperCase))]
    public string PNR { get; set; }

OR Globally;

  protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        //.......... others



        JsonMediaTypeFormatter jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings();
        jSettings.Converters.Add(new UpperCaseStringConverter());
        jsonFormatter.SerializerSettings = jSettings;
    }
Share:
23,290
Nick
Author by

Nick

Primarily a C# programmer working at a Financial Services software house. Currently working on: ASP.Net MVC WCF HTML / Javascript COBOL PL/SQL VB6 / VB.Net C / C++ Java

Updated on July 06, 2020

Comments

  • Nick
    Nick almost 4 years

    I am working in MVC4 and want to define a model using an Uppercase attribute. The idea would be that the presence of the Uppercase attribute would cause the model value to be converted to uppercase when it arrived at the server.

    At the moment I have the following code within the model:

        [Required]
        [Display(Name="Account Code")]
        [StringValidation(RegExValidation.AccountCode, Uppercase=true)]
        public string Account
        {
            get { return _account; }
            set
            {
                if (value != null)
                    _account = value.ToUpper();
            }
        }
    

    But what I would really like is this:

        [Required]
        [Display(Name="Account Code")]
        [StringValidation(RegExValidation.AccountCode)]
        [Uppercase]
        public string Account { get; set; }
    

    I think that I may need to create the Uppercase attribute as a ValidationAttribute to ensure it gets fired when the model hits the server. But that seems a bit wrong, as I'm not really validating the data. Is there a better way?

    Also, is there any way to ensure the invocation order on the attributes? I really want to convert the data to uppercase before the custom StringValidation attribute fires, as this checks the case of the text in the regex pattern.

    To add a bit of background to this, I want to reduce the need to add code to uppercase the data. The nirvana would be a single attribute, which updates the data on the way into the server, either in the model binding or validation stage. This attribute can then be referenced in the StringValidation attribute to amend the RegEx value used in its checks. I can also then lookup this attribute in a custom TextBoxFor helper method, such that I can add text-transform: uppercase so it looks correct on the client side.

    Does anyone have any ideas out there?

  • Nick
    Nick almost 11 years
    Thanks Daniel, unfortunately the DefaultModelBinder cant be assigned to Properties. AttributeUsageAttribute(AttributeTargets.Class|AttributeTarg‌​ets.Struct|Attribute‌​Targets.Enum|Attribu‌​teTargets.Interface|‌​AttributeTargets.Par‌​ameter
  • Daniel Schilling
    Daniel Schilling almost 11 years
    Then we will have to go one level higher. We need to change the model binder for the classes that contains these strings, change the UppercaseAttribute to a simple marking attribute, and tell the model binder to look for this attribute and treat those strings differently.
  • ecasper
    ecasper almost 10 years
    Hi, does this apply to MVC 4? because I'm receiving following error. 'UpperCaseValidationAttribute' does not implement interface member 'System.Web.Mvc.IClientValidatable.GetClientValidationRules(‌​System.Web.Mvc.Model‌​Metadata, System.Web.Mvc.ControllerContext)' could you please help? Thanks!!
  • Nick
    Nick almost 10 years
    Yes this works in MVC4. You need to add an override for public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context). This was the bit in the ... in the answer :)
  • Ravi Mehta
    Ravi Mehta about 8 years
    Does it Store Text in database in Capital Form? @Nick
  • Nick
    Nick about 8 years
    As far as the Controller knows, the text is always presented in uppercase. Assuming you pass that directly to the db, then yes it would hit the db in uppercase.