The Code Slinger

January 31, 2011

Client Specific Metadata in your MVC Application

Filed under: MVC — Pete @ 12:21 pm

Recently I was working on a new requirement for one of our systems in which each different client may refer to the same data types differently.  For example, ClientA might refer to roadways as a “Parkways” whereas ClientB and ClientC might refer to them as “Highways”.   Changes like these obviously aren’t big enough to warrant separate Views for each screen in which data types like these are referenced or mentioned, however sometimes small things like that make a big difference to the client and makes the application feel more in line with their existing business processes.

So, in order to make it so that my application is still a single code-base that will work for all the various clients that might come along and their varying terminologies, here is what I came up with that I think integrates nicely into an existing MVC application and requires only slight modifications.

First off, here is my interface and concrete implementation of a dictionary wrapper that I will use to not only store the key/value pairs which represent the data types but which will be what my Views utilize as an appendage to their ViewModel instances.

    public interface IMeta

    {

        MetaDictionary Meta { get; set; }

    }

 

    public class MetaDictionary

    {

        private Dictionary<string, KeyValuePair<string,string>> dict;

        public MetaDictionary()

        {

            dict = new Dictionary<string, KeyValuePair<string, string>>();

        }

 

        public void Add(string key, KeyValuePair<string,string> value)

        {

            if (!dict.ContainsKey(key))

                dict.Add(key, value);

            else

                dict[key] = value;

        }

        public void Remove(string key)

        {

            if (dict.ContainsKey(key))

                dict.Remove(key);

        }

 

        public string GetValue(string key)

        {

            if(dict.ContainsKey(key))

                return dict[key].Key;

            else

                return "";

        }

        public string GetValueAbbrev(string key)

        {

            if(dict.ContainsKey(key))

                return dict[key].Value;

            else

                return "";

        }

    }

Next, I need to write an action filter that will fetch the appropriate key/value pairs relevant to the currently logged in user and which client they belong to.  Please note that this is where your logic goes to determine A) where to retrieve your key/value pairs to begin with (DB, XML, config file, etc) and B) what differentiating factor(s) you might have which would determine how your application knows each client.  In my situation, I currently have a variable which would store the ClientID in a pre-existing injected Session wrapper.  Also note, I’m using StructureMap as my DI container.

    public class ClientMetaAttribute : ActionFilterAttribute, IActionFilter

    {

        private readonly IControllerDependency Services;

        public ClientMetaAttribute():this(ObjectFactory.GetInstance<IControllerDependency>())

        {

        }

        public ClientMetaAttribute(IControllerDependency service)

        {

            Services = service;

        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)

        {

            //Lookup all appropriate attributes.

            int? clientid = Services.SessionHandler.ClientID;

            var metadict = new MetaDictionary();

            //FILL YOUR META dictionary here!!

 
           ((IMeta)((ViewResultBase)filterContext.Result).ViewData.Model).Meta = metadict;

            base.OnResultExecuting(filterContext);

        }

 

        public override void OnResultExecuted(ResultExecutedContext filterContext)

        {

            base.OnResultExecuted(filterContext);

        }

Now, once I’ve implemented IMeta in the ViewModel used for my action method:

    public class ClientViewModel: IMeta

    {

        public MetaDictionary Meta { get; set; }

        //Rest of model class definition…

    }

Then I simply need to update any hardcoded references within View(s) which use this ViewModel such as:

<h4><%: Model.Meta.GetValue("roadname") %> (<%: Model.Meta.GetValueAbbrev("roadname") %>)</h4>

Which for ClientA will give me

Parkway (PKWY)

and for for ClientB & ClientC will give

Highway (HWY)

Obviously this could be expanded in terms of the dictionary value storage to include a host of other information that may be specific to your needs, however this example shows the intent.  Additionally, another enhancement would be to extend this for use in a localization scenario where multiple languages may be necessary as well. 

December 28, 2010

Expression based RenderAction HtmlHelper extension (with Authorization)

Filed under: ASP.NET,C#,MVC — Pete @ 11:39 am

        public static void RenderAuthorizedAction<TController>(this HtmlHelper helper, Expression<Action<TController>> action) where TController : Controller

        {

            var routeValuesFromExpression = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression<TController>(action);

 

            if(helper.IsAuthorized(action))

                helper.RenderAction(routeValuesFromExpression["Action"].ToString(), routeValuesFromExpression);

        }

 

        public static bool IsAuthorized<TController>(this HtmlHelper helper, Expression<Action<TController>> action)

        {

            var call = action.Body as MethodCallExpression;

 

            if (call == null) return false;

 

            var authorizeAttributes = call.GetAttributes<IAuthorizationFilter>();

            if (authorizeAttributes.Length == 0) return true;

 

            var controllerContext = helper.ViewContext.Controller.ControllerContext;

            var controllerDescriptor = new ReflectedControllerDescriptor(typeof(TController));

            var actionDescriptor = new ReflectedActionDescriptor(call.Method, call.Method.Name, controllerDescriptor);

 

            return authorizeAttributes.All(a => IsAuthorized(a, controllerContext, actionDescriptor));

        }

I’m sure this is coming in MVC 3, however I needed it now and it’s fairly straightforward.  To use it, simply call from your View as:

<% Html.RenderAuthorizedAction<MyController>(a => a.MyActionMethod()); %>

Blog at WordPress.com.