This was my solution to this issue, which is not great, but manageable.
In my model, I annotated the property with:
[DefaultValueSql("'A'")]
[DefaultValue("A")]
[Required]
The DefaultValueSql is a custom attribute that handles the creation of SQL default column values when doing migrations. The DefaultValue matches that value. I'm aware this is a violation of DRY, but using this solution removes the need for the default in the database anyway (I'll probably remove it in the future).
In my Controller, on the Create/Edit ActionResult post handlers I added this:
if (model.ActiveIndicator == null)
{
model.ActiveIndicator = ((DefaultValueAttribute)(model.GetType().GetProperty("ActiveIndicator").GetCustomAttributes(typeof(DefaultValueAttribute), true).First())).Value.ToString();
ModelState["ActiveIndicator"].Errors.Clear(); // Removes Model Error
}
This sets the default to the DefaultValue listed in the annotation and removes the ModelState error for this property allowing the ModelState.IsValid to be true.
This solution works well for string defaults, however non-string (types that can't be null), should probably be done differently, perhaps in the model class constructor.
Edit: If you are using TryUpdateModel, you must set the ModelState value instead of the submitted model value:
if (model.ActiveIndicator == null)
{
var defValue = ((DefaultValueAttribute)(model.GetType().GetProperty("ActiveIndicator").GetCustomAttributes(typeof(DefaultValueAttribute), true).First())).Value.ToString();
ModelState.SetModelValue("ActiveIndicator", new ValueProviderResult(defValue, "", CultureInfo.InvariantCulture));
ModelState["ActiveIndicator"].Errors.Clear(); // Removes Model Error
}