About ContextObjects, Attributes, ServerSinks, Activation, Proxies,...

Hi!

I would like to decorate types with a [Mockable] attribute, such that, when
some switch is on, instances of these classes are 'replaced' by mock
objects.

When the switch is on, the real object *must not* be instantiated, a mock
should be instantiated instead. So I can not use 'regular' proxying
techniques that create a proxy around an existing object, as the real
object should never exist.

Additionally, it is essential that this feature works, using the regular
new operator.

I managed to dynamically create a subtype of a type, and have it's
constructor not call the base constructor by using Reflection.Emit. An
object of such a subtype is what I want to use as Mock.

The problem I was however not able to solve, is that of intercepting the
constructor and returning an instance of the subtype instead of one of the
real type.

I tried a ContextAttribute and ServerSink to intercept the constructor,
which works fine, but whatever I attempt I do in breaking the sink chain
and returning my subtype instance, it always results in an error.

With the following code, I get a RemoteException saying:

  An unhandled exception of
type 'System.Runtime.Remoting.RemotingException' occurred in mscorlib.dll
  Additional information: Inconsistent state during activation; there may
be two proxies for the same object.

Why would there be two proxies ? How could I solve this ?

Enclosed some code that does not really reflect the context of mocking (I
shortened the code to limit as much as possible to the issue) but clearly
shows the problem.

I choosed to post the complete code so you could run it and see what's
going on. Just copy the code into a ConsoleApplication project.

Thanks for any help !!!

===========================================================================
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace ObjRefIssue {

 class MyEntryClass {
  [STAThread]
  static void Main(string[] args) {

   MyFoo foo = null;

   // This piece of code performs completely normally:
   foo = new MyFoo(12);
   foo.WriteBar();

   // Enable proxying:
   MyUseProxyController.Enabled = true;

   // The constructor here now fails:
   foo = new MyFoo(12);
   foo.WriteBar();

   Console.WriteLine("Done.");
   Console.ReadLine();
  }
 }


 // This is a type I create an instance of to work with:
 [MyUseProxy(typeof(MyFooProxy))]
 class MyFoo : ContextBoundObject {

  protected int bar;

  public MyFoo(int bar) {
   Console.WriteLine("Foo constructor called.");
   this.bar = bar;
  }

  public virtual void WriteBar() {
   Console.WriteLine("Bar is : {0}", bar);
  }
 }


 // When MyUseProxyController.Enabled is true, creating a
 // MyFoo should result in a MyFooProxy (which inherits from MyFoo
 // and thus is type-compatible):
 class MyFooProxy : MyFoo {

  public MyFooProxy() : base(0) {
   // Proxy type requires default constructor.
  }

  public override void WriteBar() {
   Console.WriteLine("ProxyBar is : {0}", bar);
  }
 }


 // Merely a static flag MyUseProxyController.Enabled:
 public class MyUseProxyController {
  public static bool Enabled = false;
 }

 [AttributeUsage(AttributeTargets.Class, AllowMultiple=false,
Inherited=false)]
 public class MyUseProxyAttribute : ContextAttribute {

  private Type proxyType;

  public MyUseProxyAttribute(Type proxyType) : base
("UseProxyAttribute") {
   this.proxyType = proxyType;
  }

  public override bool IsContextOK(Context ctx,
IConstructionCallMessage ctorMsg) {
   // If ProxyController enabled, reject creating
context to build sinks:
   return !MyUseProxyController.Enabled;
  }

  public override void GetPropertiesForNewContext
(IConstructionCallMessage ctorMsg) {
   base.GetPropertiesForNewContext(ctorMsg);
   ctorMsg.ContextProperties.Add(new
MyUseProxyServerSinkProvider());
  }

  // Called by MyUseProxyServerSink to construct a proxy
instance instead of the
  // regular instance:
  public MarshalByRefObject GetProxyInstance() {
   return (MarshalByRefObject)proxyType.GetConstructor
(new Type[]{}).Invoke(new object[]{});
  }
 }


 // Nothing special here, simply return a MyUseProxyServerSink
server sink:
 public class MyUseProxyServerSinkProvider : IContextProperty,
IContributeServerContextSink {

  private Context context;

  public string Name {
   get { return this.GetType().Name; }
  }

  public bool IsNewContextOK(Context newCtx) {
   return true; // Accept new context
  }

  public void Freeze(Context newContext) {
   this.context = newContext;
  }

  public IMessageSink GetServerContextSink(IMessageSink
nextSink) {
   return new MyUseProxyServerSink(context, nextSink);
  }
 }


 // MyUseProxyServerSink, here's the stuff !!!
 internal class MyUseProxyServerSink : IMessageSink {

  private Context context;
  private IMessageSink nextSink;

  internal MyUseProxyServerSink(Context context, IMessageSink
nextSink) {
   this.context = context;
   this.nextSink = nextSink;
  }

  public IMessageSink NextSink {
   get { return nextSink; }
  }

  public IMessage SyncProcessMessage(IMessage msg) {

   // If a constructor is called:
   if (((IMethodMessage)msg).MethodBase.IsConstructor)
{
    Console.WriteLine("> MyUseProxyAttribute
intercepted the constructor");

    // Retrieve the requested type:
    Type requestedType = Type.GetType
(((IMethodMessage)msg).TypeName);

    // Create an instance of proxy type instead:
    MarshalByRefObject inst =
((MyUseProxyAttribute)requestedType.GetCustomAttributes(typeof
(MyUseProxyAttribute), false)[0]).GetProxyInstance();

    // Marshal it:
    RemotingServices.Marshal(inst);

    // Get an ObjRef to it, as
IConstructionReturnMessage requires an ObjRef:
    ObjRef constructedRef =
RemotingServices.GetObjRefForProxy(inst);

    // And return a IConstructionReturnMessage
returning the ObjRef to the proxy:
    return new MyConstructionReturnMessage
((IConstructionCallMessage)msg, constructedRef);

   } else {
    // For non-constructors, simply process the
message on a regular manner:
    return nextSink.SyncProcessMessage(msg);
   }
  }

  public IMessageCtrl AsyncProcessMessage(IMessage msg,
IMessageSink replySink) {
   // For async messages, simply process the message
on a regular manner:
   return nextSink.AsyncProcessMessage(msg, replySink);
  }
 }


 // My own IConstructionReturnMessage implementation, as there is no
 // public implementation available with a suitable constructor:
 public class MyConstructionReturnMessage :
IConstructionReturnMessage {

  private IConstructionCallMessage callMessage;
  private IDictionary properties;
  private object returnValue;

  public MyConstructionReturnMessage(IConstructionCallMessage
msg, object returnValue) {
   this.callMessage = msg;
   this.returnValue = returnValue;
   this.properties = new Hashtable();
   // Copy properties:
   foreach(object key in msg.Properties.Keys) {
    this.properties.Add(key, msg.Properties
[key]);
   }
  }

  public object ReturnValue {
   get {return returnValue;}
  }

  public int OutArgCount {
   get {return 0;}
  }

  public string GetOutArgName(int index) {
   return null;
  }

  public object[] OutArgs {
   get {return new Object[0];}
  }

  public object GetOutArg(int argNum) {
   return null;
  }

  public Exception Exception {
   get {return null;}
  }

  public object[] Args {
   get {return callMessage.Args;}
  }

  public object GetArg(int argNum) {
   return callMessage.GetArg(argNum);
  }

  public string Uri {
   get {return callMessage.Uri;}
  }

  public System.Reflection.MethodBase MethodBase {
   get {return callMessage.MethodBase;}
  }

  public string MethodName {
   get {return callMessage.MethodName;}
  }

  public bool HasVarArgs {
   get {return callMessage.HasVarArgs;}
  }

  public LogicalCallContext LogicalCallContext {
   get {return callMessage.LogicalCallContext;}
  }

  public int ArgCount {
   get {return callMessage.ArgCount;}
  }

  public string TypeName {
   get {return callMessage.TypeName;}
  }

  public string GetArgName(int index) {
   return callMessage.GetArgName(index);
  }

  public object MethodSignature {
   get {return callMessage.MethodSignature;}
  }

  public System.Collections.IDictionary Properties {
   get {return properties;}
  }
 }
}
===========================================================================

===================================
This list is hosted by DevelopMentorĀ®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to