How to dynamically modify model meta data in ASP.NET MVC

Normally you just add the [Required] attribute to a view model to make it required. But I needed a way to configure whether a field to be required or not. The requirement was that it should be configured through web.config:

<appSettings>
    <add key="ticket-cat1-required" value="true" />
</appSettings>

Having to modify the view or the controller would not be very clean. Instead it’s much better to take advantage of the ModelValidatorProvider. I could have just done like this:

public class ConfigurableModelValidatorProvider : LocalizedModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<System.Attribute> attributes)
    {
        bool isRequired = metadata.ContainerType == typeof (CreateViewModel)
                            && ConfigurationManager.AppSettings["ticket-cat1-required"] == "true";


        var theAttributes = attributes.ToList();
        if (!theAttributes.Any(x => x is RequiredAttribute) && isRequired)
            theAttributes.Add(new RequiredAttribute());

        return base.GetValidators(metaDataContext.Metadata, context, attributes);
    }
}

And then assigned it in global.asax:

protected void Application_Start()
{
    ModelValidatorProviders.Providers.Clear();
    ModelValidatorProviders.Providers.Add(new ConfigurableModelValidatorProvider());

    //...
}

But that would have created a tightly coupled provider.

The loosely coupled way

Instead I decided to take advantage of my inversion of control container and define some interfaces.

/// <summary>
/// Can adapt the generated metadata before it's sent to the view
/// </summary>
public interface IModelMetadataAdapter
{
    /// <summary>
    /// Adapt the meta data
    /// </summary>
    /// <param name="context">Context information</param>
    void Adapt(MetadataContext context);
}

The context used to modify the meta data:

/// <summary>
/// context for <see cref="IModelMetadataAdapter"/>
/// </summary>
public class MetadataContext
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MetadataContext"/> class.
    /// </summary>
    /// <param name="metadata">The metadata.</param>
    public MetadataContext(ModelMetadata metadata)
    {
        if (metadata == null) throw new ArgumentNullException("metadata");
        Metadata = metadata;
    }

    /// <summary>
    /// See MSDN for info
    /// </summary>
    public ModelMetadata Metadata { get; set; }
}

Which allowed me to create this class (which is automatically registered in Griffin.Container):

[Component]
public class ToggleRequiredOnCreateModel : IModelMetadataAdapter
{
    public void Adapt(MetadataContext context)
    {
        if (context.Metadata.ContainerType != typeof(CreateViewModel))
            return;
        context.Metadata.IsRequired = false;
        if (context.Metadata.PropertyName != "Category1")
            return;

        context.Metadata.IsRequired = ConfigurationManager.AppSettings["ticket-cat1-required"] == "true";
    }
}

To make it all possible I’ve also have to modify the validator provider:

public class ConfigurableModelValidatorProvider : LocalizedModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<System.Attribute> attributes)
    {
        var services = DependencyResolver.Current.GetServices<IModelMetadataAdapter>();

        var metaDataContext = new MetadataContext(metadata);
        foreach (var service in services)
        {
            service.Adapt(metaDataContext);
        }

        var theAttributes = attributes.ToList();
        if (!theAttributes.Any(x => x is RequiredAttribute) && metaDataContext.Metadata.IsRequired)
            theAttributes.Add(new RequiredAttribute());

        return base.GetValidators(metaDataContext.Metadata, context, attributes);
    }
}

In my case I’m using my Griffin.MvcContrib project to handle the localization, that’s why I inherit LocalizedModelValidatorProvider and not DataAnnotationsModelValidatorProvider.



Switched to disqus

I’ve switched from wordpress comment system to disqus. The old comments will hopefully get imported pretty soon.



Introducing Griffin.Decoupled

I recommend that you read my other post first, since it describes the rationale behind Griffin.Decoupled. This post will only demonstrate how you use the framework.

Griffin.Decoupled allows you to execute commands and domain events within your application to reduce coupling between classes. In future versions you’ll also be able to scale out the application using different servers. And that without having to rewrite a single line of code. That’s one of the most important aspects of the framework: Start small and scale when required.

Continue Reading


When is a logger a dependency?

There are some discussion about whether loggers should get injected into your classes (dependency injection) or if you should use the logger facade (like LogManager.GetLogger<MyClass>()).

The answer rather simple:

As for all classes, dependencies which is mandatory should get injected.

That means that you should inject the logger if the class will fail without it (= not deliver the expected result). If the class can work without a logger: simply use the logger facade.


Writing decoupled and scalable applications

Preface

This article is the first in a series of three articles. It’s purpose is only to introduce you do domain events and commands. The next article will show you how you with almost no effort (thanks to Griffin.Decoupled) can get started the command/event type of applications. The series will end with and article showing how you can use the DDD domain model together with the command and event handling.

Continue Reading


Service locator is not an anti pattern.

(this article have been rewritten since it didn’t seem like I managed to communicate my thoughts properly)

There are several blog posts that states that service locator is an anti pattern. With this article I’ll try to explain why it’s not. Do understand that I’m not saying that it should be used for dependency management.

Continue Reading


Dealing with exceptions, Logging and displaying error messages

I got a client which is really firm on that the user should know what went wrong when exceptions are thrown. So we are displaying error messages like

“The database did not respond”
“The database operation failed”
“Failed to load configuration setting X”

etc etc.

If it were just me, I would simply just print something like:

“Oops, something unexpected happened. Please try again or contact the support department”

Why?

Ok. So let’s assume that we show the user a more detailed error message. What is he/she supposed to do with it? Remember, it’s from an exception. If something exceptional happens it unlikely that the user can fix the problem by reading an error message. At tops the user can try do perform the operation again.

When the user have got tired of trying he/she calls the support department. If we’re lucky the user manages to quote the error message exactly. But really. Do that help us? Not really. We still have to go through our logs to find out what happened.

The correct approach to dealing with exceptions

So what should we do when an exception happens? Well. Try to prevent it from happening in the future. That goes for all exceptions. Some can be prevented, whilst others can’t. The point is that we should at least try.

To be able to try to fix the exceptions we have to find them. Using a big fat log for everything is not a very good way. Instead try to have a seperate log for all unhandled exceptions. Make sure that you read that log. You might want to build something that emails that log to you daily.


Why Domain Driven Design (DDD) is so great

This article aims to describe why I think that DDD aids you tremendously in creating robust and flexible applications.

Today we got several cool frameworks and libraries that we can use. ORMs, WCF, ServiceStack, SignalR, WebApi, IoC etc etc. Just thinking of all can make any serious developer go crazy of joy.

Continue Reading