WCF服务返回的对象层次结构与预期不同

时间:2010-04-09 12:28:57

标签: wcf inheritance datacontractserializer proxy-classes service-reference

我的理解可能是错误的,但我认为一旦你应用了正确的属性,DataContractSerializer就会将完全限定的实例呈现给调用者。

代码运行,对象返回。但奇怪的是,一旦我看到返回的对象,我注意到命名空间消失了,通过(Web应用程序)服务引用公开的对象层次似乎变得“平坦”(不知何故)。现在,我希望这可以来自网络服务......但不是通过WCF。当然,我对WCF可以做什么的理解可能是错误的。

...请记住我还在尝试所有这些。

所以我的问题是......

问:我可以在WCF服务中做些什么来强制命名空间通过(服务引用)数据客户端代理进行渲染吗?

问:或者,或许,我(仅仅)不正确地使用该服务?

问:这甚至可能吗?

服务代码看起来像......

[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 …
}

Web应用程序代码看起来像......

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;
    }
}

如果我不清楚......

出现的问题是我希望看到(返回)的命名空间与实际返回的命名空间不同。

fishProject.ObjectState应该看起来像......

srFish.StateManagement.Project.New

fishC1FE.ObjectState应该看起来像......

srFish.StateManagement.C1FE.New

fishProject.ObjectState ACTUALLY看起来像......

srFish.New1

fishC1FE.ObjectState ACTUALLY看起来像......

srFish.New

1 个答案:

答案 0 :(得分:4)

确定 - WCF服务的默认行为是:

  • 您在服务器上定义服务合同,操作和数据合同(例如,在命名空间“Server.MyService”中)
  • 服务启动并运行后,在您的客户端上创建服务引用
  • 执行此操作时,Visual Studio或svcutil.exe所做的是询问该服务的元数据(服务方法和数据的描述)
  • 基于该元数据,生成客户端代理(名称空间“Client.MyService”),它包含服务合同(方法)和数据合同的副本

重要提示:它包含副本这些内容!它们看起来一样,并且它们在网络上序列化为相同的XML格式 - 但它们是不同的 - 在不同的名称空间中,最值得注意的是。

这就是WCF的本质 - 您所做的就是在客户端和服务器之间交换序列化消息 - 所有来回的都是文本消息。没有更多 - 没有对象引用,没有远程对象 - 没有这样的东西。把它扔出你的脑海! :-)

如果您控制电线的两端,这可能会很痛苦 - 如果您需要更改任何内容,则必须在服务器端更改它,更新客户端引用等等。

因此,如果您控制线路的两端 - 服务器和客户端 - 并且它们都是基于.NET的,您可以执行以下操作:

  • 将您的服务合同和数据合同(仅合同 - 无实施!)放入单独的程序集中
  • 来自您的服务实现,引用合同汇编
  • 将合同程序集复制到您的客户端,并在您的客户端项目中引用它

现在,如果添加服务引用,默认情况下,Visual Studio中的Add Service Reference函数将重用引用程序集中的现有类型 - 因此,如果您引用了常用的“Contracts”程序集,那么这些类型(在它们的将重用荣耀,包括其命名空间 - 不会创建额外的副本。

这样,您可以创建服务器端代码以及客户端使用的单个共享契约程序集,并且您不必混淆数据结构的任何重复。但同样:只有在控制线的两端并且两者都是.NET

时才有效