Problem with HTTP BinaryFormatter and KeyedCollection<K,V>
Hello,
I have noticed a really weird problem regarding remoting and the
KeyedCollection class.
I am noticing that the keyed collection is returned to the client but
isn't fully initialized.
For example the .Items readonly property is null any any method that
attempts to use it
fails (example .Count)
It comes down to where if the version of the assembly the collection
is defined in isn't the same on
both ends, instead of throwing a serialization error, you get a
collection that is half initialized
and will throw null excpetions if you attempt to use it.
Testcase:
- ReBuild RemotingServer
- Copy output directory to another location and run it from there
- ReBuild RemotingClient (will rebuild RemotingLib with another
version)
- Run RemotingClient and notice that:
string[] names = new string[Count];
throws a null exception on accessing Count. This is unexpected as I
am expecting
ether a fully constructed object or a serialization exception.
If you set the version of RemotingLib to 1.0.0.0 and do the above
steps it will work fine. Also
if you derive PersonCollection from
System.Collections.ObjectModel.KeyedCollection<K,V> it will
also work fine. It is only once you have it inherit from our own
KeyedCollection.
Thanks,
Gord
Example code:
RemotingLib (Class Library)
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]
using System;
using System.Collections.Generic;
using System.Text;
namespace RemotingLib
{
[Serializable]
public abstract class KeyedCollection<TKey, TItem> :
System.Collections.ObjectModel.KeyedCollection<TKey, TItem>
{
}
[Serializable]
public class Person
{
private string m_id;
private string m_name;
public string ID
{
get { return m_id; }
set { m_id = value; }
}
public string Name
{
get { return m_name; }
set { m_name = value; }
}
public Person(string id, string name)
{
ID = id;
Name = name;
}
}
[Serializable]
public class PersonCollection : RemotingLib.KeyedCollection<string,
Person>
{
protected override string GetKeyForItem(Person item)
{
return item.ID;
}
public string[] GetAllNames()
{
string[] names = new string[Count];
for (int x = 0; x < Items.Count; x++)
{
names[x] = Items[x].Name;
}
return names;
}
}
public class Service : MarshalByRefObject
{
public PersonCollection GetPeople()
{
PersonCollection people = new PersonCollection();
people.Add(new Person("1","Alice"));
people.Add(new Person("2", "Bob"));
return people;
}
}
}
RemotingClient (Console App)
Project reference to RemotingLib
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]
namespace RemotingClient
{
class Program
{
static void Main(string[] args)
{
HttpClientChannel channel = new HttpClientChannel("httpbin", new
BinaryClientFormatterSinkProvider());
ChannelServices.RegisterChannel(channel, false);
RemotingLib.Service serv =
(RemotingLib.Service)Activator.GetObject(
typeof(RemotingLib.Service),
"http://localhost:8080/Service.rem");
if (serv == null)
{
Console.WriteLine("Error, unable to locate Server");
}
else
{
RemotingLib.PersonCollection people = serv.GetPeople();
Console.WriteLine(people.GetAllNames().Length);
}
Console.ReadLine();
}
}
}
RemotingServer (Console App)
Project reference to RemotingLib
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]
namespace RemotingTest
{
class Program
{
static void Main(string[] args)
{
Hashtable settings = new Hashtable();
settings.Add("name", "httpbin");
settings.Add("port", "8080");
HttpChannel channel = new HttpChannel(settings, new
BinaryClientFormatterSinkProvider(), new
BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel,false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingLib.Service),
"Service.rem",
WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Press enter to stop server");
System.Console.ReadLine();
}
}
}
Date:Wed, 22 Aug 2007 16:48:34 -0000
Author:
|