Handle every get or set call to objects properties

2019-09-01 04:02发布

I am searching for a good approach to automatically check the access rights when accessing a propertys getter and setter.

This is just for curiosity and experimental purposes, so performance does not really matter.

One Approach I found is the following (example is just for getters):

public class DynamicPropertyClass : DynamicObject
{
    /// <summary>
    /// I want this to work for a default auto-property
    /// </summary>
    public string TestString { get; set; }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (StaticSecurityContext.CheckSecurityCondition(binder.GetType(), binder.Name))
        {
            return base.TryGetMember(binder, out result);
        }
        else
        {
            throw new SecurityException();
        }
    }
}

Using a dynamic object to abort if the Security Conditions are not given. The problem with this approach is that properties that actually exist on the class won't get handled by the TryGetMember method. So this approach forces me to only handle properties that do not actually exist in code.

As you can see the dynamic capabilitys are not really relevant for my approach. I want something similar to work on my properties written in code, dynamic capabilitys do not have to be supported.

So, is there another approach, possible in c#, without applying attributes on every property or adding custom code to the getters and setters?

标签: c# reflection
2条回答
我想做一个坏孩纸
2楼-- · 2019-09-01 04:06

It is an old question, but I thought it'd be interesting to contribute since DynamicProxy was not mentioned. You can also use the DynamicProxy nuget package from Castle.Core.

You can intercept calls to Get and Set methods for all the virtual properties of your class.

I have written a Gist explaining the concept, tracking when properties are being accessed and set.

Here is what the Interceptor looks like.

public class GetSetInterceptor : Interceptor
{
    protected override void ExecuteBefore(IInvocation invocation)
    {
    }

    protected override void ExecuteAfter(IInvocation invocation)
    {
        if(invocation.Method.Name.StartsWith("get_") || invocation.Method.Name.StartsWith("set_"))
        {
            var target = invocation.InvocationTarget as TrackedObject;
            if(target == null)
            {
                return;
            }
            var methodInvoked = invocation.Method.Name.Split("_");
            switch (methodInvoked[0])
            {
                case "get":
                    target.AddEvent(EventType.Get, methodInvoked[1], invocation.ReturnValue);
                    break;
                case "set":
                    target.AddEvent(EventType.Set, methodInvoked[1], invocation.Arguments[0]);
                    break;
            }
        }
    }
}

The creation of the proxy is done like so:

    ProxyGenerator generator = new ProxyGenerator();
    var tracked = generator.CreateClassProxy<TrackedClass>(new GetSetInterceptor());

The TrackedClass must have virtual properties:

public class TrackedClass : TrackedObject
{
    public virtual string SomeContent { get; set; }
    public virtual int SomeInt { get; set; }
}

The test is here (using xUnit):

public class GetterSetterInterceptorTests
{
    [Fact]
    public void SuccessFullyRegisterGetAndSetEvents()
    {
        ProxyGenerator generator = new ProxyGenerator();
        var tracked = generator.CreateClassProxy<TrackedClass>(new GetSetInterceptor());
        tracked.SomeContent = "some content";
        Assert.Single(tracked.GetEvents());
        var eventAfterSomeContentAssigned = tracked.GetEvents().Last();
        Assert.Equal(EventType.Set, eventAfterSomeContentAssigned.EventType);
        Assert.Equal("some content", eventAfterSomeContentAssigned.Value);
        Assert.Equal("SomeContent", eventAfterSomeContentAssigned.PropertyInfo.Name);
        tracked.SomeInt = 1;
        Assert.Equal(2, tracked.GetEvents().Count);
        var eventAfterSomeIntAssigned = tracked.GetEvents().Last();
        Assert.Equal(EventType.Set, eventAfterSomeContentAssigned.EventType);
        Assert.Equal(1, eventAfterSomeIntAssigned.Value);
        Assert.Equal("SomeInt", eventAfterSomeIntAssigned.PropertyInfo.Name);
        var x = tracked.SomeInt;
        Assert.Equal(3, tracked.GetEvents().Count);
        var eventAfterSomeIntAccessed = tracked.GetEvents().Last();
        Assert.Equal(EventType.Get, eventAfterSomeIntAccessed.EventType);
        Assert.Equal(1, eventAfterSomeIntAccessed.Value);
        Assert.Equal("SomeInt", eventAfterSomeIntAccessed.PropertyInfo.Name);
    }
}
查看更多
Deceive 欺骗
3楼-- · 2019-09-01 04:24

You can use a proxy pattern. It exist a lot of implemention provided by AOP framework but if performance is not condition, you can simply try Transparent/RealProxy used by remoting API.

https://msdn.microsoft.com/fr-fr/library/system.runtime.remoting.proxies.realproxy(v=vs.110).aspx

查看更多
登录 后发表回答