WCF ServiceKnownType design issue - c#

I'm designing a new solution that will consist of three projects:
"Server" - a WCF service
"Client" - a winforms app that will call the WCF service
"ServiceContract" - a class lib containing some base classes plus the WCF service contract (interface). This will obviously be referenced by the Server, and also by the Client (I'm using a ChannelFactory rather than have VS generate a service reference). The service contract looks something like this:-
[ServiceContract]
[ServiceKnownType("GetCommandTypes", typeof(CommandTypesProvider))]
public interface INuService
{
[OperationContract]
bool ExecuteCommand(CommandBase command);
}
It's a very basic operation - the client creates a "command" object and sends it to the server to be executed. There will be many different commands, all inheriting from CommandBase (this base class resides in the "ServiceContract" project). As I'm using the base class in the WCF operation signature, I have to specify the known types which I'm doing dynamically using the ServiceKnownType attribute. This references a helper class (CommandTypesProvider) that returns all types deriving from CommandBase.
I've created a simple proof of concept with a couple of derived command classes that reside in the "ServiceContract" project. The helper class therefore only has to reflect types in the executing assembly. This all works fine.
Now in my "real" solution these command classes will be in different projects. These projects will reference the ServiceContract project, rather than vice-versa, which makes it difficult (or impossible?) for the helper to reflect the "command" assemblies. So my question is, how can I provide the known types?
Options I've thought about:-
The "Server" and "Client" projects will reference both the "ServiceContract" project and the various "command" projects. My helper could reflect through AppDomain.CurrentDomain.GetAssemblies(), but this fails because the "command" assemblies are not all loaded (I could force this by referencing a type in each, but that doesn't feel right - I want it to be a dynamic, pluggable architecture, and not have to modify code whenever I add a new command project).
Specify the known types in config. Again it would be nice if the app was dynamic, rather than have to update the config each time I add a command class.
Is there any way to access the underlying DataContractSerializer on both the client and server, and pass it the known types? I guess I'll still have the same issue of not being able to reflect the assemblies unless they've been loaded.
Refactor things to enable the ServiceContract project to reference the various command projects. I can then reflect them using 'Assembly.GetReferencedAssemblies()'. I guess the various command classes are part of the service contract, so perhaps this is the way to go? Edit: looks like this has the same problem of only finding loaded assemblies.
Any ideas greatly appreciated! Is it something that can be achieved or do I need to rethink my architecture?!
Thanks in advance.

One thing to consider is using the DataContractResolver.
Few resources:
WCF Extensibility – Data Contract Resolver by Carlos
Building Extensible WCF Service Interfaces With DataContractResolver by Kelly
Configuring Known Types Dynamically - Introducing the DataContractResolver by Youssef

Thanks for the replies regarding the Data Contract Resolver guys. I probably would have gone down this route normally but as I was using Windsor I was able to come up with a solution using this instead.
For anyone interested, I added a Windsor installer (IWindsorInstaller) to each of my "command" projects, which are run using Windsor's container.Install(FromAssembly.InDirectory.... These installs are responsible for registering any dependencies needed within that project, plus they also register all the command classes which my known types helper can resolve from the container.

Related

WCF Multiple Services with DataContracts as Parameters C#

I have multiple DataContracts and the same number of WCF Services to manage methods for each one. I have a specific [DataContract] called User that I use as paramenter in every other service, for example: ListCompany(User, CompanyId).
When a make a service referece to the WCF service, Company, it has a [DataContract] User too like Company.User which is different from the original User. Is there any way to solve this?
Logically Same DataContract, Used in Multiple Services
You are probably adding service references in the usual way for a WCF service client using the Add Service Reference... menu option. When you do that, a Reference.cs file will be generated for each service, and each service will have a different namespace. However, since you're sharing contracts across services, you need to click the Advanced... button on the Add Service Reference dialog and make sure the Reuse types in referenced assemblies is checked and (easiest) make sure Reuse types in all referenced assemblies is checked.
Now, that will not work if you don't actually have normal project or assembly references to the assemblies that contain those [DataContract] classes. So, add those references. If those classes are mixed into your server-side implementation, you will need to move them to their own assemblies and reference them on both the client and server.
Logically Different DataContract, Used in Multilpe Services
If you really have two different types of users (i.e. two different contracts) where you are using one contract for one service client and one contract for another service client, you should make sure Reuse types in referenced assemblies is not checked, and make sure that each service reference is in a different namespace.
An alternative is using a different name for each when declaring them:
[DataContract(Name = "User"]
public class User { ... }
[DataContract(Name = "CompanyUser")]
public class User { .... }
The code above assumes each User class is in a different server-side namespace, possibly different assembly as well.
I would suggest that you should look into a better way to use your WCF services that adding service references. This golden article describes how you can share the data contracts between all your solutions, and make use of them by using Chanel factories, without having VS generate loads of code for you. http://www.netfxharmonics.com/2008/11/understanding-wcf-services-in-silverlight-2
It might seem like a lot to take in, but it's full of really useful tips, including Service Access without magic which goes like this:
Now we may turn our attention to the client application. To begin,
let me start off by reminding everyone that you shouldn't ever use
"Add Service Reference" in Visual Studio for magical service client
creation. The code is incredibly verbose, hard to manageable, edits
are prone to being overwritten, and it's almost always used as an
excuse to not actually learn WCF. There are few things worse than
having to deal with people who thing they know a product simply
because they know how to use a mouse. There are reasons why Juval
Lowy, in all his books and talks, repeatedly tells people to avoid
using this flawed feature. Fortunately, as professionals, we have the
ability to understand how to do things without magic.
As I've mentioned many times already, WCF relies on the concept of the
ABC. We've seen how we configure a WCF host by creating an endpoint
specifying an address, binding and contract. As it turns out, this is
all that's required on the client side as well. For both .NET and
Silverlight, you merge an address and a binding with a contract in a
channel factory to create a channel. This isn't just fancy conceptual
architect speak, this is exactly what your code would look like (the
sign of really good architecture!) Below is the .NET version of what
I mean:
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:1003/Person.svc");
IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
//+
Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6");
Well worth a read.

Unable to access classes via WCF

I have two projects in the same solution, a service and a consumer app. In the service I have many classes that can be instantiated by the consumer app but some classes are not accessible. There's no difference apart from name. All classes are all Public so they should all be seen. Is there any buffering problems or anything else that could cause the problem to behave like this?
Consumer does not get access to Server classes when you use WCF or any other Web Services/Removing technology. Proxy classes are created instead. Think about them as set of Interfaces that are able to call method over app boundaries. You can instantiate proxy classes but when you call method proxy class will go to Service and call corresponding method of class hosted by service.
You need to use Class Library and move move your shared classes there (and deploy dll with Service and Consumer) if both parties use them.
Update (thanks razlebe):
Business logic should not be shared in DLLs. It should be hosted by server. But it will make sense to share supporting classes (for example class that do data formatting) to avoid code duplication.
When you update service class and change interface by:
Adding a method (Your case)
Removing a method
Changing signature
Your consumer needs to learn about the change. You have to update service reference (http://msdn.microsoft.com/en-us/library/bb628652.aspx) to rebuild proxy.
How to update it?
Check here to see how: http://msdn.microsoft.com/en-us/library/bb628652.aspx)
But one image is better than thousand words:
My guess is that the classes that "are not accessible" were created after the last generation of the proxy (classes of the service, client-side). Check if REgenerating the proxy helps.

Moving WCF contracts to a separate dll

I want to move WCF contracts interfaces to third DLL and start consuming them from clients when I am generating proxy by hand.
Do those interfaces in the DLL have to have the [ServiceContract] attribute when I consume them from the client to generate a proxy ?
Any idea what is the best naming for Dll that will have only contracts and the DTOs to share between client and the server.
It is very common practice to put WCF data and service contract to separate assembly. In my previous project we used naming like Company.OurProject.Contracts.dll.
I think that to generate proxy with reusing existing classes you should put your interfaces for service contracts (those marked with [ServiceContractAttribute]) and corresponding data contracts to that assembly. I would avoid putting there actual implementation of the services.
Here is another great answer on SO with more insight into what can be reused when "Reuse types in referenced assemblies" is selected: WCF Service Reference generates its own contract interface, won't reuse mine
This is common and perhaps recommended approach.
Yes you should put the service contract on the service interface which will live in the contract dll.
Keep in mind that the namespace does not have to match the name of the dll. Assuming that your current assembly is something like CompanyName.Technology.Service.dll with a namespace like CompanyName.Technology.Service you should extract out the contract to another assembly BUT keep the namespace the same (providing it still makes sense to) and have an assembly name of CompanyName.Technology.Service.Contracts. What you do not want to have is a namespace with the name "contracts" in it.
I use *.ServiceContracts.dll because I end up having multiple contract assemblies in my systems. DataContracts.dll for example is used for accessing data stores.

Type Redefinition With WSE Web Service Import

Consider the following Visual Studio project structure
ProjectA.csproj
AClass.cs
ProjectB.csproj
References
ProjectA
Web References
AWebService
AWebService.csproj
References
ProjectA
ReturnAClassViaWebService.asmx
The issue occurs when ProjectB adds the web reference to AWebService and automatically generates all the proxy code for accessing AWebService including a new implementation of AClass. Since all of our other code needs to use the AClass defined in ProjectA, we're forced to convert the AWebService.AClass returned from the service into something we can use.
We're currently considering two solutions, neither of which are ideal.
Manually editing the generated Reference.cs to remove new definitions of AClass
Serializing AWebService.AClass to a stream then deserializing to ProjectA.AClass
Does anyone have any better solutions? This seems like something common enough for other developers to have experienced it.
Ideally we would like to have the proxy code generated in ProjectB to reference ProjectA.AClass rather than generating a whole new implementation.
Our environment is VS 2008 using .NET 2.0.
I have had the same problem that you are describing and I have tried both of the options you specify without being entirely happy about either of them.
The reason we both have this issue is at least partly because the shared-library-between-consumer-and-provider-of-a-web-service-solution is in violation of accepted patterns and practices for web service design. On the consumer side, it should be sufficient to know the interface published in the WSDL.
Still, if you are prepared to accept a tight coupling between your web service provider and web service consumer and you know for certain that your current client will never be replaced by a different client (which might not be capable of referencing the shared library), then I understand why the proposed solution seems like a neat way to structure your app. IMPORTANT NOTE: Can we really honestly answer yes to both of these questions? Probably not.
To recap:
The issue appears when you have classes (e.g. a strongly typed dataset) defined in some sort of shared library (used on both client and server).
Some of your shared classes are used in the interface defined by your web service.
When the web reference is added there are proxy classes defined (for your shared classes) within the web reference namespace.
Due to the different namespaces the proxy class and its actual counterpart in the shared library are incompatible.
Here are four solutions that can be tried if you want to go ahead with the shared library setup:
Don't. Use the proxy class on the client side. This is how it is intendend to be done. It works fine unless you simultaneously want to leverage aspects of the shared library that are not exposed by the web service WSDL.
Implement or use a provided copy/duplication feature of the class (e.g. you could try to Merge() one strongly typed dataset into another). A Cast is obviosuly not possible, and the copy option is usually not a very good solution either since it tends to have undesirable side-effects. E.g. When you Merge a dataset into another, all the rows in the target dataset will be labeled as 'changed'. This could be resurrected with AcceptChanges(), but what if a couple of the received rows were actually changed.
Serialize everything - except for elementary data types - into strings (and back again on the consumer side). Loss of type safety is one important weakness of this approach.
Remove the explicit declaration of the shared class in Reference.cs and strip the namespace from the shared class wherever it is mentioned within Reference.cs. This is probably the best option. You get what you really wanted. The shared class is returned by the web service. The only irritating drawback with this solution is that your modifications to the reference.cs file is lost whenever you update your web reference. Trust me: It can be seriously annoying.
Here is a link to a similar discussion:
You can reuse existing referenced types between the client and service by clicking on the 'Advanced' button on the 'Add Service Reference' form. Make sure the 'Reuse types in referenced assemblies' checkbox is checked and when the service client is generated it should reuse all types from project A.
In past versions this has not always worked correctly and I've had to explicitly select the shared type assemblies by selecting the 'Reuse types in specified referenced assemblies' option and then checking the appropriate assemblies in the list box. However, I just tested this with VS 2008 SP1 and it appears to work as expected. Obviously, you need to make sure that the types that are being used by the service and client projects are both from project A.
Hope that this helps.
We encountered a similar problem with one of our projects. Because we had several dependencies, we ended up creating a circular reference because project 1 required objects from project 2, but project 2 could not be build before project 3, which relied on project 1 to be build.
To solve this problem, we extracted all the public standalone classes from both projects and placed them inside a single librarie. In the end we created something like this:
Framework.Objects
Framework.Interface
Framework.Implementation
WebService
The WebService would be linked to all projects in our case, whereas external parties would only be linking to the objects and interface classes to work with. The actuall implementation was coupled at runtime through reflection.
Hope this helps

Adding WCF Service Reference doesn't generate code

Scenario:
Web Site project under .NET 3.5
Visual Studio 2010
WCF Service reference
Problem:
I'm trying to extend a class marked with the DataContract attribute. I though the generated class was declared partial, so that i could easily extend it. I tried declaring a partial class within the same namespace with the same name, but it doesn't seem to recognize what class it's extending. I tried locating the generated code file (Reference.cs) which i thought existed after reading this article inside the reference folder, but it wasn't there. When trying to navigate to the class's definition, i found out it was in a compiled library, and the biggest problem is that it wasn't declared as partial.
Question:
Is this difference related to the fact that i'm using a Web Site and not a Web Project?
If so, is there a way that i could make the code generator (which also seems to compile the generated code) to declare class as partial?
Yes there is a way you can declare your DataContract classes as Partial.
For this you'd want to use the DTO pattern. Basically this means defining "shared" Classes in a different assembly, and having both the Service, and the App which consumes the Service, both reference the assembly with your common classes.
So for example your "DTOs" assembly might contain a DTO called "Product". Ok, so you make them Partial, and next you decorate Product, and which ever other Class with the WCF attributes, like DataContract, and DataMember etc.
Now, you reference you DTO assembly with you Service project, and your Web Project.
Now, when you go to your web project and click on "Add Service Reference", click on the "Advanced", and you'll notice you can enable an option to "resuse referenced assemblies". do that and you'll have full control over you DataContracts.
Empty client reference proxy classes can indeed be a most frustrating problem to solve.
I would recommend that you use the WCF Test Client or command line svcutil.exe. against the service - you can often get a much more detailed error description with these tools than with Visual Studio service reference wizard.
In my case the issues are invariably related to serialization or namespacing issues of the entity / graph - typically mismatched get and set on DataMember properties, missing KnownType on polymorphic entities, or circular references in the graph.
Partial shouldn't be a problem. Just make sure that any additional properties that you want serialized are marked as DataMember.
If all else fails, would recommend that you run a serialization / deserialization unit test against your entity / entity graph.

Categories

Resources