4

我的用例:

  • 我已经有一个工作的 ASP.NET 应用程序
  • 我想实现一个新的 Web 服务作为该应用程序的一部分
  • 我应该使用 WCF 服务 (*.svc),而不是ASP.NET Web 服务 (*.asmx)
  • 该服务需要有一个操作,我们称之为它GetInterface(),它返回一个接口的实例。该实例必须驻留在服务器上,而不是序列化到客户端;在该接口上调用的方法必须在服务器上执行。

这是我尝试过的(请告诉我哪里出错了):

  • 为了对此进行测试,我创建了一个新的ASP.NET Web 应用程序项目,名为ServiceSide.

  • 在那个项目中,我使用“添加→新项目”添加了一个WCF 服务。我叫它MainService。这既创建了一个MainService类,也创建了一个IMainService接口。

  • 现在我创建了一个新的类库项目ServiceWorkLibrary,它只包含要在客户端和服务器之间共享的接口声明,没有其他内容:

    [ServiceContract]
    public interface IWorkInterface
    {
        [OperationContract]
        int GetInt();
    }
    
  • 回到ServiceSide,我替换了接口中的默认DoWork()方法IMainService以及MainService类中的实现,并且我还为共享添加了一个简单的实现IWorkInterface。它们现在看起来像这样:

    [ServiceContract]
    public interface IMainService
    {
        [OperationContract]
        IWorkInterface GetInterface();
    }
    
    public class MainService : IMainService
    {
        public IWorkInterface GetInterface()
        {
            return new WorkInterfaceImpl();
        }
    }
    
    public class WorkInterfaceImpl : MarshalByRefObject, IWorkInterface
    {
        public int GetInt() { return 47; }
    }
    

    现在运行这个应用程序“工作”,因为它在浏览器中为我提供了默认的 Web 服务页面,其中显示:

    您已经创建了一个服务。

    要测试此服务,您需要创建一个客户端并使用它来调用该服务。您可以使用命令行中的 svcutil.exe 工具执行此操作,语法如下:

    svcutil.exe http://localhost:59958/MainService.svc?wsdl
    

    这将生成一个配置文件和一个包含客户端类的代码文件。将这两个文件添加到您的客户端应用程序并使用生成的客户端类来调用服务。例如:

  • 那么就到客户端了。在单独的 Visual Studio 中,我创建了一个使用新解决方案调用的新控制台应用程序ClientSide项目。我添加了ServiceWorkLibrary项目并从ClientSide.

  • 然后我运行了上面的svcutil.exe调用。这生成了一个MainService.cs和一个output.config,我将它们添加到ClientSide项目中。

  • 最后,我在方法中添加了以下代码Main

    using (var client = new MainServiceClient())
    {
        var workInterface = client.GetInterface();
        Console.WriteLine(workInterface.GetType().FullName);
    }
    
  • 这已经失败,在构造函数调用中出现了一个神秘的异常。我设法通过重命名output.config来解决这个问题App.config

  • 我注意到返回类型GetInterface()object而不是IWorkInterface。有谁知道为什么?但是让我们继续...

  • 现在当我运行它时,我CommunicationException在调用时得到一个GetInterface()

    基础连接已关闭:连接意外关闭。

我该如何解决这个问题,以便获得IWorkInterface我期望的透明代理?

我尝试过的事情

  • 我尝试添加[KnownType(typeof(WorkInterfaceImpl))]WorkInterfaceImpl. 如果我这样做,我会在同一个地方得到一个不同的异常。现在是一条NetDispatcherFaultException消息:

    格式化程序在尝试反序列化消息时抛出异常:尝试反序列化参数http://tempuri.org/:GetInterfaceResult时出错。InnerException 消息是“第 1 行位置 491 中的错误。元素“http://tempuri.org/:GetInterfaceResult”包含映射到名称“http://schemas.datacontract.org/2004/07/”的类型的数据服务端:WorkInterfaceImpl'。反序列化器不知道映射到此名称的任何类型。考虑使用 DataContractResolver 或将与“WorkInterfaceImpl”对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到传递给 DataContractSerializer 的已知类型列表中。有关更多详细信息,请参阅 InnerException。

    InnerException提到的是带有SerializationException消息的:

    第 1 行位置 491 出错。元素“http://tempuri.org/:GetInterfaceResult”包含映射到名称“http://schemas.datacontract.org/2004/07/ServiceSide:WorkInterfaceImpl”的类型的数据。反序列化器不知道映射到此名称的任何类型。考虑使用 DataContractResolver 或将与“WorkInterfaceImpl”对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到传递给 DataContractSerializer 的已知类型列表中。

    请注意这似乎表明系统正在尝试序列化该类型。它不应该那样做。它应该生成一个透明代理。我如何告诉它停止尝试序列化它?

  • [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]我尝试在类中添加一个属性 , WorkInterfaceImpl。没有效果。

  • 我尝试将接口[ServiceContract]上的属性IWorkInterface(在共享库中声明ServiceWorkLibrary)更改为[ServiceContract(SessionMode = SessionMode.Required)]. 也没有效果。

  • 我还尝试将以下魔法system.diagnostics元素添加到Web.configin ServerSide

      <system.diagnostics>
        <!-- This logging is great when WCF does not work. -->
        <sources>
          <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
            <listeners>
              <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\traces.svclog"  />
            </listeners>
          </source>
        </sources>
      </system.diagnostics>
    

    这确实会c:\traces.svclog按承诺生成文件,但我不确定我能否理解它的内容。我已将生成的文件发布到此处的 pastebin。您可以使用更友好的 UI 来查看此信息svctraceviewer.exe。我这样做了,但坦率地说,所有这些东西并没有告诉我任何事情......

我究竟做错了什么?

4

3 回答 3

1
于 2012-02-09T17:57:28.130 回答
0

是什么MainServiceClient()?它是将客户端消息编组到服务器的类。

您应该查看有关在 WCF 中将接口作为参数返回的相关 SO 帖子。ServiceKnownTypeAttribute可能会有所帮助。

会话也可能是您正在寻找的,MarshalByRef因为它与 .NET 远程处理行为有关。

另一种方法(MSDN 论坛上所述)是返回EndpointAddress服务接口的而不是接口本身。

WCF会序列化所有内容- 无论绑定如何。如果您需要与同一系统上的服务进行通信,您应该采用的最佳方法是使用IPC 传输绑定( net.pipe )。

于 2012-02-07T15:20:02.773 回答
0

您正在尝试做的是直接违反 SOA 原则:“服务共享模式和合同,而不是类”。这意味着您实际上并没有将实现代码从服务传递给它的消费者,而只是将合同本身中指定的返回值传递给它。

WCF 和 SOA 的主要焦点通常是互操作性,这意味着任何平台的客户都应该可以访问服务。Java 或 C++ 使用者如何能够使用您正在设计的这项服务?简短的回答是它不能,这就是为什么您会发现很难通过 SOAP 等消息传递标准序列化此代码,如果不是不可能的话。

构建此代码的更合适的方法是将 IWorkerInterface 的每个实现托管为自己的服务(毕竟它已被定义为服务合同),并将每个服务公开在不同的端点上。MainService 不是充当 IWorkerInterface 代理的远程工厂,而是充当您设置的不同服务的端点工厂。端点元数据可以很容易地被 IMainService 序列化并提供给客户端。然后,客户端可以通过一些自定义的 IServiceProxy 实现,或者甚至通过 WCF 已经提供给您的对象(例如 ChannelFactory),获取该元数据并构造远程实现的代理。

于 2012-02-07T15:57:08.600 回答