I was involved in some debugging action this morning, regarding my XPO Publication Service. Please read this post (and others linked from there) to find out the basics about that service.
The problem in question was that a customer was receiving a weird Remoting exception when trying to connect to a data store published through the publication service. The first test I did was to change the dependencies in the service to 8.2, build it, and run my functional tests on it - all of those worked fine.
So I set out to create a simple client that would attach itself to a data store published through the Publication Service. At this point, I think a little excursion would be good. I have noticed that many people have the weirdest ideas of how to connect to a published service from XPO. Often they involve a line like this:
IDataStore dataStore = (IDataStore)Activator.GetObject(typeof(IDataStore),
@"http://tcp:2635/something.rem");
Now, this is not wrong, but it's also not necessary. Retrieving a reference from the Activator is one way of interfacing with the .NET Remoting infrastructure on the client side. But - that code is already contained in the XPO libraries, so all the gory details of using Remoting are hidden. You can easily use XPO to get a connection provider (= data store) created directly, like this:
XpoDefault.GetConnectionProvider("tcp://localhost:9999/something.rem",
AutoCreateOption.DatabaseAndSchema)
Or, even better, you can get a data layer set up directly, like this:
XpoDefault.DataLayer = XpoDefault.GetDataLayer("tcp://localhost:9999/something.rem",
AutoCreateOption.DatabaseAndSchema);
What this means is really that a connection URL for a data store published through Remoting can be used in very similar places to any other connection string. XPO will figure it out for you.
On a related note, guess how to set up an XAF application to connect to a data store publication? That's right, just use the URL as a connection string. For instance, put this into your app.config:
<add name="ConnectionString" connectionString="tcp://localhost:9999/something.rem" />
Right, end of excursion. Now, I'd created that little client app and, in contrast to all the lines above, I was using HTTP, not TCP, for the connection. The publications.xml file for the Publication Service had this Channels section:
<Channels>
<xpopub:HTTPTargetChannel>
<Port>9999</Port>
</xpopub:HTTPTargetChannel>
</Channels>
I was quite surprised to find this, but I got the same Remoting exception as described in the support center issue.
One idea I quickly had was that the problem might be related to that other weird issue I had encountered at some point when using HTTP for a publication. That issue is described in this post on my personal blog, and it's certainly not irrelevant if you use Remoting or some other protocol to publish things through HTTP. So go to this article and have a look if you're interested.
Anyway, that wasn't it, since the error messages were different. Nevertheless, I was able to solve the problem by switching to TCP. Probably a good idea anyway, unless you specifically need to use HTTP for firewall reasons (though it will forever remain a mystery to me why people don't have a problem tunneling unknown content through HTTP, while being scared $$(*&less by the same data on a different port). So I changed my publications.xml to this:
<Channels>
<xpopub:TCPTargetChannel>
<Port>9999</Port>
</xpopub:TCPTargetChannel>
</Channels>
I changed all the URLs I was using to tcp://, as shown in the code lines above, and then it worked. Great. A quick test with the more complex XAF Main Demo application showed that this worked as well. Fine.
So, back to the "why"... why is everything fine with TCP, but not with HTTP? Well, the answer came to me after some discussion with our support guy Dennis: the serialization formats don't match. Yeah. Let me say that again - the serialization formats don't match. Why doesn't Remoting throw a TheSerializationFormatsDontMatchException? I don't know. Anyway, that's what's happening.
The XPO Publication Service defaults to binary encoding, even for the HTTP channel. There's a property called UseBinaryEncoding on the channel, which can be changed through the config file, but it defaults to true. The XPO library, on the other hand, defaults to SOAP encoding when an HTTP channel is used. So, back to the publications.xml with the HTTPTargetChannel, and then insert this line before the code that establishes the connection to the server:
ChannelServices.RegisterChannel(new HttpChannel(null,
new BinaryClientFormatterSinkProvider(), null), false);
Credit for this line goes to Dennis - he had found out that this makes things work, though he wasn't exactly sure why. Here's why: because the HTTP Channel is configured to use binary encoding and thereby becomes compatible with the way the Publication Service works. The XPO library doesn't register the HTTP Channel again, since it's already there, and it doesn't change the registration either. Fine.
Now, several questions come up here. First, why are the Publication Service and the XPO defaults incompatible? I don't know. It's certainly weird and I suspect we changed something at some point, on one or both ends. It's been a long time since I wrote the Publication Service and being a pet project, it's perhaps not tested well enough - still, since we're talking about default settings here, it seems that this could have been found earlier if it had been like that forever. I don't know.
Second, is it possible to use SOAP encoding? Yes and no, oddly enough - another point where I'm not sure where a problem comes from. It is easy to switch the Publication Service to using SOAP, like this:
<Channels>
<xpopub:HTTPTargetChannel>
<Port>9999</Port>
<UseBinaryEncoding>false</UseBinaryEncoding>
</xpopub:HTTPTargetChannel>
</Channels>
The problem is, when I tried this, I got another weird exception coming up, saying that the SOAP formatter isn't able to deal with generic classes being serialized. Okay then... hm. The point where we changed XPO to have generic classes involved in the inheritance hierarchy is a really long time ago, so it's probably been like that since then. Interestingly, while I don't know how many people use Remoting with HTTP/SOAP, I'm pretty sure there are some people using XML Web Services, which use HTTP/SOAP as well. The answer to that is that the SOAP formatter for Remoting isn't the same as the one for XML Web Services. And they are both different from the one being used for WCF, if I'm not completely mistaken. Trust Microsoft to find ways of making complex things more confusing.
Summary of all the odd stuff above
If you've stopped reading my ramblings a while ago, here's the summary of important points:
- The reason for the issue is that the XPO Publication Service and the Remoting client code in the XPO libraries use different formatter defaults for the HTTP channel. We should look into making some changes so this would be compatible automatically.
- Using TCP instead of HTTP makes things work. This is probably the best idea for many cases, unless HTTP is absolutely required.
- Using binary formatting with HTTP also makes things work. An additional line of code is required for this purpose, but inserting this into application initialization code shouldn't be a problem in most cases.
- Using SOAP formatting with the Publication Service is easy, but it doesn't actually work for technical reasons in the Remoting SOAP formatter. We might look into this in a bit more detail to see if there's anything we can do about this.
- Using the HTTP/SOAP combination is perfectly possible with XML Web Services as well as WCF, to my knowledge. XML Web Services are not self-hosting, so they can't really be supported by the XPO Publication Service. WCF can be self-hosting and it would be possible to extend the Publication Service to support it directly. The Publication Service can be extended externally, i.e. without changes to its own code base. With or without support in the Publication Service, there are plenty of articles on this blog that show how to tunnel through Web Services and WCF.
Update:
I've talked to the XPO team, and they have tracked down the problem with the SOAP formatter. Turns out this should actually work - so we regard it a bug and it will be fixed. Hang on... just now an IM tells me that the fix has been checked in internally. Will be in the next minor release.
The thing with the default behavior of the XPO Remoting code is that it uses defaults the .NET Framework provides, which is a good thing of course. I guess it would make sense to change the default for the Publication Service to use SOAP instead of binary... I'll probably do that soon.
Free DevExpress Products – Get Your Copy Today
The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the
DevExpress Support Center at your convenience. We’ll be happy to follow-up.