2. 定义Artech.InheritanceHierarchy.BusinessEntity
BasicWhetherInfo.cs:
using System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Serialization;
namespace
Artech.InheritanceHierarchy.BusinessEntity
{
[DataContract]
[KnownType(typeof(WhetherConditions))]
public class BasicWhetherInfo
{
private WhetherConditions _condition;
private double _temperature;
public BasicWhetherInfo(WhetherConditions condition, double temperature)
{
this._condition = condition;
this._temperature = temperature;
}
[DataMember]
public WhetherConditions Condition
{
get { return _condition; }
set { _condition = value; }
}
[DataMember]
public double Temperature
{
get { return _temperature; }
set { _temperature = value; }
}
public override string ToString()
{
return string.Format("Conditions: {0}; Temperature: {1}", this._condition, this._temperature);
}
}
public enum WhetherConditions
{
Clear,
Cloudy,
Overcost,
Rainy
}
}
BasicWhetherInfo包含连个字段/属性:Condition和Temperature。属于Condition不属于基元(Primitive type)所以我们需要添加 [KnownType(typeof(WhetherConditions))]Attribute(由于跨AppDomain的数据传递要求传递的数据先辈Serialization。对于.NET中定义的Primitive type,比如string,int以及其他一些常用的类型,比如datetime,WCF具有一套默认的序列化机制,但是对于另外一些类型,Serializor在执行Serialization的时候 需要获得相关类型的Metadata的信息,WCF通过KnownType attribute向Serializor提供Metadata的信息。)。WindInfo中的Direction属性也是一样的原理。
WindInfo.cs
using System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Serialization;
namespace
Artech.InheritanceHierarchy.BusinessEntity
{
[DataContract]
[KnownType(typeof(WindDirection))]
public class WindInfo
{
private WindDirection _direction;
private string _speed;
public WindInfo(WindDirection direction, string speed)
{
this._direction = direction;
this._speed = speed;
}
[DataMember]
public WindDirection Direction
{
get { return _direction; }
set { _direction = value; }
}
[DataMember]
public string Speed
{
get { return _speed; }
set { _speed = value; }
}
public override string ToString()
{
return string.Format("Direction: {0}; Speed: {1}", this._direction, this._speed);
}
}
public enum WindDirection
{
East,
South,
West,
North,
Northeast,
SouthEast,
Northwest,
Southwest
}
}
3. 定义Service:Artech.InheritanceHierarchy.Service
我们首先来定义Service Contract
ISimpleWhetherForecast.cs:
using System;
using
System.Collections.Generic;
using
System.Text;
using
Artech.InheritanceHierarchy.BusinessEntity;
using
System.ServiceModel;
namespace
Artech.InheritanceHierarchy.Service
{
[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. Service
{
[ServiceContract]
public interface IFullWhetherForecast:ISimpleWhetherForecast
{
[OperationContract]
WindInfo GetWindInfo(string postalcode);
}
}
我们定义了连个Interface作为Service Contract。其中IFullWhetherForecast继承ISimpleWhetherForecast。这里需要注意的是虽然IFullWhetherForecast继承ISimpleWhetherForecast,但是运用在ISimpleWhetherForecast中的ServiceContract Attribute却不能被IFullWhetherForecast使用,这是因为在定义System.ServiceModel. ServiceContractAttribute, 把运用在它之上的AttributeUsage的Inherited设为false, 导致它不能运用到派生的Class上面:
using System;
using
System.Net.Security;
namespace
System.ServiceModel
{
[AttributeUsage(1028, Inherited = false, AllowMultiple = false)]
public sealed class ServiceContractAttribute : Attribute
{
public ServiceContractAttribute();
public Type CallbackContract { get; set; }
public string ConfigurationName { get; set; }
public bool HasProtectionLevel { get; }
public string Name { get; set; }
public string Namespace { get; set; }
public ProtectionLevel ProtectionLevel { get; set; }
public SessionMode SessionMode { get; set; }
}
}
我们接着为这两个Service Contract定义对应的Service。
SimpleWhetherForecastService:
using System;
using
System.Collections.Generic;
using
System.Text;
using
Artech.InheritanceHierarchy.BusinessEntity;
namespace
Artech.InheritanceHierarchy.Service
{
public class SimpleWhetherForecastService:ISimpleWhetherForecast
{
ISimpleWhetherForecast Members
}
}
为了代码的重用,我们让FullWhetherForecastService继承自SimpleWhetherForecastService,这样我们就不必重新定义GetBasicWhetherInfo方法了。
FullWhetherForecastService.cs:
using System;
using
System.Collections.Generic;
using
System.Text;
using
Artech.InheritanceHierarchy.BusinessEntity;
namespace
Artech.InheritanceHierarchy.Service
{
public class FullWhetherForecastService:SimpleWhetherForecastService,IFullWhetherForecast
{
IFullWhetherForecast Members
}
}
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 versinotallow="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, Versinotallow=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
< add assembly ="Microsoft.Transactions.Bridge, Versinotallow=3.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
< add assembly ="SMDiagnostics, Versinotallow=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
< add assembly ="System.IdentityModel.Selectors, Versinotallow=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
< add assembly ="System.Security, Versinotallow=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
< add assembly ="System.Web.RegularExpressions, Versinotallow=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
< add assembly ="System.Transactions, Versinotallow=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
< add assembly ="System.Messaging, Versinotallow=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
< add assembly ="System.ServiceProcess, Versinotallow=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:
Code比较长,我们现在一部分一部分地来分析。
Part I:Data Contract
把这段代码基本上对应的我们在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(Actinotallow="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfo", ReplyActinotallow="http://tempuri.org/ISimpleWhetherForecast/GetBasicWhetherInfoResponse")]
Artech.InheritanceHierarchy.Client.WhetherForecastService.BasicWhetherInfo GetBasicWhetherInfo(string postalcode);
[System.ServiceModel.OperationContractAttribute(Actinotallow="http://tempuri.org/IFullWhetherForecast/GetWindInfo", ReplyActinotallow="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 versinotallow="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
}
}
FullWhetherForecastClient.cs
using System;
using
System.Collections.Generic;
using
System.Text;
namespace
Artech.InheritanceHierarchy.Client
{
public class FullWhetherForecastClient:SimpleWhetherForecastClient,IFullWhetherForecast
{
IFullWhetherForecast Members
}
}
现在通过SimpleWhetherForecastClient和FullWhetherForecastClient完全以OO的方式优雅地调用Whether forecast service。