原文: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. python成长之路——第八天

    pickle,load :切记:如果load的是个对象的话,必须导入构建这个对象的类     封装 类和对象的关系: 每个对象里都有一个类对象指针,指向类     继承:支持单继承和多继承 print ...

  2. 0课程介绍(Week1,3月3日)

    一.自我介绍 1.姓名:杨晔 2.办公室:B211-2 3.电子邮件:yangye@zjjy.com.cn 4.QQ:6706892 5.博客:http://www.cnblogs.com/meety ...

  3. 移动平台WEB前端开发技巧汇总(转)

    最近我很关注移动前端的知识,但做为一个UI设计师和web前端工作人员没有这个工作环境接触,做为门外汉,网上系统的知识也了了,一直有种雾里看花的感觉,见到本文,我自己是奉为经典.所以我分享之后又专门打笔 ...

  4. struts.xml的配置

    <?xml version="1.0" encoding="UTF-8"?> <!--第一行必须这样写,这句话必须放在第一行--> &l ...

  5. Tomcat7 + JRebel6.3.0 + IntelliJ idea 热部署配置过程+错误分析

    以前使用Tomcat的时候直接就可以热部署,现在换了一个使用Spring框架的项目突然就不能热部署了. 网上说在tomcat里conf/context.xml中加入 <Context antiJ ...

  6. Asteroids(最小点覆盖)

    Asteroids Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18183   Accepted: 9905 Descri ...

  7. large-scale analysis of malware downloaders

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

  8. Android自定义控件实战——水流波动效果的实现WaveView

    转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38556891 水流波动的波形都是三角波,曲线是正余弦曲线,但是Android ...

  9. 一个简单的mfc单页界面文件读写程序(MFC 程序入口和执行流程)

    参考:MFC 程序入口和执行流程  http://www.cnblogs.com/liuweilinlin/archive/2012/08/16/2643272.html 程序MFCFlie      ...

  10. JSONToObejct 问题 part 1

    直接截图,就明白了 前端的处理 这里用到 JSON2.stringify()  这个方法是将对象(object) 转换成 [{},{},+...+,{}] 这种键值对形式的数据,不然rows只是一个选 ...