Automatic decorator methods

I could not think of a better way to formulate this, so it is possible, but I do not know this term. I have several classes for accessing various data stores that follow the following pattern:

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:

  • Create interface
  • Create a concrete class to implement this interface
  • Create a wrapper for this class that does the same amount of work before invoking a specific class

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

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

JavaScript example:

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 such automatic implementation possible in C #?

+4
2

(DI). WindsorCastle.

nuget .

.

IInterceptor

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

DI

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>()));

    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
    {

    }

DI, . 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.
+1

-, . .Net RealProxy .

.

RealProxy, public override IMessage Invoke(IMessage msg) - .

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);
    }
}    

AopBaseAttribute () -

/// <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;
        }
    }
}

IExcuteFilter let FilterInfo

public interface IExcuteFilter
{
    void OnExcuted(ExcutedContext excuteContext);

    void OnExcuting(ExcuteingContext excutingContext);
}

Excuteing.

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; }
}

Excuted.

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 Proxy.

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();
    }
}

ProxyFactory.GetInstance<IUserData, UserData>

UserData MarshalByRefObject .

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

0

Source: https://habr.com/ru/post/1695704/


All Articles