The complete source code for this article can be downloaded at http://www.udooz.net/file-drive/doc_download/16-determinedinteraction.html.
Introduction to Part 2
Hope you are coming from part 1 of this article. In the first part, we have seen the core objects involved in determined interaction and ended the article with the explanation of BytifyBaseObjects()
in ObjectBytifier
class. In this part, I explain the remaining components in the service layer and the sample explaining the famous medication domain scenario drug prescription.
Exploring Stringified Property
Let us see the Stringified
property of ObjectBytifier
.
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();
}
}
Every byte in the _bytified
field is getting stored as hex string in the string buffer.
Exploring Custom MessageInspector
The next thing we need to do is initialize Acknowledgement
and put into WCF operation context whenever a request made, so that it will be exposed in the domain layer. The best place to do is implementing System.ServiceModel.Dispatcher.IDispatchMessageInspector
. Let us see the implementation of its 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;
}
In order to place the Acknowledgement
into OperationContext
, it is implemented System.ServiceModel.IExtension<T>
. In the BeforeSendReply()
, this has been removed from the context. I haven't explained how to make this to be available to the WCF endpoint intentionally in this article. You can refer to the sample code for details.
Domain Objects
The below class diagram shows the main domain objects I've used in this example. Note that this is a highly simplified domain model, the actual model would have much more details, relationships with other domain objects.
The Patient
class contains the PrescribeDrug()
method which is our point of interest. Let us see its implementation.
Exploring PrescribeDrug() Domain Method
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
{
}
}
}
This method requires at least one prescription. In this case, for the sake of simplicity, I've just checked that if the drug is "Lepirudin
", then throw drug allergic warning. Note that I've also added the patient in other integrity parameter of AddWarning()
. If the result is Admonish
, then the execution has been halted, otherwise proceed further. This is the time to see the service contract and its implementation in service layer.
Service Contract and its Implementation
The below code shows the service contract definition:
[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;
}
For PrescribeDrug()
operation, the request message contains patient id and collection of prescription DTOs (data transfer object). Acknowledgement has been returned as part of the response message. Let us see the implementation of 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;
}
The Patient
domain object has been instantiated followed by assigning appropriate values into Prescription
domain objects from PrescriptionDTO
by its extension method ToDomain()
. In the response, Acknowledgement
from the operation context has been picked up and conveyed. I skipped the explanation of ToDomain()
, but you can refer to the source code.
All in the Consumer Layer
I've created a Winform application which allows to prescribe one or more drugs for a patient. See the below picture for the UI.
Instead of drug name, I've listed drug's CAS number. The selected one is for drug "Lepirudin
". When you click the Prescribe button, the first time, you will get the following alert from the service layer.
If you press Yes, the warning will be overridden and you will get the success message. Let us see the service invocation code.
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);
In the first time invocation, if the acknowledgement status is 200
, it simply shows drugs prescribed. Otherwise, it shows the warning message. Based on the consumer decision, it attaches the correlation state to appropriate prescription DTO and invokes the service. For your clarity, I've shown the request and response plain SOAP messages of determined interaction.
<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>
History
- 30th September, 2010: Initial post