可以使用本主题中的最佳做法来改进应用程序和适配器。
在通道异常时应在关闭之前调用中止
编写使用 WCF 通道模型的应用程序时,你应该先调用IRequestChannel.Abort
,然后再调用ChannelFactory.Close
。 如果不这样做, ChannelFactory.Close
将引发异常。
在以下示例中,尝试在 try/catch 块中进行通道操作。 在发生异常时,将终止该通道。
ChannelFactory<IRequestChannel> cf = new ChannelFactory<IRequestChannel>();
IRequestChannel channel = null;
try
{
cf.Open();
channel = cf.CreateChannel();
channel.Open();
channel.Request();// This causes the channel to go into a faulted state.
channel.Close();
}
catch (Exception e)
{
// Abort the channel if we have one
if(channel != null)
channel.Abort();
}
finally
{
if (cf.State == CommunicationState.Opened)
{
cf.Close(); // It throws an exception that the channel is in a faulted state.
}
}
实现异步和同步处理器
如果可能,请在适配器中实现异步和同步处理程序。 如果适配器仅实现同步调用,则处理大量消息或适配器在多线程环境中使用时可能会遇到阻塞问题。
使用连接池
WCF LOB 适配器 SDK 默认支持连接池。 但是,适配器开发人员可以决定将哪些连接池属性作为绑定属性提供。 可用的连接池设置定义在 Microsoft.ServiceModel.Channels.Common.ConnectionPoolSettings
中。
在适配器服务外接程序中没有可供选择的选项可轻松将这些属性公开为适配器连接的属性。 适配器开发人员必须手动定义适配器实现中的属性。
public CustomAdapter(): base()
{
this.Settings.ConnectionPool.EnablePooling = true;
this.Settings.ConnectionPool.HandlersShareSameConnection = true;
this.Settings.ConnectionPool.MaxConnectionsPerSystem = 50;
this.Settings.ConnectionPool.MaxAvailableConnections = 5;
}
确保适配器支持 Two-Way操作
如果适配器是从 BizTalk Server 调用的,则它必须支持双向操作,即使返回值为空也是如此。 这是因为 BizTalk Server 期望从任何传出请求中收到响应,如果您的适配器只实现单向操作,将会引发异常。
下面是返回 void 的请求-响应协定的示例。
[ServiceContract(Namespace=”Http:Microsoft.BizTalk.Samples.WCFAdapterSample”)]
public interface ICalculator
{
[OperationContract]
void Add(double n1, double n2);
}
实现跟踪
在开发周期中,向适配器添加跟踪似乎并不重要,由于可以逐步检查代码并调试任何问题。 但是,在生产环境中安装适配器后,可能无法使用运行时调试来隔离问题。 如果在整个适配器中启用了跟踪,则可以使用它来隔离发生故障的位置。
如需了解更多详细信息,请参阅使用 WCF LOB 适配器 SDK 跟踪适配器。
对频繁更改的设置使用 URI 属性
在决定是否将自定义属性公开为绑定或 URI 属性时,如果值经常更改,建议使用 URI 属性。 绑定属性应保留为很少更改的值。
示例绑定属性是所有连接使用的数据库服务器名称。 示例 URI 属性是特定连接要使用的特定表或存储过程。
不要在 URI 中传递用户名或密码值
如果适配器需要调用方凭据,建议使用 ClientCredentials 类检索凭据值,而不是将客户端凭据作为 URI 的一部分传递。 ClientCredentials 类是 WCF 的标准功能,旨在以更安全的方式将凭据信息从客户端传递到服务。 在传输过程中,将用户信息作为 URI 字符串的一部分传递可能会公开用户信息。
下表显示了传递凭据的建议方法。
方法 | DESCRIPTION |
---|---|
设计时间 | 使用“添加适配器服务引用插件”时,可以指定适配器支持的客户端凭据类型。 |
运行时 | 使用生成的 .NET CLR 代理时,可以编程方式设置客户端凭据。static void Main(string[] args) { EchoServiceClient client = new EchoServiceClient(); client.ClientCredentials.UserName.UserName = "TestUser"; client.ClientCredentials.UserName.Password = "TestPassword"; string response=client.EchoString("Test String"); } 或者,如果需要直接与通道交互,可以使用 WCF 通道模型在创建通道工厂时指定客户端凭据。 EchoAdapterBinding binding = new EchoAdapterBinding(); binding.Count = 3; ClientCredentials clientCredentials = new ClientCredentials(); clientCredentials.UserName.UserName = "TestUser"; clientCredentials.UserName.Password = "TestPassword"; BindingParameterCollection bindingParms = new BindingParameterCollection(); bindingParms.Add(clientCredentials); EndpointAddress address = new EndpointAddress("echo://"); IChannelFactory<IRequestChannel> requestChannelFactory = binding.BuildChannelFactory<IRequestChannel>(bindingParms); requestChannelFactory.Open(); |
WCF 配置 | 在客户端配置文件中,添加一个<endpointBehaviors>元素,其中包含<clientCredentials>。<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.serviceModel> . . . . . <behaviors> <endpointBehaviors> <behavior name="clientEndpointCredential"> <clientCredentials> <windows allowNtlm="false" allowedImpersonationLevel="Delegation" /> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration> |
使用 BizTalk | 使用 WCF 适配器使用适配器时,可以在“行为”选项卡上添加 clientCredentials 行为扩展。添加后,可以在终结点行为中设置所需的客户端凭据。 |
不要返回“StrongDataSetType”和“WeakDataSetType”
如果适配器返回DataSet
,请选择使用Microsoft.ServiceModel.Channels.Common.QualifiedType.StrongDataSetType%2A
或Microsoft.ServiceModel.Channels.Common.QualifiedType.WeakDataSetType%2A
,但不要同时使用两者。 这两种类型生成的根节点名称和命名空间是相同的,并且不能同时存在于 WSDL 中。
虽然WeakDataSetType
和StrongDataSetType
都表示System.Data.DataSet
,但StrongDataSetType
在 .NET 应用程序中更易于使用,因为生成的代理显示为System.Data.Dataset
。
WeakDataSetType
生成的代理是XmlElement[]
,在 .NET 应用程序中更难使用。 BizTalk Server 无法使用从 StrongDataSet
中返回的架构,但可以使用 WeakDataSetType
。
注释
StrongDataSetType
并 WeakDataSetType
仅控制客户端应用程序如何解释适配器传递的 XML 消息。 无论指定了哪种类型,XML 消息都是相同的。
注释
返回StrongDataSetType
时,必须将Microsoft.ServiceModel.Channels.Common.MetadataSettings.CompileWsdl%2A
设置为false
。 设置为 true
时,XmlSchemaSet::Compile
会在适配器中调用,以确保 WSDL 中没有错误,但StrongDataSetType
生成的架构在XmlSchemaSet
中产生异常。
将CompileWsdl
设置为false
会绕过适配器内的 WSDL 架构验证,而在代理生成期间会进行验证。 svcutil.exe 等实用工具能够同时为 StrongDataSetType
和 WeakDataSetType
生成代理。
若要同时处理 BizTalk 和 .NET 环境,请考虑实现一个绑定属性,该属性允许根据环境在两个返回类型之间进行切换。
internal static QualifiedType GetDataSetQualifiedType(MyAdapterBindingProperties bindingProperties)
{
if (bindingProperties.EnableBizTalkCompatibility)
return QualifiedType.WeakDataSetType;
else
return QualifiedType.StrongDataSetType;
}
在 BizTalk Server 中创建有意义的 XSD 架构名称
在使用适配器服务 BizTalk 项目外接程序设计时工具时,BizTalk 项目中生成的 XSD 架构的名称是使用DefaultXsdFileNamePrefix
属性、WSDL 中的fileNameHint
批注以及(如需要)唯一的整数值创建的。
例如,如果 DefaultXsdFileNamePrefix
设置为“MyAdapter”,并且 fileNameHint
批注设置为“Stream”,则创建的 XSD 架构名为 MyAdapterStream.xsd。
<xs:schema elementFormDefault='qualified' targetNamespace='http://schemas.microsoft.com/Message' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:tns='http://schemas.microsoft.com/Message'>
<xs:annotation>
<xs:appinfo>
<fileNameHint xmlns='http://schemas.microsoft.com/servicemodel/adapters/metadata/xsd'>Stream</fileNameHint>
</xs:appinfo>
</xs:annotation>
<xs:simpleType name='StreamBody'>
<xs:restriction base='xs:base64Binary' />
</xs:simpleType>
</xs:schema>
注释
默认值 DefaultXsdFileNamePrefix
为绑定的名称。 若要指定其他值,请在派生自Microsoft.ServiceModel.Channels.Common.AdapterBinding
的 Adapter 类中重写DefaultXsdFileNamePrefix
。
有两种可能的方法可以将 fileNameHint
批注添加到架构中:重写 OperationMetadata\TypeMetadata 上的 Export...Schema 方法或重写适配器的 IWsdlRetrieval 实现。 对于任一方法,都可以调用基实现,然后将批注添加到架构集合中的架构。
注释
重写导出…架构方法时,同一架构中可能存在多个操作/类型定义。适配器应确保同一架构中注释中的多个 fileNameHints
不会发生冲突。 Consume Adapter Service 外接程序将在架构中出现多次时,优先使用首次出现的 fileNameHint
。
在以下示例中,IWsdlRetrieval 用于将 fileNameHint
批注添加到 WSDL。
sealed class MyAdapterWsdlRetrieval : IWsdlRetrieval
{
IWsdlRetrieval mBaseWsdlRetrieval;
public MyAdapterWsdlRetrieval(IWsdlRetrieval baseWsdlRetrieval)
{
mBaseWsdlRetrieval = baseWsdlRetrieval;
}
ServiceDescription IWsdlRetrieval.GetWsdl(Microsoft.ServiceModel.Channels.MetadataRetrievalNode[] nodes, Uri uri, TimeSpan timeout)
{
ServiceDescription baseDesc = mBaseWsdlRetrieval.GetWsdl(nodes, uri, timeout);
foreach (XmlSchema schema in baseDesc.Types.Schemas)
{
CreateFileNameHint(schema);
}
return baseDesc;
}
void CreateFileNameHint(XmlSchema schema)
{
string targetNamespace = schema.TargetNamespace;
if (string.IsNullOrEmpty(targetNamespace))
return;
string fileNameHint = null;
//determine the filename based on namespace
if (targetNamespace.StartsWith("myadapter:// adapters.samples.myadaptpter/HelloWorld"))
{
fileNameHint = "HelloWorld";
}
if (targetNamespace.StartsWith("myadapter:// adapters.samples.myadapter/Hello"))
{
fileNameHint = "Hello";
}
//create the annotation and populate it with fileNameHint
XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
XmlSchemaAppInfo appInfo = new XmlSchemaAppInfo();
XmlDocument doc = new XmlDocument();
XmlNode[] fileNameHintNodes = new XmlNode[1];
fileNameHintNodes[0] = doc.CreateElement(null, "fileNameHint", "http://schemas.microsoft.com/servicemodel/adapters/metadata/xsd");
fileNameHintNodes[0].AppendChild(doc.CreateTextNode(fileNameHint));
appInfo.Markup = fileNameHintNodes;
annotation.Items.Add(appInfo);
schema.Items.Insert(0, annotation);
}
在处理过程中不要修改 AdapterEnvironmentSettings
适配器应仅在适配器初始化期间设置 AdapterEnvironmentSettings、 ConnectionPoolManager、 ConnectionPool 和 CommonCacheSize ,并且不应尝试修改正在运行的实例的值。
如果在当前运行的适配器实例上修改了这些设置,这可能会导致新连接覆盖当前正在执行的连接的配置设置。