Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Routing;
- namespace Funda.Web.FundaMobiel.Helpers
- {
- public class AnalyticsRouteTable
- {
- public AnalyticsRouteTable()
- {
- Routes = new Dictionary<Func<RouteData, bool>, string>();
- }
- public Dictionary<Func<RouteData, bool>, string> Routes { get; set; }
- public void Add<T>(Expression<Action<T>> expectation, string tag)
- {
- Routes.Add(AnalyticsHelper.CompileRule<T>(expectation), tag);
- }
- }
- public static class AnalyticsHelper
- {
- private static object _lock = new object();
- public static void Initialize(AnalyticsRouteTable routeTable)
- {
- lock(_lock)
- {
- _routes = routeTable.Routes;
- }
- }
- private static Dictionary<Func<RouteData, bool>, string> _routes = new Dictionary<Func<RouteData, bool>, string>();
- /// <summary>
- /// Compiles a given rule into a delegate
- /// </summary>
- /// <typeparam name="TController">Controller-type</typeparam>
- /// <param name="expectation">Action on the controller</param>
- /// <returns></returns>
- internal static Func<RouteData, bool> CompileRule<TController>(Expression<Action<TController>> expectation)
- {
- // bijhouden van een lijst met expectation
- Dictionary<string, object> expects = new Dictionary<string, object>();
- // lijst met de calls naar 'MatchesRouteData'
- List<MethodCallExpression> methodCalls = new List<MethodCallExpression>();
- // eerste argument van de delegate die we maken
- ParameterExpression parameterExpression = Expression.Parameter(typeof(RouteData), "rd");
- // controller & action uitlezen vanuit de expectation
- expects.Add("Controller", expectation.Parameters[0].Type.Name.Replace("Controller", ""));
- expects.Add("Action", ((MethodCallExpression)expectation.Body).Method.Name);
- // alle parameters die zijn meegegeven aan de actie aflopen
- var parameters = ((MethodCallExpression)expectation.Body).Method.GetParameters();
- for (var ix = 0; ix < parameters.Length; ix++)
- {
- // parameterinfo voor het type & de ingevoerde waarde
- var methodParam = parameters[ix];
- var expression = ((MethodCallExpression) expectation.Body).Arguments[ix];
- object value;
- // een MemberExpression moeten we even de waarde ophalen die deze referenced
- if(expression is MemberExpression)
- {
- value = Expression.Lambda(expression).Compile().DynamicInvoke();
- }
- // Constant is lekker simpel:
- else if (expression is ConstantExpression)
- {
- value = ((ConstantExpression) expression).Value;
- }
- // Aan andere waardes doen we voorlopig niet
- else
- {
- continue;
- }
- // null? dan negeren we deze
- if (!methodParam.ParameterType.IsValueType && value == null)
- {
- continue;
- }
- // ben je geen reference type?
- else if (methodParam.ParameterType.IsValueType
- && !methodParam.ParameterType.IsEnum)
- {
- // generic en geen reference? dan ben je Nullable<>
- if (methodParam.ParameterType.IsGenericType)
- {
- // als value == null, dan negeren
- if (Activator.CreateInstance(methodParam.ParameterType) == value)
- continue;
- }
- // anders: als waarde de default waarde van het type is, negeren (voor long is dit dus 0)
- else if (Activator.CreateInstance(methodParam.ParameterType).Equals(value))
- {
- continue;
- }
- }
- // voeg toe aan de lijst met expectations
- expects.Add(methodParam.Name, value);
- }
- // omzetten van expectation -> method call naar MatchesRouteData
- foreach (var expect in expects)
- {
- methodCalls.Add(Expression.Call(typeof(AnalyticsHelper).GetMethod("MatchesRouteData", BindingFlags.Static | BindingFlags.NonPublic),
- parameterExpression,
- Expression.Constant(expect.Key),
- // we moeten expliciet omzetten naar object; normaal gaat dit impliciet, maar niet in een expression tree
- Expression.Convert(Expression.Constant(expect.Value), typeof(object)),
- Expression.Constant(expect.Value.GetType())));
- }
- // voeg alles samen
- BinaryExpression wholeExpression = null;
- foreach (var call in methodCalls)
- {
- if (wholeExpression == null)
- {
- wholeExpression = Expression.MakeBinary(ExpressionType.AndAlso, Expression.Constant(true), call);
- continue;
- }
- // we gebruiken AndAlso
- wholeExpression = Expression.MakeBinary(ExpressionType.AndAlso, wholeExpression, call);
- }
- // de delegate wordt nu:
- // routeData => MatchesRouteData(...) && MatchesRouteData(...) && MatchesRouteData(...)
- // hetzelfde als je zelf een lambda hiervoor zou schrijven
- var del = (Func<RouteData, bool>)Expression.Lambda(wholeExpression, parameterExpression).Compile();
- return del;
- }
- public static string GetTag(RouteData routeData)
- {
- var val = _routes.Where(d => d.Key.Invoke(routeData));
- if (!val.Any()) return "unknown";
- return "/" + val.First().Value;
- }
- /// <summary>
- /// Validates whether the given key in routeData is equal to the given value
- /// </summary>
- /// <param name="routeData">Full set of routedata</param>
- /// <param name="key">Parameter name</param>
- /// <param name="value">Expected value</param>
- /// <param name="type">The full type, for modelbinder</param>
- /// <returns></returns>
- private static bool MatchesRouteData(RouteData routeData, string key, object value, Type type)
- {
- object outValue;
- if (routeData.Values.TryGetValue(key, out outValue))
- {
- // langs smartbinder halen
- if (ModelBinders.Binders.DefaultBinder != null)
- {
- outValue = ModelBinders.Binders.DefaultBinder.BindModel(GetControllerContext(), GetModelBindingContext(key, outValue, type));
- }
- if (outValue != null && outValue.Equals(value))
- {
- return true;
- }
- if(outValue != null && outValue.ToString().Equals(value.ToString(), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- return false;
- }
- private static ControllerContext GetControllerContext()
- {
- return new ControllerContext();
- }
- private static ModelBindingContext GetModelBindingContext(string id, object value, Type type)
- {
- ValueProviderResult valueProviderResult = new ValueProviderResult(value, value.ToString(), new CultureInfo("nl-NL"));
- ValueProviderDictionary dictionary = new ValueProviderDictionary(null)
- {
- { id, valueProviderResult }
- };
- return new ModelBindingContext
- {
- ModelName = id,
- ValueProvider = dictionary,
- ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type)
- };
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement