.NET Remoting with Service that uses DAAB with Unity give Exception

Jul 10, 2009 at 1:09 PM
Edited Jul 10, 2009 at 1:12 PM

Hi,

I have a project that is using Unity and I am trying to implement Remoting.  I have a Remoting Server for an existing Service Layer that has a Data Layer using the Data Access Application Block. The problem is that due to Remoting requiring a parameterless constructor when the TestService is called I am
getting a null reference exception. Any ideas on how to implement this with Unity?

Here is my code:


TestService from Service Layer:

public class TestService : MarshalByRefObject, ITestService
    {
        private ITestRepository repository;

        public TestService(ITestRepository repository)
        {
            this.repository = repository;        
        }

        public TestService() <--- parameterless constructor
        {}

        public List<Book> GetBooks()
        {
            return repository.GetBooks();
        }

TestRepository from Data Layer:

public class TestRepository : ITestRepository
    {
        private Database db;

        public TestRepository(Database db)
        {
            this.db = db;      
        }

        public List<Book> GetBooks()
        {
          List<Book> list = new List<Book>();
            list.Add(new Book(1, "test book 1"));
            list.Add(new Book(2, "test book 2"));
            return list;
        }     


Book class:

[Serializable]
    public class Book : MarshalByRefObject
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public Book() { }
        public Book(int id, string name)
        {
            this.ID = id;
            this.Name = name;
        }
    }


Simple test RemotingServer:

TestRepository testRepository;
TestService testService;

IUnityContainer container = new UnityContainer();

container.AddNewExtension<EnterpriseLibraryCoreExtension>();
container.AddNewExtension<DataAccessBlockExtension>();


container.RegisterType<ITestRepository, TestRepository>();

container.RegisterType<ITestService, TestService>();

testService = container.Resolve<TestService>();

TcpServerChannel channel = new TcpServerChannel(9988);
ChannelServices.RegisterChannel(channel);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(TestService), "TestService", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Press Any Key");
System.Console.ReadKey();

Simple Remoting Client:


ChannelServices.RegisterChannel(new TcpClientChannel());
            ITestService testService = (ITestService)Activator.GetObject(typeof(ITestService), "tcp://localhost:9988/TestService");
            
            foreach(Book item in testService.GetBooks())
            {
                Console.WriteLine("Book:\n" + item.Name);    
            }
            
            
            Console.ReadKey();

Unity Configuration:

TestRepository testRepository;
TestService testService;

IUnityContainer container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
container.AddNewExtension<DataAccessBlockExtension>();
container.RegisterType<ITestRepository, TestRepository>();
container.RegisterType<ITestService, TestService>();

I have also tried using setter injection through configuration and in the TestService I have a property:

container.Configure<InjectedMembers>().ConfigureInjectionFor<TestService>(new InjectionProperty("repository"));

The TestService is modified to contain a property: public ITestRepository repository { get; set; } and Constructor injection is removed in this case.

Here is the full exception:

line 40 is calling repository.GetBooks();

at Test.Services.IssueService.GetBooks() in TestService.cs:line 40 at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs) at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs) at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)

 

Thanks,

John

Jul 14, 2009 at 8:20 PM

I haven't touched remoting in years (literally) so I don't have a whole lot of insight to add. The fundamental issue appears to be that remoting is doing the object creation for you, which gives no opportunity to run things through the container. I honestly don't remember (and haven't used much of when I did it) if remoting has a way to register an explicit factory for service objects instead? If so, then you could have the factory get the service object from the container instead.

Another option would be to store the container in a global, and set up your dependencies as properties rather than constructor parameters. Then in your service constructor you could do something like:

GlobalContainer.BuildUp(this);

and get your dependencies injected that way. It's ugly, but sometimes that's the only way around the issue. I don't know enough about the ins and outs of remoting to know more.

Another option could be WCF - I know there's a behavior you can plug in to take over object creation there.

 

Jul 16, 2009 at 4:46 PM

Thanks for the suggestion, I'll give BuildUp a try.