Lav dine egne Proxy klasser - nemt og ligetil

Udgivet: 20. februar 2010

For lang tid siden var jeg til noget Microsoft gøjl, hvor Jakob Tikjøb Andersen snakkede om DynamicProxy. Tiden er gået, og det er nu blevet dagen, hvor jeg tænkte jeg kunne bruge dette framework til at implementere noget caching på min blog.

DynamicProxy er en dynamisk proxy generator (flot oversat, ikke?). Ved en proxy klasse menes der en klasse som har én opgave i livet, og det er at ligne en anden klasse, og så kalde den anden klasses metoder.

Et fint lille eksempel kunne være, at man gerne ville have en log over hver gang denne klasse blev kaldt, samt at den noterede hvor lang tid metoden tog. Man kunne tjekke sikkerhed, osv. - brug din fantasi :)

Forestil dig at du har en klasse som har 100 metoder. Hver metode vil du gerne gøre noget bestemt ved - evt. ændre parametrene, eller som i vores eksempel logge. Skulle du lave dette manuelt, ville du nok være doven, og kun vælge at gøre det de vigtigste steder, og du ville have noget kode som er svær at vedligeholde.

DynamicProxy er en af de værktøjer som kan hjælpe dig af med dette. Jeg valgte at skrive denne blog post, da det overraskede mig hvor let, og ligetil det er at bruge. Faktisk tog det mig vel 2min at implementere et simpelt eksempel, og det overraskede mig alligevel noget, da jeg forestille mig at der sikkert skulle store XML filer til, for at få det til at virke.

Først gik jeg ind og hentede DynamicProxy, jeg tilføjede derefter DLL filerne til mit lille test projekt, samt sørgede for at jeg havde en reference til System.Web (Umildbart så kræver DynamicProxy System.Web) Andre siger dette ikke er nødvendigt med System.Web, så hvorfor jeg lige skulle bruge den, kan jeg ikke svare på.

Jeg lavede derefter jordens mest simple interface og klasse:

public interface IDataProvider
{
    void DoStuff();
}

public class DataProvider : IDataProvider
{
    public void DoStuff() {
        Console.WriteLine("Done stuff");
    }
}

Jeg har behov for at have en klasse, som står for at lave mit logging arbejde. Dette heder i DynamicProxy for en Interceptor:

public class LogInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation) {
        Console.WriteLine("Before");
        invocation.Proceed();
        Console.WriteLine("After");
    }
}

Denne metode bliver simpelthen kaldt, hver gang en metode i mit interface bliver kaldt. Du kan se at jeg bare skriver noget ud til console, men jeg kunne nemt have gjort alt muligt fancy.

Derefter tog jeg min Main metode og skrev flg.:

class Program
{
    static void Main(string[] args) {
        ProxyGenerator p = new ProxyGenerator();
        IDataProvider provider = p.CreateInterfaceProxyWithTarget<IDataProvider>
                                      (new DataProvider(), new LogInterceptor());

        provider.DoStuff();

        Console.ReadLine();
    }
}

Det jeg gør er simpelhen at lave en ny generator, og så bede den om dynamic at lave en proxy klasse der implementere mit interface, og som har min konkrete klasse som target. Jeg kobler også min nye LogInterceptor på, og vola - så er der proxy på min DataProvider:

Before
Done stuff
After

Det var altså alt hvad der skulle til, for at lave mig en proxy klasse. Har du allerede løst koblet objekter i dit projekt, så er det altså meget let at lave en proxy, som kan fuldføre ens arbejde som alle dine metoder skal udføre.

Mulighederne er mange, og jeg har bare vist jer jordens mest simple eksempel.

Kommentarer

  • Jakob Tikjøb andersen skrev den 20. februar:

    Hej Jesper,

    Super at du rent faktisk fik noget ud af min snak dengang :-)

    Dog forstår jeg ikke hvorfor du mener System.Web er en afhængighed, det er den ikke i DynamicProxy og jeg bruger ofte dynamicproxy uden reference til denne.

    En anden vigtig diskussion er at snakke om hvordan du hvis du håndterer sikkerhed håndterer initializering af objekter så man ikke ved en fejl kommer til at omgå den sikkerhed der ligger i proxyen.

  • Jesper Blad Jensen skrev den 21. februar:

    Hej Jakob,

    Det kan du have ret i. Jeg tror dog det er noget som man skal se på ligemeget hvilken sikkerhedsmetode man bruger i sin kode. Men selvfølgelig skal man sikre at alle kalder gennem proxien.

    Jeg undrede mig også over System.Web, men jeg kunne ikke få den til at compile, hvis jeg ikke havde den refferance. Det kan dog være en af de andre DLL filer som måske ikke er nødvendige.

    Men nu arbejder jeg primært i web, så jeg er sådan set ligeglad, men det er selvfølgelig tit et krav at man ikke bruger System.Web i normale applikationer - så det virkede også forkert at den var krævet.

    Så det er nok mig der har misforstået noget :)

  • Arne Vajhøj skrev den 6. marts:

    Jeg har heller ikke brug for den web ref.

    Hvis man selv har kontrol over build af det hele, så ville jeg foretrække at lave den slags compile time - med f.eks. AspectDNG AOP.

    Men til et framework der skal bruges af andre er runtime f.eks. Castle DynamicProxy mest praktisk.

Skriv en kommentar


Du kan bruge Markdown formatering