使用确定的交互模式在 WCF 中实现服务警告 - 第二部分





0/5 (0投票)
本文介绍了如何在 SOA 系统中生成、传递和处理警告。
可以在以下网址下载本文的完整源代码:http://www.udooz.net/file-drive/doc_download/16-determinedinteraction.html。
第二部分简介
希望您来自本文的 第一部分。在第一部分中,我们已经了解了确定性交互中涉及的核心对象,并以 ObjectBytifier
类中的 BytifyBaseObjects()
的解释结束了本文。在这一部分中,我将解释服务层中的其余组件,以及解释著名的药物领域场景药物处方的示例。
探索字符串化属性
让我们看看 ObjectBytifier
的 Stringified
属性。
public string Stringified
{
get
{
byte[] bytified = Bytified;
int length = bytified.Length;
StringBuilder stringified = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
stringified.Append(bytified[i].ToString("X2"));
}
return stringified.ToString();
}
}
_bytified
字段中的每个字节都以十六进制字符串的形式存储在字符串缓冲区中。
探索自定义 MessageInspector
接下来我们需要做的是初始化 Acknowledgement
,并在每次发出请求时将其放入 WCF 操作上下文中,以便它可以在域层中公开。最好的方法是实现 System.ServiceModel.Dispatcher.IDispatchMessageInspector
。让我们看看它的 AfterReceiveRequest
的实现。
public object AfterReceiveRequest
(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext)
{
Acknowledgement ack = new Acknowledgement();
ack.Status = 200;
OperationContext.Current.Extensions.Add(ack);
return null;
}
为了将 Acknowledgement
放入 OperationContext
中,它实现了 System.ServiceModel.IExtension<T>
。在 BeforeSendReply()
中,它已从上下文中删除。我没有在本文中故意解释如何使其可用于 WCF 端点。您可以参考示例代码了解详细信息。
域对象
下面的类图显示了我在本例中使用的主要域对象。请注意,这是一个高度简化的域模型,实际模型将具有更多的细节,与其他域对象的关系。

Patient
类包含 PrescribeDrug()
方法,这是我们感兴趣的点。让我们看看它的实现。
探索 PrescribeDrug() 域方法
public void PrescribeDrug(List<Prescription> prescriptions)
{
foreach (Prescription admPrescription in prescriptions)
{
if (admPrescription.Drug.Name == "Lepirudin")
{
var admObjects = new List<Prescription>();
admObjects.Add(admPrescription);
if (Acknowledgement.AddWarning
(12345,
"Drug Lepiruding is allergy to this patient",
admObjects.ToArray(), new object[] {ID}) ==
WarningElevation.Admonish)
{
return;
}
else
{
//Do Prescribe all the drugs
}
}
}
此方法至少需要一个处方。在这种情况下,为了简单起见,我只是检查了如果药物是 "Lepirudin
",则抛出药物过敏警告。请注意,我还将患者添加到 AddWarning()
的其他完整性参数中。如果结果是 Admonish
,则执行已停止,否则继续执行。现在是时候查看服务合同及其在服务层中的实现了。
服务合同及其实现
下面的代码显示了服务合同定义
[ServiceContract(Namespace =
"http://www.udooz.net/samples/services",
Name = "DrugPrescriptionService")]
public interface IDrugPrescriptionService
{
[OperationContract]
PrescribeDrugResponse PrescribeDrug
(PrescribeDrugRequest request);
}
[MessageContract]
public class PrescribeDrugRequest
{
[MessageBodyMember]
public string PatientID;
[MessageBodyMember]
public IList<PrescriptionDTO> PrescriptionDetails;
}
[MessageContract]
public class PrescribeDrugResponse
{
[MessageBodyMember]
public Acknowledgement Acknowledgement;
}
[DataContract(Namespace =
"http://www.udooz.net/samples/services/types",
Name = "PrescriptionDTO")]
public class PrescriptionDTO
{
[DataMember]
public DateTime PrescribedAt;
[DataMember]
public string Code;
[DataMember]
public string Amount;
[DataMember]
public string Frequency;
[DataMember]
public string PrescribedBy;
[DataMember]
public string CorrelationState;
}
对于 PrescribeDrug()
操作,请求消息包含患者 ID 和处方 DTO(数据传输对象)的集合。确认已作为响应消息的一部分返回。让我们看看 PrescribeDrug()
的实现
public PrescribeDrugResponse PrescribeDrug
(PrescribeDrugRequest request)
{
Patient p = new Patient();
p.ID = request.PatientID;
List<Prescription> prescriptions = new List<Prescription>();
foreach (PrescriptionDTO dto in request.PrescriptionDetails)
{
prescriptions.Add(dto.ToDomain());
}
p.PrescribeDrug(prescriptions);
PrescribeDrugResponse response = new PrescribeDrugResponse();
response.Acknowledgement = Acknowledgement.Current;
return response;
}
Patient
域对象已被实例化,随后通过其扩展方法 ToDomain()
将适当的值从 PrescriptionDTO
分配到 Prescription
域对象中。在响应中,已提取并传递来自操作上下文的 Acknowledgement
。我跳过了 ToDomain()
的解释,但您可以参考源代码。
消费者层中的所有内容
我创建了一个 Winform 应用程序,允许为患者开一种或多种药物。请参见下面的 UI 图片。

我列出了药物的 CAS 编号,而不是药物名称。选择的药物是 "Lepirudin
"。首次单击“处方”按钮时,您将从服务层收到以下警报。

如果按“是”,警告将被覆盖,您将收到成功消息。让我们看看服务调用代码。
List<PrescriptionDTO> prescriptions =
new List<PrescriptionDTO>
(prescriptionSource.Cast<PrescriptionDTO>());
bool canContinue = true;
do
{
Acknowledgement ack =
proxy.PrescribeDrug(txtPatientID.Text, prescriptions);
if (ack.Status == 200)
{
MessageBox.Show("Successfully prescribed");
canContinue = false;
}
else
{
Warning w = ack.Warnings[0];
if (MessageBox.Show(w.Message + ".
Do you want to override?", w.Code.ToString(),
MessageBoxButtons.YesNo)
== System.Windows.Forms.DialogResult.Yes)
{
foreach (PrescriptionDTO p in prescriptions)
p.CorrelationState = w.CorrelationState;
canContinue = true;
}
else canContinue = false;
}
}while(canContinue);
在第一次调用时,如果确认状态为 200
,它只会显示处方药。否则,它会显示警告消息。根据消费者的决定,它将关联状态附加到适当的处方 DTO 并调用服务。为了清楚起见,我显示了确定性交互的请求和响应纯 SOAP 消息。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1"
xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">
http://www.udooz.net/samples/services/DrugPrescriptionService/PrescribeDrug
</Action>
</s:Header>
<s:Body>
<PrescribeDrugRequest xmlns="http://www.udooz.net/samples/services">
<PatientID>IN1616</PatientID>
<PrescriptionDetails
xmlns:d4p1="http://www.udooz.net/samples/services/types"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:PrescriptionDTO>
<d4p1:Amount>2</d4p1:Amount>
<d4p1:Code>120993-53-5</d4p1:Code>
<d4p1:CorrelationState>
D0331F000AF15C8C1F554722FC4D2B3C
</d4p1:CorrelationState>
<d4p1:Frequency>1/day</d4p1:Frequency>
<d4p1:PrescribedAt>
2010-09-30T20:58:37.4970704+05:30
</d4p1:PrescribedAt>
<d4p1:PrescribedBy>udooz</d4p1:PrescribedBy>
</d4p1:PrescriptionDTO>
</PrescriptionDetails>
</PrescribeDrugRequest>
</s:Body>
</s:Envelope>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<PrescribeDrugResponse
xmlns="http://www.udooz.net/samples/services">
<Acknowledgement
xmlns:a="http://schemas.datacontract.org/2004/07/Udooz.Samples.Core"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Status>200</a:Status>
<a:TimeStamp>0001-01-01T00:00:00</a:TimeStamp>
<a:Warnings i:nil="true"
xmlns:b="http://schemas.datacontract.org/2004/07/Udooz.Samples" />
</Acknowledgement>
</PrescribeDrugResponse>
</s:Body>
</s:Envelope>
历史
- 2010 年 9 月 30 日:初次发布