2

I have basic class:

public abstract class AbstractBaseModel : IModel
{
    [Display(Name = "Some Name")]
    [DisplayFormat(DataFormatString = "{0:d0}")]
    [RegularExpression(@"[0-9]{1,10}", ErrorMessage = "error")]
    public virtual string SomeName{ get; set; }
}

The IModel interface is just simple declaration of properties:

public interface IModel
{
    string SomeName{ get; set; }
}

From the base model I have 2 derived models

public class ClientModel : AbstractBaseModel
{
    [Required(ErrorMessage = "Some error message for customer only")]
    public override string SomeName{ get; set; }
}

public class PowerUserModel : AbstractBaseModel
{
    [Required(ErrorMessage = "Different message for the admin")]
    public override string SomeName{ get; set; }
}

This model, or rather interface is part of another model that combines multiple models:

public class ComboEndModel
{
     public IModel Model { get; set; }
     public IDifferentModel DifferentModel { get; set; }
}

Depending on the View/Controler that is currently used, I pass new ClientModel or PowerUserModel as a Model in ComboEndModel

When the view is rendered, I'm only getting the annotations from the base, abstract model, not the ones added in the derivative type. I suspect that's because I'm using interface as a nested model property instead of type.

Whats the correct way of implementing this relation, or working around the issue with incorrect annotations? Should I try with custom binding?

3
  • Redacted thought you had dup. tags; My mistake (sips coffee) Commented Aug 28, 2015 at 16:57
  • Can you share the parts of your view code that would cause the model annotations to matter? Are you using @Html.EditorFor(), for example? Commented Aug 28, 2015 at 20:32
  • What you are noticing is the expected behavior, and more importantly, you will not be able to bind to you model when you submit to the POST method because the DefaultModelBinder cannot initialize interfaces. Use concrete implementations. Commented Aug 28, 2015 at 22:30

2 Answers 2

3

One thing that might work for you would be to create editor templates for each derived model.

If you define your ComboEndModel like this

var model = new ComboEndModel() { Model = new PowerUserModel() };
return View(model);

and in your view you use EditorFor

@model WebApplication.Models.ComboEndModel
@Html.EditorFor(m => m.Model)

depending on what Type Model is, it will pick the associated editor template and apply the annotations for that derived type. In this case you'd need a partial view in your EditorTemplates folder named PowerUserModel.cshtml with a model type of PowerUserModel

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

2 Comments

You actually don't need the editor templates at all, unless you want to customise the markup. The default editor template will cope with this scenario just fine.
No arguments from me there. ;-)
2

Ok I have some problems with that too, and what I understood so far is (someone correct me if I'm wrong):

Why this problem occurs?

Because the DataAnnotation that is retrieved are retrived from the type of the model. Let me show you some samples:

If you have in your .cshtml file:

Sample 1:

@model AbstractBaseModel
// The type that will be retrived is `AbstractBaseModel`.

Sample 2:

@model IModel
// The type that will be retrived is `IModel`.

Or in other case if you have actions:

public ActionResult SomeAction(AbstractBaseModel model) { /* ... */ }

Sample 2:

public ActionResult SomeAction(IModel model) { /* ... */ }

So what matters is what type you specified in @model or in the action parameter. Doesn't matter if is interface, abstract class, base class or other type of class, matter only the type you described in your use.

In your example specifically you wrote:

public class ComboEndModel
{
    public IModel Model { get; set; }
    // ...
}

So the DataAnnotation will be retrive from IModel, but IModel don't have a RequiredAttribute defined so you get one step further in the chain of inheritance/implementation and get AbstractBaseModel. (Try this to confirm what I'm writing: put a required in IModel.SomeName with a different message and see what message will get).

Solution/Suggestion

If I'm right, there is no solution for your problem without change the strategy. So here some suggestions about what you can do:

  • Remove Required attribute and do yourself the validation (in action or javascript, depending of how type of validation you're using).
  • Replace the error message after validation occurs, (I did this in my case).
  • Give up of this inheritance and make independent models.
  • 1 Comment

    What you're talking about is a different problem. EditorTemplates don't support interfaces at all. On top of this, there is a deeper issue with interfaces and attributes, which you're hinting at suggesting as a fix. From this link the dev team cover their reasoning behind why data annotations and interfaces don't play nicely together. See the accepted answer there for the explanation.

    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.