Can't register remoted instances

Jul 15, 2009 at 1:37 PM

At the startup of my application I create an instance of a remoted object and try to register it using RegisterInstance<T>, but Unity won't let me.  Is there a good reason for this?  I think that part of not caring which object implements my service interface should be not caring if it is local or remote.  At the moment I have had to write a proxy class which passes calls onto the remoted instance, but I don't like having to do this.

So, why is this restriction in place, and is there any chance of having it removed?

 

 

Jul 15, 2009 at 6:20 PM

What do you mean "won't let you"? Are you getting an exception? What is it?

 

Jul 15, 2009 at 11:08 PM

Sorry for the vagueness of my post.  I had seen it discussed before so I thought it was a well known issue...

 

 

static void RegisterApplicationServices(IUnityContainer container)
{
var channel = new TcpChannel();
ChannelServices.RegisterChannel(channel, true);
var application = (IMyAppApplicationServer)Activator.GetObject(typeof(IMyAppApplicationServer), Properties.Settings.Default.ApplicationServerUrl);
container.RegisterInstance<IMyAppApplicationServer>(application, new ContainerControlledLifetimeManager());
}

static void RegisterApplicationServices(IUnityContainer container)
{
var channel = new TcpChannel();
ChannelServices.RegisterChannel(channel, true);
var application = (IMyAppApplicationServer)Activator.GetObject(typeof(IMyAppApplicationServer), Properties.Settings.Default.ApplicationServerUrl);
container.RegisterInstance<IMyAppApplicationServer>(application, new ContainerControlledLifetimeManager());
}

 

 

 

System.ArgumentException was unhandled
  Message="The type System.MarshalByRefObject cannot be assigned to variables of type MyApp.Server.SharedTypes.IMyApplicationServer.\r\nParameter name: instance"

  Source="Microsoft.Practices.Unity"
  ParamName="instance"
  StackTrace:
       at Microsoft.Practices.Unity.Utility.Guard.TypeIsAssignable(Type assignmentTargetType, Type assignmentValueType, String argumentName)
       at Microsoft.Practices.Unity.UnityContainer.RegisterInstance(Type t, String name, Object instance, LifetimeManager lifetime)
       at Microsoft.Practices.Unity.UnityContainerBase.RegisterInstance[TInterface](TInterface instance, LifetimeManager lifetimeManager)
       at MyApp.ServiceBootStrapper.RegisterApplicationServices(IUnityContainer container) 
       at MyApp.ServiceBootStrapper.Initialize(IUnityContainer container) 
       at MyApp.App..ctor() in
       at MyApp.App.Main() in
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

 

 

 

 

Jul 20, 2009 at 3:55 PM

Any ideas for this?  Having to write a passthrough for every remoted class is a pain.

Jul 20, 2009 at 11:59 PM

Hi.

When you ask the Activator for an instance of the interface, the remoting TransparentProxy infrastructure cannot create any other concrete object other than MarshalByRefObject, although it does implement the interface.  The weirdness that is remoting means that aspects of the reflection and type system in .NET cannot interrogate the object definition cleanly ( look at the application instance in the debugger and you see what I mean )

There is an easy answer though.  You need to write a cusom lifetime manager to handle the Remoting Activation and use RegisterType instead of RegisterInstance.  I have attached a sample nUnit TestFixture that "works on my machine!".

Hope this helps

Cheers...

Robert

 

    [TestFixture]
    public class RemotingTest
    {
        [Test]
        public void RegisterRemotingInstance()
        {
            TcpChannel channel = new TcpChannel(14000);

            ChannelServices.RegisterChannel( channel, true );

            WellKnownServiceTypeEntry serviceTypeEntry = new WellKnownServiceTypeEntry( typeof(HelloWorldServer), "HelloWorld", WellKnownObjectMode.Singleton );
            RemotingConfiguration.ApplicationName = "HelloWorld";
            RemotingConfiguration.RegisterWellKnownServiceType( serviceTypeEntry );

            IUnityContainer container = new UnityContainer()
                .RegisterType<IHelloWorld, IHelloWorld>(new RemotingLifetimeManager<IHelloWorld>("tcp://localhost:14000/HelloWorld"));

            Assert.AreEqual( "Hello world", container.Resolve< IHelloWorld >().SayHello() );

            ChannelServices.UnregisterChannel( channel );
        }
    }

    public class HelloWorldServer : MarshalByRefObject, IHelloWorld
    {
        #region IHelloWorld Members

        public string SayHello()
        {
            return "Hello world";
        }

        #endregion
    }

    public interface IHelloWorld
    {
        string SayHello();
    }

    public class RemotingLifetimeManager<I> : LifetimeManager
    {
        private string uri;

        public RemotingLifetimeManager( string uri )
        {
            this.uri = uri;
        }

        public override object GetValue()
        {
            return (I)Activator.GetObject( typeof (I), this.uri );
        }

        public override void RemoveValue()
        {
            throw new NotImplementedException();
        }

        public override void SetValue(object newValue)
        {
            throw new NotImplementedException();
        }
    }

 

Jul 21, 2009 at 12:18 AM

Well, the real answer is to fix that assert, but I kind of like the custom lifetime manager idea. Although I might put it in a custom build plan instead (like the static factory extension does); that way you can actually use the container lifetime management on your proxies, instead of getting a new one every time like this implementation will do.

 

Jul 21, 2009 at 12:34 AM

yes, a custom build plan would be cleaner, but more complicated solution and I usually tend towards simplicity.

You could always create a ContainerControlledRemotingLifetimeManager ( let's see how big we can make the names ;) to cache the proxy, but I always create new instances of proxies ( both Remoting and WCF ) everytime I want to use it.  The overhead of the actual remote call far outweighs the overhead of creating a proxy and you get a safer experience.

I'm not sure that you can fix the assert, unless you actually cast one to the other and catch the exception.

 

Jul 21, 2009 at 9:43 PM

Thanks!

I made my service a simple request/response instead so no need to modify each time I add new functionality.  I think Unity needs to be fixed to allow remoted implementors too though.

Jun 15, 2010 at 5:19 PM

we just ran into this same issue trying to register instances of remotable interfaces. would be nice to see this fixed in a future version of Unity

Mark

 

Jun 16, 2010 at 12:14 AM

I thought we DID fix this in Unity 2.0. If not, could you please make sure we've got a bug in the issue tracker?