views:

357

answers:

4

Hi

I am writing a client/server application, where the client is a Windows Forms app, and the server is a WCF service hosted in a Windows Service. Note that I control both sides of the application.

I am trying to implement the practice of coding against an interface: i.e. I have a Shared assembly which is referenced by the client application. This project contains my WCF ServiceContracts and interfaces which will be exposed to clients. I am trying to only expose interfaces to the clients, so that they are only dependant on a contract, not any specific implementation. One of the reasons for doing this is so that I can have my service implementation, and domain change at any time without having to recompile and redeploy the clients. The interfaces/contracts will in this case not change. I only need to recompile and redeploy my WCF service.

The design issue I am facing now, is: on the client, how do I create new instances of objects, e.g. ICustomer, if the client doesn't know about the Customer concrete implementation? I need to create a new customer to be saved to the DB.

Do I use dependency injection, or a Factory class to instantiate new objects, or should I just allow the client to create new instances of concrete implementations?

I am not doing TDD, and I will typically only have one implementation of ICustomer or any other exposed interface.

A: 

Well, I think you're confusing (or mixing up) two things:

  • your Service contract will describe only the functions your service exposes, things like FindCustomer or AddCustomer - for those operations, your client only needs to know the interface, and when you add a client proxy (by using "Add Service Reference"), you'll also get a YourServiceClient concrete class in your client proxy which implements those calls

  • the Data contract describes the data going back and forth, and those are always concrete classes - Customer, Invoice etc. - since those are based on a XML schema. Those are defined at the server, and the client, when adding a Service Reference, will infer those types from the WSDL/XSD published by the service, and will create concrete classes on the client side for this data. Those are NOT the exact same classes as the server uses - they look the same, and their main requirement is that they serialize into and deserialize from XML the same - but they're different classes really (different .NET namespace, most likely).

So for your functionality, you have the service contract which you really only need to share as an interface - that's sufficient, and the client will create a "proxy class" (the YourServiceClient class) from that interface. The client does not depend on the server implementation or its concrete class there.

On the other hand, the data being exchanged - being serialized into XML format by default - will always be concrete classes (no interfaces) which are infered from the WSDL/XSD - again, the client does not depend on the server's classes - only on their "XML serialization fingerprint", so to speak.

Now I'm not sure this helps you a lot :-) but at least it hopefully makes things as they are a bit clearer - or not?

Marc

marc_s
@marc_s: I think you misunderstood my question completely. I think I should modify it to make it clearer. I fully understand WCF, and everything that you explained. But I am not using WCF like that. Both client and server share the MyProject.Shared assembly, which contains my types and interfaces (or contracts as I'll call it, but not [DataContract]s). They therefore both share the exact same types from the same assembly. I am using the NetDataContractSerializer, and using WCF to emulate something closer to .Net remoting
Saajid Ismail
+1  A: 

We've discussed doing this internally for enterprise apps where we control both sides of application, as a productivity gain, for .NET clients. Jury is still out on this one.

However, when discussing having contracts in a shared library (between client and service) typically this would include both service contracts ([ServiceContract]), as well as the entities ([DataContract]) for any parameters to service operations. These types are traditionally the concrete types your expecting for those service operations.

In your case, your DTO implements an interface, such as ICustomer, implementing properties that represent Customer, and is a [DataContract]. Assuming that the type will serialize correctly going to the service (using NetDataContractSerializer), then I imagine the client can pretty much shove whatever concrete implementation they want - the service is only interested in what conforms to ICustomer.

A client can create any concrete instance of ICustomer that they want: OrderingCustomer, FinanceCustomer, etc. As long as the type implements the service ICustomer interface it could conceivably be passed as the value to a service operation if it serializes correctly. e.g.

public class OrderingCustomer : ICustomer
{
}

I am not sure that you will achieve zero client impact your aiming for. If you change an interface type (add property to ICustomer) your clients will need to recompile. If you add a parameter, even one of core .NET type (e.g. int), your clients will need to recompile. This is effectively the same impact as the client updating their service reference and recompiling.

However, if your not changing your service implementation or behavior (e.g. bug fix), then in both cases (shared types or service reference) the clients will need not do anything as long as that contract between you and your client doesn't change. Of course, I'd also like to hear any experiences you've had with this that prove this wrong!

This practice would also completely kill your interoperable story with non-.NET systems. As sure as I sweep this one under the rug, some department somewhere will hear about your super spiffy service and want to use it....and they will be running some Java stack,or COBOL, or FORTRAN..etc. :)

Zach Bonham
You've described what I am trying to achieve precisely, but you haven't answered my question - how do I create new instances of objects on the client side. You obviously can't do this: `ICustomer cus = new ICustomer`, and you can't do this either: `ICustomer cus = new Customer()` since the client doesn't have access to the assembly containing the `Customer: ICustomer` implementation.
Saajid Ismail
ah, I think I missed a point in my response! Thats what I get for reading too fast! :) The client has to have access to ICustomer, if ICustomer is a parameter to the service operation. Otherwise, how will they ever invoke the service? They won't know what a ICustomer looks like - they can't just make this up on the fly, its a binary contract between client and service. The client cannot create its own ICustomer implementation and use that, you have to provide it. :)
Zach Bonham
You missed a critical point again. The client does have access to `ICustomer`, it's in the Shared assembly, referenced by both client and server. It's a must! But `ICustomer` is an interface - and you can't instantiate an interface type, as explained above.
Saajid Ismail
Sorry. I think i have the point, just worded my response poorly! You've provided clients with an ICustomer, so clients could implement ICustomer as they saw fit and forward that to your service. As long as the client type adhered to Service.ICustomer it would be fine. These concrete types would be beyond your control - only the interface ICustomer would you own, so that you can control what a concrete "Customer" would look like. Am I making more sense? If not, lemme know!
Zach Bonham
I emphasized the point in my answer above to see if that helps?
Zach Bonham
Yes that makes sense :-) I guess i could create a new implementation of ICustomer on the client side alone, and pass that to the server. I was thinking more on the lines of using a factory or DI in the Shared assembly, to new up an instance of Customer, but return it as an ICustomer. This will work - and makes more sense to me since I control both server and client. I don't need another concrete implementation of `ICustomer`. The main point is that I am trying my best to program against an interface only.
Saajid Ismail
+1  A: 

I've come across the same question in my WCF development. The fact is there must be a concrete implementation of your data contracts on both sides of the communication. So where do those implementations come from? On the server side, the implementations are typically your business objects, with all of their business logic and so on. And you don't want to use those implementations on the client--that would mean putting them in the shared assembly, and you don't want that for several reasons. So the client needs its own, separate set of concrete classes. Either you're going to write them yourself, or you're going to have them generated automatically.

Writing the implementations yourself is tedious. Because the client objects are typically simple--all they really do is store data--code generation is probably the way to go. At that point you have a few choices: You could generate code based on the WSDL via Add Service Reference, or you could generate classes at runtime based on the interfaces using a framework of some sort.

Both approaches give you the benefits of coding against an interface: As long as the interface doesn't change, the server and client can be modified and rebuilt independently. In one case the interface is the WSDL and in the other it's a CLR interface, but since WCF generates the WSDL based on the CLR interface, they're really one and the same. Personally I like Add Service Reference because it's already there and doesn't add any dependencies to my project, but pick whichever you like better.

Aaron
A: 

You've probably got your answer by now but I'm trying to do something similar at the moment - inject my Repositories into my WCF services, so those services don't have any dependencies on code that accesses the db. I've found the following articles which I think may help answer your question:

http://geekswithblogs.net/ballhaus/archive/2009/12/12/unit-testing-your-wcf-service.aspx http://www.silverlightshow.net/items/Deep-dive-into-WCF-part-1-TDD.aspx

HTH

Ciaran

Ciaran