Managing State

In the preceding section, titled Sending and Receiving Messages, you saw the basic code requirements for handling messages. Messages are used for requests or responses dealing with state. Sometimes, it may be necessary to change the state associated with a service. In this section, we will look at the ways you can manage state.

Posting Data to an Internal Port

In the last section, the Get operation was used to retrieve the state for a service. The state was posted to the main port for the service, which resulted in a SOAP message appearing in the Internet browser. Alternatively, state can be posted to an internal port. This is useful in cases in which data needs to be collected and saved until it is requested. Such data can be stored in a privately held version of the state. The state will only be maintained as long as the service is running.

To understand how this might work, consider a continually operating service that is responsible for monitoring a network port for all incoming HTTP requests. When a request is received, it could temporarily store information about the request on an internal port. The state data would not be posted to the main port until it is requested by a user or another service.

Another example is a long-running service that is responsible for reading a serial port or parallel port and storing the data returned in a privately held state. The data would only be posted to the main port when it was requested.

To post data to an internal port, you need to declare state variables to store the internal data. You also need to declare an additional port, such as in the following code where a port named MyInternalPort is created:

Port<string> MyInternalPort = new Port<string>();

You then need to create a new message that is derived from the Update generic class. You will also create a new operation type and service handler for the operation type you are creating. This new operation type will be added to the list of operations supported for the main PortSet. For example, a service that supported an operation named GetURL would have a main operations port that looked like the following:

[ServicePort()]
public class MonitorRequestsOperations : PortSet<DsspDefaultLookup,
        DsspDefaultDrop, Get, GetURL>
{
}

You will also need to add an exclusive message handler for the new message. This is where you would add code to poll the HTTP port for requests. To post data to an internal port, you will need to utilize the Concurrency and Coordination Runtime (CCR) Arbiter class. The CCR is necessary to avoid conflicts when handling inbound requests. The Arbiter.Receive method is used to create a single item receiver. For CCR to manage the receiver task, it needs to be wrapped inside a call to the DsspServiceBase.Activate() method. For example, the following code can be used to post a string to the port named MyInternalPort:

MyInternalPort.Post(message);
Activate(Arbiter.Receive(true, MyInternalPort, MyInternalPortHandler));

Saving State to a File

In some cases, it may be beneficial to save the state of a service to a file so it can be retrieved later. This can be useful when you want to save settings to a configuration file. State can be persisted with the use of an Initial State partner. A partner is declared by using a Partner attribute. Services can have more than one partner or no partners declared.

Partner

A partner represents a service that is tied to another service. Partnerships are established to inform the MSRS runtime about relationships and possible dependencies between services. Some partnerships, such as ConstructorService, are generated automatically when you compile your service.

You can see whether a service is associated with a partner by selecting the Service Directory from the System Services Web page (see Figure 2-4). Each partner associated with a service will be listed in the Partners’s column for the service instance. For example, the service named ServiceTutorial3 is associated with the following three services:

Service Instance Directory lists all services currently running on the node. From here you can see a list of partners associated with the service.

Figure 2-4. Service Instance Directory lists all services currently running on the node. From here you can see a list of partners associated with the service.

  • ConstructorService. This is used to initiate a new service for services that use the Create operation. A new instance of the ConstructorService will be created automatically for each service using a GUID (this is created at compile time).

  • PartnerListService. The Partner List Manager service is used to return a list of valid partners. This partner will be listed for all services regardless of whether a partner is included.

  • StateServiceThis service is used when there is a need to manage state. It is not necessary to include this service in the list of partners if state is only returned and not saved.

DSS allows the use of a specific kind of partner known as the Initial State partner. This type of partner is used to set the initial value of the state based on values retrieved from a state document. For example, the following code uses the InitialStatePartner attribute to declare a partner that will save the state to a file named MyState.Config.xml:

[InitialStatePartner(Optional = true, ServiceUri = "MyState.Config.xml")]
private MyServiceState _state = new MyServiceState()

Because the partner was declared with the optional tag, the service will start even if the MyState.Config.xml does not exist. The state document should reside in the store directory beneath the MSRS installation folder.

If the state does not already exist, then you need to add code to the Start() method. This method will execute every time the service is started. If no state document exists, then the code should set initial values for all state variables. For example, the following Start() method could be used to initialize the state for the DssService1 service created earlier:

protected override void Start()
{
 //Check to see if the state already exists.
 //If not, then we will initialize it
 if (_state == null)
 {
      _state = new DssService1State();
      _state.Message = "";
 }

 //Start the service
 base.Start();

}

In the example above, we were dealing with one state variable because only one variable was marked with the DataMember attribute. The state for a service can consist of multiple variables of differing types.

To save the state, you will need to add code that saves the state to the MyService1.config.xml file. The SaveState() method is part of the DSS service model, and it is available to your service application within the following .NET namespace:

using Microsoft.Dss.ServiceModel.DsspServiceBase;

You can reference methods in the DsspServiceBase by using the base reference, which represents the base class for service implementations. Code that saves the state should be added to an area of code in which the state changes value. You will then invoke the SaveState() method through the base reference, such as the following:

base.SaveState(_state);

Saving state can be useful when there is a need to maintain state data in between calls to a service. To see a step-by-step example of saving state to a file, refer to Service Tutorial 3, which is available at http://msdn2.microsoft.com/library/bb483063.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.196.175