Click here to Skip to main content
15,880,364 members
Articles / Programming Languages / C# 4.0

Service Warning in WCF using Determined Interaction Pattern - Part 2

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
30 Sep 2010CC (ASA 2.5)3 min read 15.4K   5  
This article explains how to generate, convey and handle warnings in SOA system.

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.

C#
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.

C#
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.

Domain Objects

The Patient class contains the PrescribeDrug() method which is our point of interest. Let us see its implementation.

Exploring PrescribeDrug() Domain Method

C#
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
			}
		}
	}

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:

C#
[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():

C#
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.

RxPoint Console

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.

Warning Message

If you press Yes, the warning will be overridden and you will get the success message. Let us see the service invocation code.

C#
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.

XML
<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

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License


Written By
Architect Aditi
India India
Working as Architect for Aditi, Chennai, India.

My Website: www.udooz.net

My Blog: www.udooz.net/blog

Comments and Discussions

 
-- There are no messages in this forum --