【问题标题】:Object hierarchy returned by WCF Service is different than expectedWCF 服务返回的对象层次结构与预期不同
【发布时间】:2010-04-09 12:28:57
【问题描述】:

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

代码运行,对象返回。但奇怪的是,一旦我查看返回的对象,我注意到命名空间消失了,并且通过(Web 应用程序)服务引用公开的对象层次结构似乎变得“扁平”(不知何故)。现在,我希望通过 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 实际上看起来像...

srFish.New1

fishC1FE.ObjectState 实际上看起来像...

srFish.New

【问题讨论】:

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


    【解决方案1】:

    好的 - WCF 服务的默认行为是这样的:

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

    重要提示:它包含这些东西的副本!它们看起来相同,并且在网络上序列化为相同的 XML 格式 - 但它们是不同的 - 在不同的命名空间中,最明显的是。

    这就是 WCF 的本质 - 您所做的只是在客户端和服务器之间交换 序列化消息 - 所有来回都是文本消息。仅此而已 - 没有对象引用,没有远程对象 - 没有这样的。把它从你的脑海中抛开! :-)

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

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

    • 将您的服务合同和数据合同(只有合同 - 没有实现!)放入单独的程序集中
    • 从您的服务实现中引用该合同程序集
    • 将合同程序集复制到您的客户,并在您的客户项目中引用它

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

    这样,您可以创建供服务器端代码和客户端使用的单个共享合同程序集,并且您不必处理任何重复的数据结构。但同样:只有当您控制电线的两端并且两者都是 .NET 时,这才有效

    【讨论】:

    • 感谢马克的回答。我将尝试您创建“合同程序集”并在两者中引用它的建议。另外,我确实了解这些消息是序列化的副本。我想我曾希望服务(本身)中的新“WCF 属性”(例如 KnownType、ServiceKnownType 等)足以正确地将对象层次结构描述给任何客户端代理。显然,它不是……虽然它应该是。真的很失望。
    • 好的,所以我尝试了这个,它确实有效。有些事情我必须从合同类中抽出(将调用映射到另一个程序集)。
    • 我仍然很好奇是否存在其他方法,所以更多建议会很棒!
    猜你喜欢
    • 2016-06-17
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    相关资源
    最近更新 更多