原文:WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效

本篇文章来源于几天前一个朋友向我咨询的问题。问题是这样的,他说他采用ASP.NET应用程序的方式对定义的WCF服务进行寄宿(Hosting),并使用配置的方式对服务的BaseAddress进行了设置,但是在创建ServiceHost的时候却抛出InvalidOperationException,并提示相应Address Scheme的BaseAddress找不到。我意识到这可能和WCF中用于判断服务寄宿方式的逻辑有关,于是我让这位朋友将相同的服务寄宿代码和配置迁移到GUI程序或者Console应用中,看看是否正常。结果如我所想,一切正常,个人觉得这应该是WCF的一个Bug。今天撰文与大家讨论,看看大家对这个问题有何见解。

一、问题重现

问题很容易重现,假设我们通过ASP.NET应用对服务CalculatorService进行寄宿,为了简单起见,我将服务契约和服务实现定义在一起。CalculatorService的定义如下面的代码片断所示:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1: using System.ServiceModel;

   2: namespace Artech.AspnetHostingDemo

   3: {

   4:     [ServiceContract(Namespace = "urn:artech.com")]

   5:     public class CalculatorService

   6:     {

   7:         [OperationContract]

   8:         public double Add(double x, double y) { return x + y; }

   9:     }

  10: }

下面是服务寄宿相关的配置,在<host>/<baseAddresses>配置节中为服务添加了一个Scheme为http的BaseAddress:http://127.0.0.1:3721/services,那么终结点的地址就可以定义为基于该BaseAddress的相对地址了:calculatorservice。

   1: <?xml version="1.0"?>

   2: <configuration>

   3:   <system.serviceModel>

   4:     <services>

   5:       <service name="Artech.AspnetHostingDemo.CalculatorService">

   6:         <host>

   7:           <baseAddresses>

   8:             <add baseAddress="http://127.0.0.1:3721/services"/>

   9:           </baseAddresses>

  10:         </host>

  11:         <endpoint address="calculatorservice" binding="wsHttpBinding" contract="Artech.AspnetHostingDemo.CalculatorService"/>

  12:       </service>

  13:     </services>

  14:   </system.serviceModel>

  15:   <system.web>

  16:     <compilation debug="true"/>

  17:   </system.web>

  18: </configuration>

我们把服务寄宿的代码定义在一个Web Page的Load事件中。但程序执行到到创建ServiceHost的时候,抛出如下图所示的InvalidOperationException异常。

 

下面是错误信息和异常的StackTrace:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1: Could not find a base address that matches scheme http for the endpoint with binding WSHttpBinding. Registered base address schemes are [].

 

   1: at System.ServiceModel.ServiceHostBase.MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding, UriSchemeKeyedCollection baseAddresses)   

   2: at System.ServiceModel.Description.ConfigLoader.LoadServiceDescription(ServiceHostBase host, ServiceDescription description, ServiceElement serviceElement, Action`1 addBaseAddress)   

   3: at System.ServiceModel.ServiceHostBase.LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, ServiceElement serviceSection)   

   4: at System.ServiceModel.ServiceHostBase.LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, String configurationName)   

   5: at System.ServiceModel.ServiceHostBase.ApplyConfiguration()   

   6: at System.ServiceModel.ServiceHostBase.InitializeDescription(UriSchemeKeyedCollection baseAddresses)   

   7: at System.ServiceModel.ServiceHost.InitializeDescription(Type serviceType, UriSchemeKeyedCollection baseAddresses)   

   8: at System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri[] baseAddresses)   

   9: at Artech.AspnetHostingDemo._Default.Page_Load(Object sender, EventArgs e) in e:\WCF Projects\AspnetHostingDemo\AspnetHostingDemo\Default.aspx.cs:line 16   

  10: at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)   

  11: at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)   

  12: at System.Web.UI.Control.OnLoad(EventArgs e)   at System.Web.UI.Control.LoadRecursive()   

  13: at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

二、问题分析

通过上面提供StackTrace,我们可以看到错误发生在WCF试图将BaseAddress和RelativeAddress进行组合生成AbsoluteAddress的时候。从错误消息可以看出,在进行地址的组合时,由于没有找到适合绑定类型(WsHttpBinding)Scheme(http)的BaseAddress,导致了异常的抛出。

要解答这个问题,首先要解释一下WCF的BaseAddress在不同服务寄宿(Service Hosting)方式下的定义方式。对于WCF服务的自我寄宿(Self Hosting)或者采用Windows Service进行服务寄宿,我们可以通过代码或者形如上面的配置为服务指定一系列的BaseAddress(对于一个既定的URI Scheme,只能由唯一的BaseAddress)。但是对于采用IIS或者WAS进行服务寄宿,我们需要为相应的服务定义一个.svc文件,我们通过访问.svc文件的方式来调用相应的服务。对于后者,.svc文件得地址就是WCF服务的BaseAddress,所以WCF会忽略BaseAddress的配置。

那么WCF采用怎样的方式来判断当前服务寄宿的方式是基于IIS呢,还是其他呢?答案是通过System.Web.Hosting.HostingEnvironment的静态属性IsHosted。对于ASP.NET有一定了解的人应该很清楚,在一个ASP.NET应用下,该属性永远返回为True。也就是说,WCF会把基于ASP.NET应用的服务寄宿,看成是基于IIS的服务寄宿,这显然是不对的。

   1: public sealed class HostingEnvironment : MarshalByRefObject

   2: {     //其他成员

   3:     public static bool IsHosted { get; }

   4: }

WCF对BaseAddress配置的加载和添加的逻辑定义在ServiceHostBase的LoadHostConfig方法中,大致的逻辑如下面的代码所示:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1: public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable

   2: {

   3:     [SecurityTreatAsSafe, SecurityCritical]

   4:     private void LoadHostConfig(ServiceElement serviceElement, ServiceHostBase host, Action<Uri> addBaseAddress)

   5:     {

   6:         HostElement element = serviceElement.Host; if (element != null)

   7:         {

   8:             if (!ServiceHostingEnvironment.IsHosted)

   9:             {                //BaseAddress配置加载与添加

  10:             }

  11:         }

  12:     }

  13: }

 

   1: public static class ServiceHostingEnvironment

   2: {

   3:     private static bool isHosted; internal static bool IsHosted { get { return isHosted; } }

   4:     internal static void EnsureInitialized()

   5:     {

   6:         if (hostingManager == null)

   7:         {

   8:             lock (ThisLock)

   9:             {

  10:                 if (hostingManager == null)

  11:                 {

  12:                     if (!HostingEnvironmentWrapper.IsHosted)

  13:                     {

  14:                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("Hosting_ProcessNotExecutingUnderHostedContext", new object[] { "ServiceHostingEnvironment.EnsureServiceAvailable" })));

  15:                     }

  16:                     HostingManager manager = new HostingManager();

  17:                     HookADUnhandledExceptionEvent(); 

  18:                     Thread.MemoryBarrier(); 

  19:                     isSimpleApplicationHost = GetIsSimpleApplicationHost();

  20:                     hostingManager = manager; 

  21:                     isHosted = true;

  22:                 }

  23:             }

  24:         }

  25:     }

  26: }

  27:  

   1: internal static class HostingEnvironmentWrapper

   2: {

   3:     public static bool IsHosted

   4:     {

   5:         get { return HostingEnvironment.IsHosted; }

   6:     }

   7: }

三、解决方式

其实这种情况也没有什么好的解决方案,不外乎就是避免通过配置的方式设置服务的BaseAddress,可以通过代码的方式来设置。如下面的代码所示:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1: namespace Artech.AspnetHostingDemo

   2: {

   3:     public partial class _Default : System.Web.UI.Page

   4:     {

   5:         private ServiceHost _serviceHost;

   6:         protected void Page_Load(object sender, EventArgs e)

   7:         {

   8:             this._serviceHost = new ServiceHost(typeof(CalculatorService), new Uri("http://127.0.0.1:3721/services")); 

   9:             this._serviceHost.Open();

  10:         }

  11:     }

  12: }

另一种方式就是采用绝对地址的方式定义终结点:

   1: <?xml version="1.0"?>

   2: <configuration>

   3:   <system.serviceModel>

   4:     <services>

   5:       <service name="Artech.AspnetHostingDemo.CalculatorService">

   6:         <endpoint address="http://127.0.0.1:3721/services/calculatorservice" binding="wsHttpBinding" contract="Artech.AspnetHostingDemo.CalculatorService"/>

   7:       </service>

   8:     </services>

   9:   </system.serviceModel>

  10:   <system.web>

  11:     <compilation debug="true"/>

  12:   </system.web>

  13: </configuration>

 

作者:Artech

出处:http://artech.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效的更多相关文章

  1. WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构

    原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构 细算起来,已经有好几个月没有真正的写过文章了.近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直 ...

  2. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

  3. WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

    原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...

  4. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

  5. WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成

    原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...

  6. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  7. WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务

    原文:WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务 在<基于IIS的WCF服务寄宿(Hosting)实现揭秘>中,我们谈到在采用基于IIS(或者 ...

  8. WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘

    原文:WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘 通过<再谈IIS与ASP.NET管道>的介绍,相信读者已经对IIS和ASP.NET的请求处理管道有了一个大致 ...

  9. WCF技术剖析之三:如何进行基于非HTTP的IIS服务寄宿

    原文:[原创]WCF技术剖析之三:如何进行基于非HTTP的IIS服务寄宿 在上面一篇文章中,我们对不同版本的IIS,以及ASP.NET得的实现机制进行了详细而深入的分析.在介绍IIS7.0的时候,我们 ...

随机推荐

  1. Matlab近期用到的函数(持续更新)

    最近任务用到matlab较多,第一版的代码大部分对于矩阵类的计算都是用for循环来完成的,主要是思维还没有适应matlab.看了同事的一份代码后,现在遇到需要循环的地方第一反应就是如何能够用矩阵的直接 ...

  2. Qt探秘——谈ui文件的用法

    转载自:点击打开链接http://blog.csdn.net/luo_isaiah/article/details/5794973 相信用过Qt Designer的朋友,对Qt Project中的.u ...

  3. MySQL root密码重置报错:mysqladmin: connect to server at 'localhost' failed的解决方案!

    -- ==================================================================== --  mysqladmin: connect to s ...

  4. IT第五天 - 循环的使用、本周总结 ★★★

    IT第五天 上午 循环 1.while循环.do-while循环.switch语句块的使用 下午 编程 1.编程注释的编写 2.编程力求代码的精简,算法的优化 3.变量的优化使用 小项目 1.swit ...

  5. highchats与php结合生成动态统计图

    series: [{ type: 'pie', name: 'Browser share', data: [ ['Firefox', 45.0], ['IE', 26.8], { name: 'Chr ...

  6. large-scale analysis of malware downloaders

    http://www.christian-rossow.de/publications/downloaders-dimva12.pdf

  7. Windows 下统计行数的命令

    大家都知道在Linux下统计文本行数能够用wc -l 命令.比如: -bash-3.2$ cat pif_install.log | wc -l       712 但在Windows下怎样统计输出文 ...

  8. datetime.timedelta

    from django.utils import timezoneimport datetime timezone.now()datetime.datetime(2014, 7, 18, 9, 42, ...

  9. php calling scope

    昨天在Yaf交流群, 大草原同学批评我变懒了, Blog很久没更新了, 今天刚好有人在Segmentfalut上问了我一个问题,  我在微博上也做了简单的解答, 不过感觉一句话说不清楚, 就写篇blo ...

  10. 基于visual Studio2013解决算法导论之012计数排序

     题目 计数排序 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...