我的用例:
- 我已经有一个工作的 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.config
inServerSide
:<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
。我这样做了,但坦率地说,所有这些东西并没有告诉我任何事情......
我究竟做错了什么?