Search This Blog

Friday, March 12, 2010

Implementing Dynamic Proxy in WCF

This article is based on  Vipul Modi's DynamicProxyFactory , i have made a little modification to the implementation in the code,


If you download & view the sample from here site you'll find out that the method InvokeComplexCalculator has an object of DynamicComplexNumber class created inside.


Consider a scenario where the class object is being passed as a parameter to the InvokeComplexCalculater function and the wsdl url & the service contracts are also dynamic i.e we call different services dynamically which have different service contracts also we have different class objects.


i.e.


as per the sample 



private static void InvokeComplexCalculator(DynamicProxyFactory factory)
        {
            // create the DynamicProxy for the contract IComplexCalculator and perform
            // operations
            Console.WriteLine("Creating DynamicProxy to ComplexCalculator Service");
            DynamicProxy complexCalculatorProxy = factory.CreateProxy("IComplexCalculator");

            // Call the Add service operation.
            DynamicComplexNumber value1 = new DynamicComplexNumber(factory.ProxyAssembly);
            value1.Real = 1; value1.Imaginary = 2;
.....

}

what if we had the DynamicComplexNumber class object being passed as an input parameter

somewhat like this


private static void InvokeComplexCalculator(DynamicComplexNumber dynamicComplexNumber, string serviceWsdlUri, string serviceContractName)
        {

DynamicProxyFactory factory = new DynamicProxyFactory(serviceWsdlUri);
DynamicProxy complexCalculatorProxy = factory.CreateProxy(serviceContractName);
...

}


if we had such a requirement we would have to hardcode the class types which we need to pass as an input parameter, also consider a class object being sent back as a response

for e.g.


    class DynamicComplexNumber : DynamicObject
    {
        const string TypeName = "WcfSamples.DynamicProxy.Example.ComplexNumber";
        const string ImaginaryPropertyName = "Imaginary";
        const string RealPropertyName= "Real";
      
        public static Type GetType(Assembly assembly)
        {
            return assembly.GetType(TypeName, true, true);
        }

        public DynamicComplexNumber(Assembly assembly)
            : this(GetType(assembly))
        {
        }

        public DynamicComplexNumber(Type employeeType)
            : base(employeeType)
        {
            CallConstructor();
        }

        public DynamicComplexNumber(object complexNumber)
            : base(complexNumber)
        {
        }

        public double Imaginary
        {
            get
            {
                return (double)GetProperty(ImaginaryPropertyName);
            }

            set
            {
                SetProperty(ImaginaryPropertyName, value);
            }
        }

        public double Real
        {
            get
            {
                return (double)GetProperty(RealPropertyName);
            }

            set
            {
                SetProperty(RealPropertyName, value);
            }
        }
    }


here we need to re-define all the properties we require

so i came up with a DynamicProxyClass which would inherit from the DynamicObject below is the implementation of this class


    public class DynamicProxyClass : DynamicObject
    {
        public static Type GetType(Assembly assembly, string TypeName)
        {
            return assembly.GetType(TypeName, true, true);
        }

        public DynamicProxyClass(Assembly assembly, string TypeName)
            : this(GetType(assembly, TypeName))
        {
        }

        public DynamicProxyClass(Type employeeType)
            : base(employeeType)
        {
            CallConstructor();
        }

        public DynamicProxyClass(object complexNumber)
            : base(complexNumber)
        {
        }

        public void SetObject(string propertyName, object value)
        {
            base.SetProperty(propertyName, value);
        }

        public object GetObject(string propertyName)
        {
            return base.GetProperty(propertyName);
        }
    }

and now we can have methods defined as below


static void Main(string[] args)
        {

            Request request = new Request();
            request.RequestId = "SomeId";
            Response response = DoSomethingDynamically(request);
            ......


        }


        public static void DoSomethingDynamically(Request request)
        {
            Response response = new Response();
            DynamicProxy proxy = null;
            try
            {
                DynamicProxyFactory factory = new DynamicProxyFactory("http://localhost:8753/SampleService?wsdl");

                proxy = factory.CreateProxy("ISampleService");

                DynamicProxyClass requestProxy = new DynamicProxyClass(factory.ProxyAssembly, request.GetType().ToString());

                    Type requestType = request.GetType();

                    // Set the values from the Request Class to the proxy class
                    foreach (PropertyInfo propertyInfo in requestType.GetProperties())
                    {
                        requestProxy.SetObject(propertyInfo.Name.ToString(), propertyInfo.GetValue(request, null));
                    }
              

                object[] requestObject = new object[]{ requestProxy.ObjectInstance };

                object someResponse =  proxy.CallMethodByName("SomeMethod", requestObject);

                DynamicProxyClass responseProxy = new DynamicProxyClass(someResponse);
// Get the Type of the Proxy Response Class so that we get all the properties
                    // & their values dynamically
                    Type responseProxyType = responseProxy.ObjectInstance.GetType();

                    // Get the type of the response object which we would be sending back
                    // This is done to populate the values into the response object
                    Type responseType = response.GetType();

                    // Set the values from the Dynamic Reponse Class to the Response Class which
                    // we want to send back as a return value
                    foreach (PropertyInfo propertyInfo in responseProxyType.GetProperties())
                    {
                        // Skip the extra properties which are generated dynamically by the Dynamic Proxy Object
                        if (responseType.GetProperty(propertyInfo.Name) != null)
                        {
                            responseType.GetProperty(propertyInfo.Name).SetValue(response,
                                propertyInfo.GetValue(responseProxy.ObjectInstance, null), null);
                        }
                    }
                .............
                }


and below we have our request & response classes defined the normal WCF way


    [DataContract]
    public class Request
    {
        [DataMember]
        public string RequestId { get; set; }
    }


    [DataContract]
    public class AnotherClass : Response
    {
        [DataMember]
        public string SomeOtherProperty { get; set; }
    }

just added a collection object to test the calls are working as expected


    [DataContract]
    public class SampleClass : Response
    {
        [DataMember]
        public string SomeProperty { get; set; }

        [DataMember]
        public List AnotherClass { get; set; }
    }


    [DataContract]
    [KnownType(typeof(AnotherClass))]
    [KnownType(typeof(SampleClass))]
    public class Response
    {
        [DataMember]
        public string SomeResponseString { get; set; }
    }


So this sorted out things for me easily, just thought of sharing my experience with others.





1 comments:

Sai said...

Cool one. The only trouble I see is the memory trouble associated with dynamic objects. MS itself says Reflection can be a overhead in normal practices. However, the possibilities of this is awesome. Also, I will love this, if it can be modified by using generics rather than hardcoding assembly names(someway or the other, assembly names/types are getting hardcoded here), which in turn makes it more type safe.