quarta-feira, 20 de junho de 2012

Logging, Authorizing and Performance analysis on method calls

Intercepting method calls in order to:

  - Analyze the implementation performance
  - Authorize using
  - Log access

With use cases implemented by business class methods, it is a interesting way intercept the calls of these methods and apply these strategies.

How to intercept methods:
The instance of business should be created through a DynamicProxy.
The interceptable methods must be virtual, so that the proxy can override it.
Installs Castle.Core the project, obtained via NuGet, which provides a great API to intercept methods.

This recipe is for properties as well.

Below there is a simple application which implements the idea.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InterceptingApp
{
    using Castle.DynamicProxy;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var user = new User { Login = "It's me" };
            var allowedMethods = new List<MemberInfo> 
            {
                typeof(MyBusiness).GetMethod("GetProducts") 
            };

            var performanceInterceptor = new PerformanceInterceptor();
            var authorizeInterceptor = new AuthorizeInterceptor(allowedMethods);
            var logInterceptor = new LogInterceptor(user);

            var generator = new ProxyGenerator(new PersistentProxyBuilder());
            var business = generator.CreateClassProxy<MyBusiness>(performanceInterceptor, authorizeInterceptor, logInterceptor);

            try
            {
                // This call was allowed, so it should works just fine
                Console.WriteLine("Products:");
                foreach (var product in business.GetProducts())
                {
                    Console.WriteLine("Product name: {0} price: {1:c}", product.Name, product.Price);
                }

                
                // This call was not allowed, so it should throw an exception
                Console.WriteLine("BestSellers:");
                foreach (var product in business.GetBestSellers())
                {
                    Console.WriteLine("Product name: {0} price: {1:c}", product.Name, product.Price);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }

    public class MyBusiness
    {
        public virtual IEnumerable<Product> GetProducts()
        {
            System.Threading.Thread.Sleep(100);
            return new List<Product> 
            {
                new Product { Name = "Beer", Price = 1.80M },
                new Product { Name = "Soda", Price = 1.50M },
                new Product { Name = "Juice", Price = 2.15M }
            };
        }

        public virtual IEnumerable<Product> GetBestSellers()
        {
            System.Threading.Thread.Sleep(100);
            return new List<Product> 
            {
                new Product { Name = "Juice", Price = 2.15M }
            };
        }
    }

    public class User
    {
        public string Login { get; set; }
    }

    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

    class PerformanceInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            var start = DateTime.Now;
            try
            {
                invocation.Proceed();
            }
            catch
            {
                throw;
            }
            finally
            {
                var end = DateTime.Now;

                Console.WriteLine("[Performance]: Method {0}.{1} takes {2}",
                    invocation.Method.DeclaringType.Name,
                    invocation.Method.Name,
                    end - start);
            }
        }
    }

    class AuthorizeInterceptor : IInterceptor
    {
        IEnumerable<MemberInfo> _allowedMethods;
        public AuthorizeInterceptor(IEnumerable<MemberInfo> allowedMethods) { _allowedMethods = allowedMethods; }

        public void Intercept(IInvocation invocation)
        {
            if (_allowedMethods.Contains(invocation.Method))
            {
                Console.WriteLine("[Authorize]: Granted! You are allowed to access this method");
                invocation.Proceed();
            }
            else
            {
                throw new Exception("[Authorize]: Denied! You are not allowed to access this method.");
            }
        }
    }

    class LogInterceptor : IInterceptor
    {
        User _user;
        public LogInterceptor(User user) { _user = user; }

        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine("[Log]: Method {0}.{1} was called by '{2}' at {3}",
                 invocation.Method.DeclaringType.Name,
                 invocation.Method.Name,
                 _user.Login,
                 DateTime.Now);

            invocation.Proceed();
        }
    }
}


segunda-feira, 12 de março de 2012

With statement for C# through extension method

Extension methods are really cool feature. I was missing the VB with statement recently, so I thought it will be cool to implement a really simple extension method to do it. Below are a VB.Net, C#, short C# and C# through With extension method: VB.Net
With dataGridView.Columns["customer"]
    .Width = 200
    .HeaderText = "Customer"
    .HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
End With
C#
dataGridView.Columns["customer"].Width = 200;
dataGridView.Columns["customer"].HeaderText = "Customer";
dataGridView.Columns["customer"].HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
C# short
var column = dataGridView.Columns["customer"];
column.Width = 200;
column.HeaderText = "Customer";
column.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
C# With through extension method
dataGridView.Columns["customer"].With(column => {
    column.Width = 200;
    column.HeaderText = "Customer";
    column.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
});

static public class ObjectExtensions
{
    static public void With<T>(this T o, Action<T> action)
    {
        action(o);
    }
}