1

I am trying to come up with a way that (either static or instance) method calls can be intercepted by dynamic proxy. I want to implement it as c# extension methods but stuck on how to generate dynamic proxy for static methods.

Some usages:

Repository.GetAll<T>().CacheForMinutes(10);
Repository.GetAll<T>().LogWhenErrorOccurs();

//or     
var repo = new Repository();
repo.GetAll<T>().CacheForMinutes(10);
repo.GetAll<T>().LogWhenErrorOccurs();

I am open to any library (linfu, castle.dynamic proxy 2 or etc).

Thanks!

Jeff
  • 13,079
  • 23
  • 71
  • 102
  • 2
    Don't. Just avoid static classes. Stick to instance classes abstracted by interfaces and you will be more happy. Oh and you can intercept those. – Krzysztof Kozmic Apr 23 '10 at 05:59

2 Answers2

9

Totally impossible.

In fact, proxies can't even be generated on all instance methods - they have to be virtual, so that the proxy generator can create a derived class and override them.

Static methods are never virtual, and therefore, cannot be overridden by a proxy.

(Technically there's a workaround for non-virtual methods which is to derive the class from MarshalByRefObject, but remoting-based solutions to this are slow and clunky and still won't support static methods.)

Given that your class is named Repository, I'm going to suggest that you make these methods instance methods instead. These kinds of operations generally shouldn't be static to begin with. If you make them static, you lose a lot of things: Loose coupling, mocking, dependency injection, a certain amount of unit testability, and - as you've just discovered - proxying and interception.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • @Downvoter: I'd love to hear this one. You got a better answer? – Aaronaught Apr 23 '10 at 02:33
  • Can you provide a little more detail on the workaround for non-virtual methods? Would it work to proxy the Add method on List ? – Maslow Apr 09 '11 at 15:19
  • @Maslow: I could answer that, but I'd be doing you a disservice. `List` implements `IList`, so you should never be trying to proxy the class itself; design your code to always use `IList` and either start with your own custom implementation or just wrap the `List` with it when you need that behaviour. – Aaronaught Apr 09 '11 at 15:48
  • it's not my code, I can not recompile a microsoft.dll designed to work only with List, I always write to IList, but what I'm trying to cover is a poor behavior design decision to me, and just override it. – Maslow Apr 09 '11 at 18:50
  • @Maslow: Are you saying that there is a *Microsoft* library out there that not only insists on a `List` as an argument somewhere, but actually modifies its contents? That is really quite shockingly poor design. If you are absolutely certain that you cannot simply wrap it, see the answers to [How do I intercept a method call in C#?](http://stackoverflow.com/q/25803/38360) – Aaronaught Apr 09 '11 at 21:15
0

Impossible with common interception strategies.

But most of AOP Framework working at compile time can do it. (example : PostSharp)

I work on an open source NConcern AOP Framework.

This is a simple .NET AOP Framework allowing interception at runtime by swaping methods.

It can do its job for virtual methods, non virtual methods and static method without any factory pattern and inheritance needs.

My recommandation is avoid use AOP to "monkey patch" and static methods must be only "singleton usages shortcut", not a mainstream.

in your case it is easier to use singleton pattern with static methods as shortcup and DI (Dependency Injection) to enable easy proxy pattern.

Example :

interface

public interface IRepository
{
    IQueryable<T> Query<T>()
        where T : class;
}

the sugar using DI (via a factory)

static public class Repository
{
    //You can wrap the interface (proxy) here if you need...
    static private readonly IRepository m_Repository = MyDIFactory.Import<IRepository>();

    static public IQueryable<T> Query<T>()
        where T : class
    {
        return Repository.m_Repository.Query<T>();
    }
}

Usage

Repository.Query<T>().CacheForMinutes(10);
Repository.Query<T>().LogWhenErrorOccurs();
Tony THONG
  • 772
  • 5
  • 11