我的WCF之旅(7):面向服務架構(SOA)和面向對象編程(OOP)的結合 - Part II
4.Host Service:http://localhost/Artech.InheritanceHierarchy
現在我們完成了Service的定義,現在我們來Host我們定義的Service,這次我們通過IIS的方式來host service。我們首先在該Website中引用Artech.InheritanceHierarchy.Service Project。然後為FullWhetherForecastService定義相應的.SVC文件(由於Service Contract的繼承關系構成了一種Service Contract的層次結構,從而導致所有定義的Operation都出現在最底層的Contract中,由於SimpleWhetherForecastService的Operation沒有被FullWhetherForecastServiceOverride,所以現在我們只需要Host FullWhetherForecastService就可以了)。
<%@ ServiceHost Service="Artech.InheritanceHierarchy.Service.FullWhetherForecastService,Artech.InheritanceHierarchy.Service" %>
接著我們在Web.config中為Service注冊Endpoint。
<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.serviceModel>
<services>
<service name="Artech.InheritanceHierarchy.Service.FullWhetherForecastService" behaviorConfiguration="returnFaults">
<endpoint contract="Artech.InheritanceHierarchy.Service.IFullWhetherForecast" binding="wsHttpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="returnFaults">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Web.RegularExpressions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/></assemblies></compilation>
</system.web>
</configuration>
5.定義Client:Artech.InheritanceHierarchy.Client
到現在為止,我們完成了Service的定義和Host的工作,換句話說現在我們定義的Whether
Forecast service已經可以訪問,並暴露在這樣的一個Address上:http://localhost/Artech.InheritanceHierarchy/FullWhetherForecastService.svc。現在我們來編寫我們Client來訪問這個Service。值得一說的是,現在這個Sample中,Client是一個獨立的Application,我們既沒有讓他引用我們定義的Artech.InheritanceHierarchy.BusinessEntity(在WCF中,這個相當於Data Contract),也沒有讓它和Service共享同一個Service Contract。這很類似於在純Web環境下調用Service。
我們通過添加Service reference的方式生成我們Client端的code, WCF中的添加Service reference同Web Service中的添加Web reference相識。通過添加Service reference,WCF會為我們生成基於Client的service contract,data contract,proxy,configuraiton,甚至為我們添加System.ServiceNModel dll的引用。下面就是我們生成的Code:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.312
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Artech.InheritanceHierarchy.Client.WhetherForecastService
{
using System.Runtime.Serialization;
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
[System.SerializableAttribute()]
public partial class BasicWhetherInfo : object, System.Runtime.Serialization.IExtensibleDataObject
{
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private Artech.InheritanceHierarchy.Client.WhetherForecastService.WhetherConditions ConditionField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private double TemperatureField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WhetherConditions Condition
{
get
{
return this.ConditionField;
}
set
{
this.ConditionField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public double Temperature
{
get
{
return this.TemperatureField;
}
set
{
this.TemperatureField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
public enum WhetherConditions : int
{
[System.Runtime.Serialization.EnumMemberAttribute()]
Clear = 0,
[System.Runtime.Serialization.EnumMemberAttribute()]
Cloudy = 1,
[System.Runtime.Serialization.EnumMemberAttribute()]
Overcost = 2,
[System.Runtime.Serialization.EnumMemberAttribute()]
Rainy = 3,
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
[System.SerializableAttribute()]
public partial class WindInfo : object, System.Runtime.Serialization.IExtensibleDataObject
{
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private Artech.InheritanceHierarchy.Client.WhetherForecastService.WindDirection DirectionField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string SpeedField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WindDirection Direction
{
get
{
return this.DirectionField;
}
set
{
this.DirectionField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Speed
{
get
{
return this.SpeedField;
}
set
{
this.SpeedField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
public enum WindDirection : int
{
[System.Runtime.Serialization.EnumMemberAttribute()]
East = 0,
[System.Runtime.Serialization.EnumMemberAttribute()]
South = 1,
[System.Runtime.Serialization.EnumMemberAttribute()]
West = 2,
[System.Runtime.Serialization.EnumMemberAttribute()]
North = 3,
[System.Runtime.Serialization.EnumMemberAttribute()]
Northeast = 4,
[System.Runtime.Serialization.EnumMemberAttribute()]
SouthEast = 5,
[System.Runtime.Serialization.EnumMemberAttribute()]
Northwest = 6,
[System.Runtime.Serialization.EnumMemberAttribute()]
Southwest = 7,
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast")]
public interface IFullWhetherForecast
{
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfo", ReplyAction="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfoResponse")]
Artech.InheritanceHierarchy.Client.WhetherForecastService.BasicWhetherInfo GetBasicWhetherInfo(string postalcode);
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IFullWhetherForecast/GetWindInfo", ReplyAction="http://tempuri.org/IFullWhetherForecast/GetWindInfoResponse")]
Artech.InheritanceHierarchy.Client.WhetherForecastService.WindInfo GetWindInfo(string postalcode);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IFullWhetherForecastChannel : Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class FullWhetherForecastClient : System.ServiceModel.ClientBase<Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast>, Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast
{
public FullWhetherForecastClient()
{
}
public FullWhetherForecastClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public FullWhetherForecastClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public FullWhetherForecastClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public FullWhetherForecastClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public Artech.InheritanceHierarchy.Client.WhetherForecastService.BasicWhetherInfo GetBasicWhetherInfo(string postalcode)
{
return base.Channel.GetBasicWhetherInfo(postalcode);
}
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WindInfo GetWindInfo(string postalcode)
{
return base.Channel.GetWindInfo(postalcode);
}
}
}
Code比較長,我們現在一部分一部分地來分析。
Part I:Data Contract
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
[System.SerializableAttribute()]
public partial class BasicWhetherInfo : object, System.Runtime.Serialization.IExtensibleDataObject
{
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private Artech.InheritanceHierarchy.Client.WhetherForecastService.WhetherConditions ConditionField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private double TemperatureField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WhetherConditions Condition
{
get
{
return this.ConditionField;
}
set
{
this.ConditionField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public double Temperature
{
get
{
return this.TemperatureField;
}
set
{
this.TemperatureField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
public enum WhetherConditions : int
{
[System.Runtime.Serialization.EnumMemberAttribute()]
Clear = 0,
[System.Runtime.Serialization.EnumMemberAttribute()]
Cloudy = 1,
[System.Runtime.Serialization.EnumMemberAttribute()]
Overcost = 2,
[System.Runtime.Serialization.EnumMemberAttribute()]
Rainy = 3,
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
[System.SerializableAttribute()]
public partial class WindInfo : object, System.Runtime.Serialization.IExtensibleDataObject
{
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private Artech.InheritanceHierarchy.Client.WhetherForecastService.WindDirection DirectionField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string SpeedField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WindDirection Direction
{
get
{
return this.DirectionField;
}
set
{
this.DirectionField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Speed
{
get
{
return this.SpeedField;
}
set
{
this.SpeedField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://schemas.datacontract.org/2004/07/Artech.InheritanceHierarchy.BusinessEntit" +
"y")]
public enum WindDirection : int
{
[System.Runtime.Serialization.EnumMemberAttribute()]
East = 0,
[System.Runtime.Serialization.EnumMemberAttribute()]
South = 1,
[System.Runtime.Serialization.EnumMemberAttribute()]
West = 2,
[System.Runtime.Serialization.EnumMemberAttribute()]
North = 3,
[System.Runtime.Serialization.EnumMemberAttribute()]
Northeast = 4,
[System.Runtime.Serialization.EnumMemberAttribute()]
SouthEast = 5,
[System.Runtime.Serialization.EnumMemberAttribute()]
Northwest = 6,
[System.Runtime.Serialization.EnumMemberAttribute()]
Southwest = 7,
}
把這段代碼基本上對應的我們在Artech.InheritanceHierarchy.BusinessEntity定義的Data Contract:兩個Class:BasicWhetherInfo& WindInfo和連個Enum:WindDirection& WhetherConditions。不過有一點我覺得奇怪的是我們原來的BasicWhetherInfo& WindInfo的定義中,我Override了ToString方法,但是在生成的Class中,卻沒有相應的Code。我不清楚Microsoft對此事作怎麼樣的考慮,還是忽略了這一點。
Part II Service Contract
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast")]
public interface IFullWhetherForecast
{
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfo", ReplyAction="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfoResponse")]
Artech.InheritanceHierarchy.Client.WhetherForecastService.BasicWhetherInfo GetBasicWhetherInfo(string postalcode);
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IFullWhetherForecast/GetWindInfo", ReplyAction="http://tempuri.org/IFullWhetherForecast/GetWindInfoResponse")]
Artech.InheritanceHierarchy.Client.WhetherForecastService.WindInfo GetWindInfo(string postalcode);
}
在Service端,我們通過運用繼承定義了一套Service contract的層次結構,並為處於最底層的Contract公開了一個Endpoint。在Client端,我們通過添加Service reference的方式生成了Client的Service contract的結構。不過Client的Service contract的結構是一種扁平的結構:通過一個Contract定義所有的Operation。
Part III Proxy
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IFullWhetherForecastChannel : Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class FullWhetherForecastClient : System.ServiceModel.ClientBase<Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast>, Artech.InheritanceHierarchy.Client.WhetherForecastService.IFullWhetherForecast
{
public FullWhetherForecastClient()
{
}
public FullWhetherForecastClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public FullWhetherForecastClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public FullWhetherForecastClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public FullWhetherForecastClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public Artech.InheritanceHierarchy.Client.WhetherForecastService.BasicWhetherInfo GetBasicWhetherInfo(string postalcode)
{
return base.Channel.GetBasicWhetherInfo(postalcode);
}
public Artech.InheritanceHierarchy.Client.WhetherForecastService.WindInfo GetWindInfo(string postalcode)
{
return base.Channel.GetWindInfo(postalcode);
}
}
現在我們可以通過FullWhetherForecastClient這個Proxy Class來訪問Service了。雖然能完成我們的所有操作,但是這樣的代碼總覺得很別扭。我們希望的是以Service端定義結構進行Service的調用:Client具有兩個Proxy:SimpleWhetherForecastClient和FullWhetherForecastClient。FullWhetherForecastClient繼承自SimpleWhetherForecastClient。我們先刪除我們生成的Code,按照以下的步驟來實現這樣功能。
Step 1:定義Client端的Service Contract
ISimpleWhetherForecast
using System;
using System.Collections.Generic;
using System.Text;
using Artech.InheritanceHierarchy.BusinessEntity;
using System.ServiceModel;
namespace Artech.InheritanceHierarchy.Client
{
[ServiceContract]
public interface ISimpleWhetherForecast
{
[OperationContract]
BasicWhetherInfo GetBasicWhetherInfo(string postalcode);
}
}
IFullWhetherForecast.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.InheritanceHierarchy.BusinessEntity;
namespace Artech.InheritanceHierarchy. Client
{
[ServiceContract]
public interface IFullWhetherForecast:ISimpleWhetherForecast
{
[OperationContract]
WindInfo GetWindInfo(string postalcode);
}
}
除了namespace,和Service端的contract沒有什麼區別。
Step2:引用Artech.InheritanceHierarchy.BusinessEntity
Step3: 配置Endpoint
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost/Artech.InheritanceHierarchy/FullWhetherForecastService.svc"
binding="wsHttpBinding" bindingConfiguration="" contract="Artech.InheritanceHierarchy.Client.IFullWhetherForecast"/>
</client>
</system.serviceModel>
</configuration>
因為Service端的Endpoint對應的Contract是Artech.InheritanceHierarchy.Service IFullWhetherForecast,所以我們適應對應的Client端的Contract:Artech.InheritanceHierarchy.Client.IFullWhetherForecast
Step 4:建立Proxy
SimpleWhetherForecastClient.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.InheritanceHierarchy.BusinessEntity;
namespace Artech.InheritanceHierarchy.Client
{
public class SimpleWhetherForecastClient:ClientBase<IFullWhetherForecast>,ISimpleWhetherForecast
{
ISimpleWhetherForecast Members#region ISimpleWhetherForecast Members
public BasicWhetherInfo GetBasicWhetherInfo(string postalcode)
{
return this.Channel.GetBasicWhetherInfo(postalcode);
}
#endregion
}
}
FullWhetherForecastClient.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Artech.InheritanceHierarchy.Client
{
public class FullWhetherForecastClient:SimpleWhetherForecastClient,IFullWhetherForecast
{
IFullWhetherForecast Members#region IFullWhetherForecast Members
public Artech.InheritanceHierarchy.BusinessEntity.WindInfo GetWindInfo(string postalcode)
{
return this.Channel.GetWindInfo(postalcode);
}
#endregion
}
}
現在通過SimpleWhetherForecastClient和FullWhetherForecastClient完全以OO的方式優雅地調用Whether forecast service。