4

I couldn't think of a better way of phrasing this so it's possible this is out there but I don't know the term for it. I have a number of classes for accessing different datastores which follow a pattern like this:

interface IUserData {
  User GetUser(uint id);
  User ByName(string username);
}

class UserData : IUserData {
  ...
}

class AuthorizedUserData : IUserData {
  IUserData _Data = new UserData();

  public User GetUser(uint id) {
    AuthorizationHelper.Instance.Authorize();
    return _Data.GetUser(id);
  }

  public User ByName(string name) {
    AuthorizationHelper.Instance.Authorize();
    return _Data.ByName(name);
  }
}

So the basic setup is:

  1. Create an interface
  2. Create a concrete class for actually implementing that interface
  3. Create a wrapper for that class which performs the same body of work before calling into the concrete class

Given that those classes implement the same interface and the exact same body of work is done at the beginning of each method in the wrapping class, this makes me think I can automate this wrapping process.

I know creating such a decorator is possible in JavaScript and Python.

Example in JavaScript:

function AuthorizedUserData() {
  ...
}

const userDataPrototype = Object.getPrototypeOf(new UserData());
Object.getOwnPropertyNames(userDataPrototype)
  .forEach(name => {
    const val = userDataPrototype[name];
    if (typeof val !== 'function') {
      return;
    }

    AuthorizedUserData.prototype[name] = function(...args) {
      AuthorizationHelper.Authorize();
      return this._Data[name](...args);
    };
  });

Is this sort of automatic implementation possible in C#?

Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • Something like interceptors ?? https://stackoverflow.com/questions/2592787/what-really-interceptors-do-with-my-c-sharp-class – Rudresha Parameshappa Apr 03 '18 at 13:53
  • Can`t you use an abstract class? – richej Apr 03 '18 at 13:59
  • @RudreshaParameshappa That definitely looks promising, yes. @richej I don't understand how that would help. I still need to be able to instantiate `UserData` at will and the wrapper would still need to manually call each of the methods on `IUserData` or the matching base method via `base.GetUser` or similar. – Mike Cluck Apr 03 '18 at 14:02
  • @MikeC I am creating a simple solution. I will try to post in some time – Rudresha Parameshappa Apr 03 '18 at 14:04
  • 1
    [PostSharp](https://www.postsharp.net/) will do exactly what you're describing using post IL weaving (instead of DI) – qujck Apr 03 '18 at 15:36

2 Answers2

1

Use Any Dependency Injection (DI) framework. The below one uses WindsorCastle.

use nuget to install Windsor Castle.

The interceptors can be used in your scenario to intercept any request to a method.

Interceptors can be created by implementing IInterceptor

public class AuthorizedUserData : IInterceptor
{
   public void Intercept(IInvocation invocation)
   {
       //Implement validation here
   }
}

Use a DI container to register the dependencies and register your interceptor and classes

var container = new WindsorContainer();
container.Register(Castle.MicroKernel.Registration.Component.For<AuthorizedUserData>().LifestyleSingleton());
container.Register(
               Castle.MicroKernel.Registration
               .Classes
               .FromAssemblyInThisApplication()
               .BasedOn<IUserData>()
               .WithServiceAllInterfaces().Configure(
                   x => x.Interceptors<AuthorizedUserData>()));

Your class and interface structure will be as below

    public interface IUserData
    {
        User GetUser(uint id);
        User ByName(string username);
    }

    public class UserData : IUserData
    {
        public User GetUser(uint id)
        {
            throw new System.NotImplementedException();
        }

        public User ByName(string username)
        {
            throw new System.NotImplementedException();
        }
    }


    public class User
    {

    }

Then use DI container to resolve the instance you need. Here we need an instance of IUserData

var user = container.Resolve<IUserData>(); // Creates an instance of UserData
user.ByName("userName"); //This call will first goto `Intercept` method and you can do validation.
Rudresha Parameshappa
  • 3,826
  • 3
  • 25
  • 39
  • 1
    This is great! Thanks for the answer. I'm going to leave the question open for a while to see if there is a solution which does not use a DI framework but this is definitely a valid solution and one I could use. – Mike Cluck Apr 03 '18 at 14:34
0

You are looking for something related to AOP. in .Net you can use RealProxy this abstract class.

Here is a simple to make it.

Create a class and inherit RealProxy,the public override IMessage Invoke(IMessage msg) is your weaving point.

internal class DynamicProxy<T> : RealProxy
    where T : MarshalByRefObject,new()
{
    private T _target;

    private IMethodCallMessage callMethod = null;

    private IMethodReturnMessage returnMethod = null;

    public DynamicProxy(T target) : base(typeof(T))
    {
        _target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        callMethod = msg as IMethodCallMessage;
        MethodInfo targetMethod = callMethod.MethodBase as MethodInfo;

        FilterInfo Attrs = new FilterInfo(_target, targetMethod);


        ExcuteingContext excuting = Excuting(Attrs.ExcuteFilters);
        if (excuting.Result != null)
        {
            returnMethod = GetReturnMessage(excuting.Result, excuting.Args);
        }
        else
        {
            InvokeMethod(targetMethod, excuting);

            ExcutedContext excuted = Excuted(Attrs.ExcuteFilters);
        }

        return returnMethod;
    }

    private void InvokeMethod(MethodInfo targetMethod, ExcuteingContext excuting)
    {
        object result = targetMethod.Invoke(_target, excuting.Args);
        returnMethod = GetReturnMessage(result, excuting.Args);
    }


    private ExcutedContext Excuted(IList<IExcuteFilter> filters)
    {
        ExcutedContext excutedContext = new ExcutedContext(returnMethod);

        foreach (var filter in filters)
        {
            filter.OnExcuted(excutedContext);
            if (excutedContext.Result != null)
                break;
        }

        return excutedContext;
    }

    private ExcuteingContext Excuting(IList<IExcuteFilter> filters)
    {
        ExcuteingContext excuteContext = new ExcuteingContext(callMethod);

        foreach (var filter in filters)
        {
            filter.OnExcuting(excuteContext);
            if (excuteContext.Result != null)
                break;
        }

        return excuteContext;
    }

    private ReturnMessage GetReturnMessage(object result, object[] args)
    {
        return new ReturnMessage(result,
                                    args,
                                    args.Length,
                                    callMethod.LogicalCallContext,
                                    callMethod);
    }
}    

Getting AopBaseAttribute filter (weaving) on proxy class

/// <summary>
/// Getting filter Attribute on proxy class
/// </summary>
public class FilterInfo
{
    private List<IExcuteFilter> _excuteFilters = new List<IExcuteFilter>();

    public FilterInfo(MarshalByRefObject target, MethodInfo method)
    {
        //search for class Attribute
        var classAttr = target.GetType().GetCustomAttributes(typeof(AopBaseAttribute), true);
        //search for method Attribute
        var methodAttr = Attribute.GetCustomAttributes(method, typeof(AopBaseAttribute), true);

        var unionAttr = classAttr.Union(methodAttr);

        _excuteFilters.AddRange(unionAttr.OfType<IExcuteFilter>());
    }

    public IList<IExcuteFilter> ExcuteFilters
    {
        get
        {
            return _excuteFilters;
        }
    }
}

Create an interface IExcuteFilter let FilterInfo get and weaving it

public interface IExcuteFilter
{
    void OnExcuted(ExcutedContext excuteContext);

    void OnExcuting(ExcuteingContext excutingContext);
}

Encapsulate Context for Excuteing method.

public class ExcuteingContext 
{
    public ExcuteingContext(IMethodCallMessage callMessage)
    {
        Args = callMessage.Args;
        MethodName = callMessage.MethodName;
    }

    public object[] Args { get; set; }

    public string MethodName { get; set; }

    public object Result { get; set; }
}

Encapsulate Context for Excuted method.

public class ExcutedContext
{
    public ExcutedContext(IMethodReturnMessage returnMethod)
    {
        Args = returnMethod.Args;
        MethodName = returnMethod.MethodName;
        Result = returnMethod.ReturnValue;
    }

    public object[] Args { get; set; }

    public string MethodName { get; set; }

    public object Result { get; set; }
}

/// <summary>
/// Filter AttributeBase
/// </summary>
public abstract class AopBaseAttribute : Attribute, IExcuteFilter
{

    public virtual void OnExcuted(ExcutedContext context)
    {
    }

    public virtual void OnExcuting(ExcuteingContext context)
    {
    }
}

/// <summary>
/// Customer Filter
/// </summary>
public class AuthorizedUserDataAttribute : AopBaseAttribute
{
    public override void OnExcuting(ExcuteingContext context)
    {
        //Console.WriteLine("Test");
        //Implement validation here
    }
}

ProxyFactory provide a Proxy instance.

public class ProxyFactory
{
    public static TInterface GetInstance<TInterface,TObj>() 
        where TObj : MarshalByRefObject,new()
    {
        TObj proxyObj = Activator.CreateInstance(typeof(TObj)) as TObj;
        return (TInterface)new DynamicProxy<TObj>(proxyObj).GetTransparentProxy();
    }
}

When you want to use need to call ProxyFactory.GetInstance<IUserData, UserData>

UserData must inherit MarshalByRefObject to be Transparent proxy.

public interface IUserData
{
    User GetUser(uint id);
    User ByName(string username);
}

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

[AuthorizedUserData]
public class UserData : MarshalByRefObject,IUserData
{
    public User GetUser(uint id)
    {
        return new User() { };
    }
    public User ByName(string username)
    {
        return new User() { };
    }
}

IUserData user = ProxyFactory.GetInstance<IUserData, UserData>();
user.ByName("1");

RealProxy

D-Shih
  • 44,943
  • 6
  • 31
  • 51
  • Sorry if I made myself unclear but this isn't what I'm asking for. What you're showing is dependency injection, which is definitely valuable. What I'm asking is if there is a way to avoid writing out the definition for each method in `AuthorizedUserData`. I want it to automatically call `AuthorizationHelper.Instance.Authorize()` in every method which is also implemented in `UserData` and then call into `_Data.{methodName}`. – Mike Cluck Apr 03 '18 at 13:58
  • @MikeC Sorry i misunderstood your question.I edit my answer and provide other solution. – D-Shih Apr 03 '18 at 16:17