Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
570 views
in Technique[技术] by (71.8m points)

asp.net mvc - Finding custom attributes on view model properties when model binding

I've found a lot of information on implementing a custom model binder for validation purposes but I haven't seen much about what I'm attempting to do.

I want to be able to manipulate the values that the model binder is going to set based on attributes on the property in the view model. For instance:

public class FooViewModel : ViewModel
{
    [AddBar]
    public string Name { get; set; }
}

AddBar is just

public class AddBarAttribute : System.Attribute
{
}

I've not been able to find a clean way to find the attributes on a view model property in the custom model binder's BindModel method. This works but it feels like there should be a simpler solution:

public class FooBarModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = base.BindModel(controllerContext, bindingContext);

        var hasBarAttribute = false;

        if(bindingContext.ModelMetadata.ContainerType != null)
        {
            var property = bindingContext.ModelMetadata.ContainerType.GetProperties()
                .Where(x => x.Name == bindingContext.ModelMetadata.PropertyName).FirstOrDefault();
            hasBarAttribute = property != null && property.GetCustomAttributes(true).Where(x => x.GetType() == typeof(AddBarAttribute)).Count() > 0;
        }

        if(value.GetType() == typeof(String) && hasBarAttribute)
            value = ((string)value) + "Bar";

        return value;
    }
}

Is there a cleaner way to view the attributes on the view model property or a different kind of attribute I could be using? The DataAnnotation attributes really seem to be for a different problem.

UPDATE

Craig's answer got me to the right place but I thought I'd put some examples in here for others.

The metadata provider I ended up with looks like

public class FooBarModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var metaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        if(attributes.OfType<AddBarAttribute>().Any())
            metaData.AdditionalValues.Add("AddBarKey", true);

        return metaData;
    }
}

The model binder looks like:

public class FooBarModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = base.BindModel(controllerContext, bindingContext);

        if(bindingContext.ModelMetadata.AdditionalValues.ContainsKey("AddBarKey"))
            value = ((string)value) + "Bar";

        return value;
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The "correct" way (per the guy who wrote it) is to write a model metadata provider. There's an example at the link. Not precisely "simple," but it works, and you'll be doing what the rest of MVC does.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...