斗爷

导航

wcf服务编程(第3版)文摘

第1章 wcf基础

什么是wcf: System.ServiceModel.dll

服务 服务的执行边界: proxy

地址:http/https,tcp,ipc,peer newwork,msmq,service bus(服务总线地址)

契约:服务契约,(类虽然可以使用内部的属性,索引器,静态成员,但WCF客户端却无法访问)

数据契约,错误契约,消息契约(不是WCF 的常见情况)

托管:iis托管只能使用http协议

was托管:(window激活服务)

wsa扩展程序(window server appfabric):主要面向的事wf服务

选择宿主:p21,22(互联网,局域网)

绑定:基本绑定,TCP绑定,IPC绑定,WEB服务绑定,MSMQ绑定

选择绑定:p24

终结点

元数据的交换:1.基于http-get的元数据,2.元数据交换终结点

客户端编程:生成代理

WCF体系结构: 不使用代理类,使用通道直接调用服务ChannelFactory

第2章 服务契约

操作重载:不同的name区分操作

契约继承

服务契约分解与设计:服务契约的最佳数量3-5个

第3章 数据契约

net格式器:1.BinaryFormatter 2.SoapFormatter

wcf格式器:DataContrct

第4章 实例管理

单调服务:percall 客户端持有一个代理的引用,不是实际的对象 (sessionmode.notallowed)

会话服务:persession (sessionmode.required)

单例服务:single 全局日志

限流

第5章 操作

请求应答操作,默认的操作模式

单向操作,没有返回值

回调操作(双向操作)

第6章 错误契约

错误契约,客户端捕捉

第7章 事务

第8章 并发管理

第9章 队列服务

第10章 安全

第11章 服务总线

中继服务

----------------------------------------------------------------------

WCF编码规范

一个全面完整的编码规范是成功交付产品的根本。规范有助于推行最佳实践以及避免缺陷,可以让团队成员更容易分享知识与技能。传统的编码规范是一本浩如烟海一般的高文大册,厚达数百页,详尽了每个指南的基本原理。制定这样的规范固然是有胜于无,然而,如此努力却很难被大多数开发者所接受。比较而言,本书介绍的WCF编码规范却只有薄薄的几篇,主要介绍了WCF编码细节、编码内容以及设计目的。我相信,若要充分理解各种编程决策,可能需要阅读大量书籍,积累数年的经验,然而如果要实施编码规范则不必如此。当吸收一名新兵加入你的团队时,你可以交给他这份规范,告诉他:“先看看这个。”若要完全理解以及认识到范围的价值,或许需要时间与经验,然而,如果只是需要在此之前就能够遵循规范的约定,却是一蹴而就的事情。编码规范详细介绍了编码要求、缺陷、指南以及建议。编码规范同时还使用了本书介绍最佳实践以及辅助类。

通用设计指南

  1. 所有的服务必须遵循以下原则:
    1. 服务是安全的。
    2. 服务操作在系统中应该保持状态一致。
    3. 服务是线程安全的,且可以被并发客户端访问。
    4. 服务是可靠的。
    5. 服务是健壮的。
  2. 服务应该遵循以下可选原则:
    1. 服务是可互操作的。
    2. 服务的规模是不变的。
    3. 服务是可用的。
    4. 服务是及时响应的。
    5. 服务是受限的,阻塞客户端的时间不能过长。

WCF基础

  1. 应该将服务代码放入到类库中,而不是放到宿主EXE中。
  2. 不要为服务类提供参数构造函数,除非托管的服务是明确的单例服务。
  3. 在相关的绑定中启用可靠性。
  4. 要为契约提供有意义的命名空间。对于公开向外的服务,可以使用公司的URL或者等同的URN,然后加上年份和月份以避免版本冲突;例如:

[ServiceContract(Namespace = "http://www.idesign.net/2009/06")]

interface IMyContract

{...}

对于局域网服务,可以使用任何有意义的唯一的名称,如MyApplication;例如:

[ServiceContract(Namespace = "MyApplication")]

interface IMyContract

{...}

  1. 对于运行在Windows XP以及Windows Server 2003 上的局域网应用程序,最好选用自托管,而不是IIS托管。
  2. 在Windows Vista和Windows Server 2008或更近版本中,最好选用WAS (IIS7)托管,而不是自托管。
  3. 启用元数据交换。
  4. 要为客户端配置文件中的所有终结点命名。
  5. 不要使用SvcUtil或者Visual Studio2008生成配置文件。
  6. 不要复制代理的代码。如果两个或多个客户端使用相同契约,可以将代理分解到单独的类库中。
  7. 总是关闭或释放代理。

服务契约

  1. 总是将ServiceContract特性应用到接口上,而不是类上:

//避免

[ServiceContract]

class MyService

{

     [OperationContract]

     public void MyMethod()

{...}

}

//正确

[ServiceContract]

interface IMyContract

{

     [OperationContract]

     void MyMethod();

}

class MyService : IMyContract

{

     public void MyMethod()

{...}

}

  1. 服务契约要添加前缀I:

[ServiceContract]

interface IMyContract

{

     [OperationContract]

     void MyMethod();

}

  1. 要避免准属性(Property-Like)操作:

//避免

[ServiceContract]

interface IMyContract

{

     [OperationContract]

     string GetName();

 

[OperationContract]

     void SetName(string name);

}

  1. 避免定义只有一个成员的契约。
  2. 每个服务契约最好只定义3~5个成员。
  3. 每个服务契约的成员不要超过20个。12个是可能的实际限定。

数据契约

  1. 避免推断式数据契约(POCO)。总是显式地应用DataContract特性。
  2. 只在属性或只读公有成员上使用Datamember特性。
  3. 避免为定制类型显式地执行XML序列化。
  4. 避免使用消息契约。
  5. 当使用DataMemberAttribute特性的Order属性时,应该为同一类层级的所有成员分配相同的值。
  6. 数据契约应实现IExtensibleDataObject接口。使用显式接口实现。
  7. 避免在ServiceBehavior和CallbackBehavior特性上设置IgnoreExtensionDataObject。应保持它的默认值为false。
  8. 不要将委托和事件标记为数据成员。
    1. 不要将.NET的特殊类型例如Type作为操作的参数。
    2. 不要在操作中接受或返回ADO.NET的DataSet类型和DataTable类型(或者它们的类型安全子类)。应该返回一个与技术无关的表示形式,例如数组。
    3. 禁止为泛型的类型参数生成散列值,而应该提供易懂的类型名。

实例管理

  1. 考虑系统的可伸缩性时,最好使用单调实例模式。
  2. 如果契约设置为SessionMode.NoAllowed,则总是将服务实例配置为InstanceContextMode.PerCall。
  3. 不要在相同的服务上混合使用会话契约与无会话契约。
  4. 避免使用单例服务,除非该服务本质上就是单例的。
  5. 要为会话服务使用有序递送。
  6. 避免为会话服务停止实例。
  7. 避免使用分步操作。
  8. 对于持久服务,总是指定完成操作。

操作与调用

  1. 不要将单向调用设置为异步调用。
  2. 不要将单向调用设置为并发调用。
  3. 对单向操作抛出的异常做出预期。
  4. 为单向调用启用可靠性。对于单向调用而言,使用有序传递属于可选项。
  5. 避免在会话服务中定义单向操作。如果定义了,则应将它定义为终止操作:

[ServiceContract(SessionMode = SessionMode.Required)]

interface IMyContract

{

     [OperationContract]

void MyMethod1();

 

[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]

void MyMethod2();

}

  1. 为服务端的回调契约取名时,应使用服务契约名加上Callback后缀:

interface IMyContractCallback

{...}

[ServiceContract(CallbackContract = typeof(IMyContractCallback))]

interface IMyContract

{...}

  1. 尽量将回调操作标记为单向。
  2. 只为回调使用回调契约。
  3. 避免在相同的回调契约中将常规的回调与事件混为一谈
  4. 事件操作的设计应遵循如下规范:
    1. void返回类型
    2. 没有out参数
    3. 标记为单向操作
  5. 避免在事件管理中使用原来的回调契约,而应该使用发布-订阅框架
  6. 避免为回调显式地定义创建(Setup)方法和销毁(Teardown)方法:

[ServiceContract(CallbackContract = typeof(IMyContractCallback))]

Interface IMyContract

{

[OperationContract]

void DoSomething();

 

[OperationContract]

Void Connect();

 

[OperationContract]

void Disconnect();

}

Interface IMyContractCallback

{...}

  1. 使用类型安全的DuplexClientBase<T,C>,而不是DuplexClientBase<T>。
  2. 使用类型安全的DuplexChannelFactory<T,C>,而不是DuplexChannelFactory<T>。
  3. 调试或在局域网部署基于WSDualHttpBinding绑定的回调时,应该使用CallbackBaseAddressBehavior特性,并将CallbackPort设置为0:

[CallbackBaseAddressBehavior(CallbackPort = 0)]

Class MyClient:IMyContractCallback

{...}

错误

  1. 永远都不要在异常抛出之后使用代理实例,即使你捕获了该异常。
  2. 避免错误契约,并允许WCF屏蔽错误。
  3. 在异常抛出之后不要重用回调通道,即使你捕获了该异常,因为通道可能已经发生了错误。
  4. FaultContract特性中包含的类型应该是异常类类型,而不是单纯的可序列化类型:

//避免

[OperationContract]

[FaultContract(typeof(double))]

Double Divide(double number1,double number2);

 

//正确:

[OperationContract]

[FaultContract(typeof(DivideByZeroException))]

double Divide(double number1, double number2);

  1. 避免耗时过长的处理,例如IErrorHandler.ProvideFault()方法中的日志操作。
  2. 测试状态下,服务类与回调类的IncludeExceptionDetailInFaults值均应设置为true,可以在配置文件中配置,也可以采用编程方式:

public class DebugHelper

    {

        public const bool IncludeExceptionDetailInFaults =

        #if DEBUG

            true;

        #else

            false;

        #endif

    }

    [ServiceBehavior(IncludeExceptionDetailInFaults =

    DebugHelper.IncludeExceptionDetailInFaults)]

class MyService : IMyContract

{...}

  1. 构建交付版本时,除了诊断场景,不要将未知异常作为错误返回。
  2. 如果要提升异常为错误契约,并实现自动地错误日志记录,可以考虑在服务上使用ErrorHandlerBehavior特性:

[ErrorHandlerBehavior]

class MyService : IMyContract

{...}

  1. 如果要提升异常为错误契约,并实现自动地错误日志记录,可以考虑在回调客户端上使用CallbackErrorHandlerBehaviorAttribute特性:

[CallbackErrorHandlerBehaviorAttribute]

class MyClient : IMyContractCallback

{

     public void OnCallback()

     {...}

}

事务

  1. 永远不要直接使用ADO.NET事务。
  2. 要将TransactionFlow特性应用在契约上,而不是服务类上。
  3. 不要再服务构造函数中执行事务型工作。
  4. 如果使用本书术语,服务应该被配置为Client事务或Client/Service事务。避免使用None事务或Service事务。
  5. 如果使用本书术语,回调应该被配置为Service事务或Service/Client事务。避免使用None事物或Callback事物。
  6. 使用Client/Service或Service/Callback模式时,应该使用BindingzRequirement特性约束绑定使用事务流。
  7. 如果服务被配置为None事务或Service事务,则客户端总是会捕获它所抛出来的异常。
  8. 在使用事务时,允许可靠性和有序传递。
  9. 在服务操作中,永远不要捕获一个异常,并手动取消事务。

//避免

[OperationBehavior(TransactionScopeRequired = true)]

public void MyMethod()

{

try

{

...

}

catch

{

rtansaction.Current.Rollback();

}

}

  1. 如果在事务型操作中捕获了一个异常,那么总是要重新抛出它,或者抛出其它异常。
  2. 保证事务尽可能短。
  3. 总是使用默认的隔离级别IsolationLevel.Serializable。
  4. 不要调用事务中的单向操作。
  5. 不要调用事务中的非事务型服务。
  6. 不要访问事务中的非事务型资源(例如文件系统)。
  7. 对于会话服务,避免在会话关闭时通过自动完成将会话边界与事务边界等同对待。
  8. 尽量使用TransationalBehavior特性管理会话服务的事务。

[Serializable]

[TransationalBehavior]

class MyService :IMyContrant

{

public void MyMethod()

   {...}

}

  1. 使用一个会话服务后事务型的单例服务时,应使用易失性资源管理器管理状态,同事避免显示的状态相关的编程,或者在完成时依赖WCF停止实例。
  2. 对于事务性持久服务,总是将SaveStateInOperationTransaction设置为true,从而将事务传播给持久存储。

并发管理

  1. 总是线程安全访问如下内容:
    1. 会话服务或单例服务的内存状态
    2. 回调期间的客户端内存状态
    3. 共享资源
    4. 静态变量
    5. 建议选用ConcurrencyMode.Single(默认值)。它能够启用事务型访问,并提供线程安全的。
    6. 在ConcurrencyMode.Single模式下,保证会话服务和单例服务的操作耗时短,这是为了避免过长时间地堵塞其他客户端。
    7. 使用ConcurrencyMode.Multiple时,必须使用自动完成事务。
    8. 考虑为单调服务使用ConcurrencyMode.Multiple,从而允许并发调用。
    9. 配置为ConcurrencyMode.Multiple的事务型单例服务,则必须将ReleaseServiceInstanceOnTransactionComplete设置为false:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,

ConcurrencyMode = ConcurrencyMode.Multiple,

ReleaseServiceInstanceOnTransactionComplete = false)]

class MySingleton : IMyContract

{...}

  1. 永远不要在UI线程上实现自托管,也不要通过UI应用程序调用服务。
  2. 永远不要执行到达调用服务的UI应用程序的回调,除非回调使用SynchronizationContext.Post()方法传递调用。
  3. 为同步方法和异步方法提供代理时,只能将FaultContractAttribute特性应用到同步方法上。
  4. 保证异步操作尽可能短。不要将异步操作与长时间操作混为一谈。
  5. 不要混合使用事务与异步调用

队列服务

  1. 在客户端,总是调用队列服务之前验证队列(如果可能则验证死信队列)是否可用。可以使用QueuedServiceHelper.VerifyQueue()方法进行验证。
  2. 在运行一个队列服务(通过ServiceHost<T>自动执行)时,总是要验证队列是否可用。
  3. 除了是在隔离场景中,要避免设计相同的服务同时工作在队列和非队列中。
  4. 服务应该参与回放事务。
  5. 参与回放事务时,要避免在队列服务中执行耗时过长的处理。
  6. 避免会话队列服务。
  7. 使用一个单例队列服务时,应使用易失性资源管理器管理单例状态。
  8. 使用一个单调队列服务时,要显式地将契约和服务配置为单调模式以及无会话状态:

[ServiceConstract(SessionMode = SessionMode.NotAllowed)]

interface IMyContract

{...}

 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

class MyService : IMyContract

{...}

  1. 总是将单例队列服务的契约显式地设置为禁止会话:

[ServiceConstract(SessionMode = SessionMode.NotAllowed)]

interface IMyContract

{...}

   

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

class MyService : IMyContract

{...}

  1. 客户端应该在事务内调用一个队列服务。
  2. 在客户端,不要将队列服务代理储存在成员变量中。
  3. 避免将TimeToLive的值设置为相对较短的值,因为他们会抵消使用队列服务的价值。
  4. 避免非事务队列。
  5. 使用响应队列时,要让服务参与到回放事务中,并在事务中将响应放入队列。
  6. 要让响应服务参与到响应回放事务中。
  7. 避免在一个队列响应操作中执行耗时过长的处理。
  8. 对于MSMQ3.0 建议使用一个响应服务,而不是有害队列服务去处理服务自身的重复错误。
  9. 对于 MSMQ4.0 应对有害消息使用ReceiveErrorHandling.Reject, 除非通过ReceiveErrorHandling.Move 进行高级处理。应避免ReceiveErrorHandling.Fault和ReceiveErrorHandling.Drop。
  10. 对于 MSMQ4.0 考虑使用响应服务来处理服务回放失败。
  11. 除非是处理一个会话契约与会话服务,否则永远不要假定队列调用的顺序。

安全

  1. 总是要保护消息,提供消息证书与完整性。
  2. 在局域网中,只要保护级别被设置为EncryptAndSign,就可以使用传输安全。
  3. 在局域网中要避免使用模拟。应将模拟级别设置为TokenImpersonationLevel.Identification.
  4. 当使用模拟时,客户端应使用TokenImpersonaltionLevel.ImPersonation。
  5. 应使用声明性安全框架,避免手动配置。
  6. 永远不要将PrincipalPermission特性直接应用到服务类上:

//总是会失败

[PrincipalPermisson (SecurityAction.Demand,Role = “…”) ]

public calss MyService : IMyContract

{...}

  1. 避免在服务构造函数中使用需要授权的敏感工作。
  2. 不管是否要求角色,都应该避免强制要求一个特定的用户:

//避免

[[PrincipalPermisson (SecurityAction.Demand,Role =”John”) ]

public void MyService : IMyMethod

{...}

  1. 在客户端的回调操作中不要依赖于基于角色的安全机制。
  2. 对于互联网客户端,总是要使用消息安全。
  3. 允许客户端与服务证书进行协商(默认)。
  4. 使用ASP.NET的Provider定制证书。
  5. 开发一个定制证书存储时,应将它开发为ASP.NET Provider.
  6. 在使用端点信任( Peer Trust )时要验证证书。
  7. 尽量将客户端运行在部分信任之下。只能将客户端许可授予:
    1. 执行
    2. 显示用户界面
    3. 连接服务
    4. 获得本地凭据
  8. 在获得服务宿主环境时,在完全信任下运行和托管。将Microsoft和ECMA授予完全信任,但要移除其他的代码组,并将他们授予无许可权。
  9. 在部分信任下托管,只能将宿主和服务许可授予为:
    1. 执行
    2. 接受客户端调用
    3. 获得本地凭据
    4. 认证和授权客户端
    5. 如果需要获得本地资源

 

 

posted on 2014-02-06 20:55  斗哥哥  阅读(303)  评论(0编辑  收藏  举报