views:

626

answers:

5

We want to have two .NET apps running on the same machine communicate with each other. We want three projects. A library containing interfaces. A "server" app that implements the interfaces and "client" app that communicates with the server using the interfaces. We do not want the client to reference the server.
We have a test app that does this with remoting but recently discovered that remoting is being replaced with WCF. Since this is new development we feel we should use WCF but we haven't managed to make it work with WCF and were wondering if it is possible?


EDIT:

Sorry for my lack of detail above but I didn't have access to any code at the time. I am working with two other people on this project and haven't looked closely at their IPC stuff. I know that they are currently of the opinion that WCF cannot do what they want it to do and I am hoping to be able to show them that it can.

I've started looking at my coworkers attempts at this and will continue to update this post as I understand it.

Below is a simplified version of their test code for remoting. They want to be able to do this using WCF but so far have been unable to get it to work.

Both the ServiceApp and ClientApp assemblies reference the Lib assembly. They do not reference each other.

I will edit this again when I am better able to explain their attempt to replicate this behavior in WCF. At this point all I really know is that they are using NetNamedPipeBinding and are having trouble with the client app complaining that it cannot access the service assembly.


EDIT:

Below is a simplified version of the test code for WCF.

The following line from ClientProgram throws an exception:

IMessage msg2 = service.CreateMessage("Hello World");

Here is the exception:

Could not load file or assembly 'ServiceApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.


EDIT:

In trying to understand Steven's latest comment I googled IMetadataExchange which of course had as its first hit the MSDN Page. This page said:

There is no need to implement the MetadataReference contract in your service implementation. Instead, add the ServiceMetadataBehavior to the service description.

Or, when using configuration, set the contract attribute of the endpoint element to IMetadataExchange. For an example, see How to: Publish Metadata for a Service Using a Configuration File.

For details on publishing metadata in WCF see Publishing Metadata.

Because, for this example, I am not using configuration files but instead I choose to go to the Publishing Metadata link. From there I went to the How to: Publish Metadata for a Service Using Code link which had a nice example that I used to modify my service app code. The added code is on lines 15-20.

I was able to add the Service Reference after a little work. The service has to be running but if you run the service through Visual Studio then you don’t have the option to add the service reference. Also, I still don't understand how the "Discover Services in Solution" option is supposed to work. In the end I was able to add the reference by opening by bin/debug folder to manually run the service and then entering the URL I added to my service app. If this is the correct way it rather awkward.

After all of that the code generated doesn’t make any since to me. First off it recreates my IService interface but turns all my IMessages into objects and decorates it slightly differently. If their interface is used then not only will it be using a different IService then my service actually implements but the methods in this IService don't even have the same signatures!

Putting that aside I assumed I would have to change my client to use the objects from the newly generated object so I changed my client code.

Now when I attempt to run it I get the following error on line 4:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:msg. The InnerException message was 'XML 'Element' 'http://tempuri.org/:msg' does not contain expected attribute 'http://schemas.microsoft.com/2003/10/Serialization/:Type'. The deserializer has no knowledge of which type to deserialize. Check that the type being serialized has the same contract as the type being deserialized.'.  Please see InnerException for more details.


EDIT:

One day left for the bounty and no answers since I added the bounty. Is there anything I can clarify before the bounty time is up?


EDIT:

In the end we went with remoting because it doesn't look as if what we want to do is possible with WCF. Steven Sudit gets the bounty for all his help despite the fact that it was all before I offered the bounty.

+4  A: 

Used both.

Definitely go with WCF.

Remoting is badly documented and touchy. WCF is so much easier by comparison. Not sure the issues you're having but there's tons of docs on WCF out there.

Will
+2  A: 

Remoting has some use cases, such as communication among appdomains within a single process. Having said that, yes, WCF is the way to go. From some of the things you've said, though, I'm not sure if you understand how this is supposed to work.

The usual way to do things in WCF is to create a shared assembly consisting entirely of data transfer objects (all properties, no code) and interfaces. Both sides reference this assembly, but the client uses a service reference of the server. Does that help?

Steven Sudit
WCF can bridge AppDomains as well. Refer to the In-Proc Hosting example in the downloads section of Juval Lowy's website, http://www.idesign.net.
Matt Davis
Will check it out as soon as I get the email with the link.
drs9222
WCF can do lots of things, but consider the case where you're loading an assembly into an AppDomain just for isolation, and there's no WCF support in it.
Steven Sudit
@Matt I finally got the download link and checked out the example. However, there wasn't anything in it I haven't already seen.
drs9222
@Steven I just realized that I never responded to your actual answer after I made my edits. We do have a shared assembly which contains the interfaces but no objects. I haven't been able to add a service reference. Visual studio insists that there are no services in the solution.
drs9222
@drs9222: To add a service reference, that service has to be running and it has to have a channel that exposes the metadata. In other words, it has to implement IMetadataExchange. The alternative, which requires a little coding but is quite viable, would be to specialize ChannelFactory<T> to create a channel that implements T (where T is the interface), allowing you to call through it. You should be able to find examples of all of this online.
Steven Sudit
@Steven See my latest edit
drs9222
drs9222, I'm not sure how much more I can help here because you're doing things very differently from the ways I've tried. I think you stumbled somewhere in creating your service reference. While the "Discover Services in Solution" item should be expected not to work, because you're not hosting services under IIS, you should be enabling "Reuse types in referenced assemblies" so that it doesn't generate new data transfer objects from the metadata and instead reuses the ones in the shared interface/dto assembly. I wound up using config files and not using service references.
Steven Sudit
That's okay. I appreciate all of the help you've given me. FYI I just double checked and I did enable "Reuse types in referenced assemblies"
drs9222
+18  A: 

WCF is the way to go, specifically check out the Net Named Pipe binding with WCF. This will allow very fast interprocess communication on the same machine.

If your targeting Windows Server 2008 for your production deployment then you can leverage IIS 7 as a hosting environment. Check out Extend Your WCF Services Beyond HTTP With WAS.

There is lots of WCF getting started type information out there. Check out:

If your having problems getting started, then post any specific errors, or examples, you might have demonstrating it and I'm sure someone here can help!

Zach Bonham
+1 for reference to Net Named Pipe binding.
Matt Davis
How does Net Named Pipe binding compare with TCP/IP for IPC? If I remember correctly, TCP/IP winds up using shared memory when connecting to localhost.
Steven Sudit
@StevenSudit Not sure how net.tcp stacks up against net.pipe, I've not done any formal testing myself. I imagine they might be comparable, but without explicit knowledge/experience, I'd lean towards net.pipe.I'd definitely be interested if anyone has experience there though I'd probably stick with net.pipe as the implied intent is clearer (to me). We did do some informal testing between remoting and net.pipe, the results of which are here: http://stackoverflow.com/questions/1295353/why-am-i-seeing-slower-performance-here-from-wcf-than-remoting/1297762#1297762
Zach Bonham
@Zach: Named pipes have some performance problems when not used on the same machine, whereas TCP/IP seems to work well in both cases.
Steven Sudit
From the reading I've done lately I thought that Microsoft chose to disallow the use of named pipes in WCF when they are not used on the same machine.
drs9222
@dres9222: Given the performance issue, that makes perfect sense. I guess an advantage of TCP/IP is that you can later choose to separate the client and server onto their own machines, most likely just by changing the config file.
Steven Sudit
Definitely WCF. NetNamedPipes locally and NetTcp if you change your architecture to use more than one box on your intranet.
Jerry Bullard
+2  A: 

I just switched a program over from remoting to WCF and I have come across a bunch of things that used to work seamlessly in remoting but that causes problems in wcf:

  • The CallContext class is gone. I used that for passing some information about e.g. the currently selected language and some info about the user. With WCF I had to create a MessageInspector to add headers with this info to every request.
  • The exceptions are no longer passed through unchanged. Instead you need to use the FaultException and FaultContracts.
  • The proxies don't seem to be as light weight. I need to dispose them instead of just leaving them to be garbage collected or else my service will hang. (Actually it isn't even as simple as calling Dispose to correctly terminate a proxy you need a fairly complex sequence of try, catch, finally that calls Close and Abort for different types of exceptions.) The need to close the proxies causes a bit of a problem where I want my IoC framework to manage the proxies.

I am not saying you should use Remoting, I'm just saying that there are reasons to consider Remoting as well. It is definitely the simpler one to set up and get to work.

I guess you could argue that I ran into some of these problems only because I wasn't using best practices in my Remoting program, though. :-P

Johan Levin
WCF is definitely more complicated.
Steven Sudit
+1  A: 

Here one pattern to do Remoting-like things with WCF:

  • 3 projects: server, client, shared.dll
  • server and client both reference shared.dll
  • put the service interface with [ServiceContract] into shared.dll
  • put service implementation class into the server
  • don't use interfaces for your data types
  • create bare-bones DataContract types with only the logic you want shared between client and server (e.g. validation)
  • put your DataContract data types into shared.dll
  • Don't use NetDataContractSerializer (like you seem to be doing in your code)

There are variations on this theme of course. E.g. instead of the shared dll you can share code files between server and client projects with #if for different compilation.

Eugene Osovetsky
Getting it to work without interfaces is simple but the closest we can come to getting it to work with interfaces (which is their requirement) is by using the NetDataContractSerializer.
drs9222