views:

204

answers:

1

Good Day Everyone...

My understanding may be wrong, but I thought once you applied the correct attributes the DataContractSerializer would render fully-qualified instances back to the caller.

The code runs and the objects return. But oddly enough, once I look at the returned objects I noticed the namespacing disappeared and the object-hierarchy being exposed through the (web applications) service reference seems to become "flat" (somehow). Now, I expect this from a web-service…but not through WCF. Of course, my understanding of what WCF can do may be wrong.

...please keep in mind I'm still experimenting with all this.

So my questions are…

Q: Can I do something within the WCF Service to force the namespacing to render through the (service reference) data client proxy?

Q: Or perhaps, am I (merely) consuming the service incorrectly?

Q: Is this even possible?

The service code looks like…

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class DataService : IFishData
{
    public C1FE GetC1FE(Int32 key)
    {
         //… more stuff here …
    }
    public Project GetProject(Int32 key)
    {
      //… more stuff here …
    }
}

[ServiceContract]
[ServiceKnownType(typeof(wcfFISH.StateManagement.C1FE.New))]
[ServiceKnownType(typeof(wcfFISH.StateManagement.Project.New))]
public interface IFishData
{
     [OperationContract]
     C1FE GetC1FE(Int32 key);

     [OperationContract]
     Project GetProject(Int32 key);
}

[DataContract]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class Project
{
      [DataMember]
      public wcfFISH.StateManagement.ObjectState ObjectState { get; set; }

      //… more stuff here …
}

[DataContract]
KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class C1FE
{
      [DataMember]
      public wcfFISH.StateManagement.ObjectState ObjectState { get; set; }

   //… more stuff here …
}

[DataContract(Namespace = "wcfFISH.StateManagement")]
[KnownType(typeof(wcfFISH.StateManagement.C1FE.New))]
[KnownType(typeof(wcfFISH.StateManagement.Project.New))]
public abstract class ObjectState
{
      //… more stuff here …
}

[DataContract(Namespace = "wcfFISH.StateManagement.C1FE", Name="New")]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class New : ObjectState
{
      //… more stuff here …
}

[DataContract(Namespace = "wcfFISH.StateManagement.Project", Name = "New")]
[KnownType(typeof(wcfFISH.StateManagement.ObjectState))]
public class New : ObjectState
{
      //… more stuff here …
}

The web application code looks like…

public partial class Fish_Invite : BaseForm
{
    protected void btnTest_Click(object sender, EventArgs e)
    {
       Project project = new Project();
       project.Get(base.ProjectKey, base.AsOf);

       mappers.Project mapProject = new mappers.Project();

       srFish.Project fishProject = new srFish.Project();
       srFish.FishDataClient fishService = new srFish.FishDataClient();

       mapProject.MapTo(project, fishProject);

       fishProject = fishService.AddProject(fishProject, IUser.UserName);

       project = null;
    }
}

In case I’m not being clear…

The issue arises in that the namespacing I expect to see (returned) is different from what is actually returned.

fishProject.ObjectState SHOULD look like...

srFish.StateManagement.Project.New

fishC1FE.ObjectState SHOULD look like...

srFish.StateManagement.C1FE.New

fishProject.ObjectState ACTUALLY looks like...

srFish.New1

fishC1FE.ObjectState ACTUALLY looks like...

srFish.New

…“Help me Obi-Wan Kenobi, you’re my only hope!”

A: 

OK - default behavior for a WCF Service is this:

  • you define your service contracts, operations, and data contract on the server (e.g. in namespace "Server.MyService")
  • once the service is up and running, on your client, you create a service reference
  • when doing so, what Visual Studio or svcutil.exe do, is interrogate that service for its metadata (description of service methods and data)
  • based on that metadata, the client side proxy is generated (namespace "Client.MyService") and it contains replicas of the service contract (the methods) and the data contract

Important: it contains replicas of those things! They look the same, and they serialize into the same XML format on the wire - but they are different - in different namespaces, most notably.

This is the very nature of WCF - all you do is exchange serialized messages between client and server - all that goes back and forth are textual messages. Nothing more - no object references, no remote object - nothing like that. Toss that out of your mind! :-)

If you control both ends of the wire, this can be a pain - if you need to change anything, you have to change it on the server side, update the client references and so forth.

So if you control both ends of the wire - both the server and the client - and they're both .NET based, you can do the following:

  • put your service contracts and your data contracts (only the contracts - no implementations!) into a separate assembly
  • from your service implementation, reference that contracts assembly
  • copy the contracts assembly to your client, and also reference it in your client project

Now, if you add the service reference, by default, the Add Service Reference function in Visual Studio will reuse existing types in referenced assemblies - so if you have referenced your common "Contracts" assembly, those types (in their full glory, including their namespace) will be reused - no additional copies will be created.

That way, you can create a single, shared contracts assembly used by both the server side code, as well as your client, and you don't have to mess with any duplication of data structures. But again: that only works if you are in control of both ends of the wire, and both are .NET

marc_s
Thanks for the answer Marc. I'll try your suggestion of creating a "contract assembly" and referencing it in both.Also, I do understand the messages are serialized replicas. I guess I had hoped the new “WCF attributes” (e.g. KnownType, ServiceKnownType et al) within the service (itself) would be enough to correctly DESCRIBE the object hierarchy to any client proxy. Obviously, it isn’t...although it should be. Very disappointing really.
OKAY, so I tried this and it DOES INDEED work. There are some things I'll have to yank OUT of the contract classes (mapping calls to another assembly).
I'm still curious if other methods exist, so more suggestions would be GREAT!
Thanks again Marc!