DCSIMG

 Subscribe in a reader

February 2008 - Posts - Guy kolbis

February 2008 - Posts

The title is a bit confusing. So, in order to solve that, let me explain the meaning of it.

Lets say I have a Client - Server application.

The server is hosted in a console application and can perform several operations that are requested from a client application.

The client application is a windows application that sends messages to the server using WCF.

One thing that I think is extremely important when developing such applications is the acknowledgement process. In other words, I want to know that the operation I invoked from the client against the server completed successfully or failed.

In order to do so,  I would like the server to send the client an acknowledgement upon completion. There are several ways of doing so, however I will describe my way.

I have selected to create a service that will be exposed from the client and will be hosted in the windows application. The server will add a service reference to it and will be able to report the client of its progress. So far everything is pretty straight forward.

The code should look something like this:

[ServiceContract(Namespace = "http://www.QTech.co.il/08/01/20")]
public interface IAknowledgemnt
{
    [OperationContract]
    void Aknowledge(AknowledgeInfo aknowledge);
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class AknowledgemntService : IAknowledgemnt
{
    #region IAknowledgemnt Members

    public void Aknowledge(AknowledgeInfo aknowledge)
    {

    }

    #endregion
}

The server will add a service reference and will send messages to the AknowledgementService. The challenge is to upon receiving the message do some GUI related operations, such as displaying progress bar or adding the message into a log control or simply pop up a message box.

In order to achieve that, I will need to alert the GUI. To do so, I will use static events and will modify the AknowledgementService as follows:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class AknowledgemntService : IAknowledgemnt
{
    private static EventHandlerList _eventHandlers = new EventHandlerList();

    private static readonly object _dataArriveKey = new object();

    public static event DataArriveEventHandler DataArrive
    {
        add
        {
            _eventHandlers.AddHandler(_dataArriveKey, value);
        }
        remove
        {
            _eventHandlers.RemoveHandler(_dataArriveKey, value);
        }
    }

    internal void OnDataArrive(AknowledgeInfo aknowledge)
    {
        DataArriveEventHandler myEvent = _eventHandlers[_dataArriveKey] as DataArriveEventHandler;
        if (myEvent != null)
            myEvent(null, new DataArriveEventArgs(aknowledge));
    }

    #region IAknowledgemnt Members

    public void Aknowledge(AknowledgeInfo aknowledge)
    {
        OnDataArrive(aknowledge);
    }

    #endregion
}

public delegate void DataArriveEventHandler(object sender, DataArriveEventArgs e);

public class DataArriveEventArgs : EventArgs
{
    private AknowledgeInfo _aknowledge;
    public string Aknowledge
    {
        get { return _aknowledge; }
        set
        {
            _aknowledge = value;
        }
    }

    public DataArriveEventArgs(AknowledgeInfo aknowledge)
    {
        _aknowledge = aknowledge;
    }
}

The addition to the code is with regards to the events. Once a new message arrives from the server I will fire the event to all whom are registered to it.

We have one more peace to add to the passel and it is the consuming of the service from the client. The client must register to the event, right? So here is the basic concept for that:

public partial class CtrlAknowledge : UserControl
{
    AknowledgemntService _aknowledgemntService;

    public CtrlAknowledge()
    {
        InitializeComponent();

        _aknowledgemntService = new AknowledgemntService();
        AknowledgemntService.DataArrive += new DataArriveEventHandler(OnDataArrive);
    }

    void OnDataArrive(object sender, DataArriveEventArgs e)
    {
        MessageBox.Show("OK");
    }
}

By adding your own custom logic to the OnDataArrive method, you can display the message arrived from the server on to the GUI.

kick it on DotNetKicks.com

I know that those of you who visited the DevAcademy2 had a problem selecting the lectures to attend. So, I have been asked to repeat my presentation on:

clip_image002

"Application Performance Analysis With VSTS"

However, this time I have much more time.... :)

So, I am going to take my presentation and expend it further. So, what is on our menu?

  1. PDD (Performance Driven Development).
  2. Profiling our application with Sampling and Instrumentation.
  3. Investigate memory allocation and objects lifetime.
  4. Collecting and Understanding System and Application Performance Data.
  5. Load Test our DAL for performance issues and troubleshooting.
  6. Tips & Tricks.

So, You are all invited for my session!

When is it? This Monday, the 11th at 17:30.

Where is it? Microsoft offices.

Here is a link yo subscribe to the event:

http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032368228&Culture=he-IL

See you there!

Service Oriented Architecture contains 4 tenets:

  1. Boundaries are Explicit
  2. Services Are Autonomous
  3. Services share schema and contract, not class
  4. Service compatibility Is based upon policy

When someone asks me if he should follow SOA tenets in his design I usually say:

"It depends...."

And next thing I find myself in, is a long discussion about the pros and cons for going with SOA. In the following post I will provide an argument why I think that SOA is not always the best way to go with.

To present my argument and result, I will use an example which most of you probably encountered at least once.

I have a solution that contains six projects as describes here:

  1. WCFServices1
  2. WCFServices2
  3. Contracts
  4. Common
  5. Host
  6. Client

image

The two WCF services Service1 and Service2 implement the same contract as defined in the Contract project. Here is a code snippet for that contact:

[ServiceContract(Namespace="http://blogs.microsoft.co.il/blogs/kolbis/06/02/08")]
public interface ICustomerService
{
    [OperationContract]
    Customer GetCustomer(int id);
}

The contract references an assembly named Common that contains a class named Customer which is returned as a result for the GetCustomer OperationContract.

The customer class is decorated with a DataContract attribute as shown in the following code snippet:

[DataContract(Namespace="http://blogs.microsoft.co.il/blogs/kolbis/06/02/08")]
public class Customer
{
    [DataMember(IsRequired = true, Order = 0)]
    public int Id { get; set; }
    [DataMember(IsRequired = true, Order = 1)]
    public string LastName { get; set; }
    [DataMember(IsRequired=true,Order=2)]
    public string FirstName { get; set; }
}

So far everything is simple.

Now, I have added a console host that will host the two services:

static void Main(string[] args)
{
    ServiceHost host1 = new ServiceHost(typeof(WCFServices1.Service1));
    ServiceHost host2 = new ServiceHost(typeof(WCFServices2.Service1));

    host1.Open();
    host2.Open();

    Console.ReadLine();

    host1.Close();
    host2.Close();

Now I wanted to add the client that will consume the services.

Here is what I usually do when I need to add a service:

  • Run the host in a non debug mode (Ctrl + F5).
  • In the client application select to add a service reference:

image

  • I need to add both services:

image

Here is the result:

image 

Great! we have set our service proxies.

If we will look inside the generated proxy, there is an automated file that was generated for us named Reference.cs:

image

That file contains a definition for the Customer class that was defined in the Common project as you can see in the following code snippet:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Customer", Namespace="http://blogs.microsoft.co.il/blogs/kolbis/06/02/08
[System.SerializableAttribute()]
public partial class Customer : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
    private int IdField;
    private string LastNameField;
    private string FirstNameField;
    [global::System.ComponentModel.BrowsableAttribute(false)]
    public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
        get {
            return this.extensionDataField;
        }
        set {
            this.extensionDataField = value;
        }
    }
    [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
    public int Id {
        get {
            return this.IdField;
        }
        set {
            if ((this.IdField.Equals(value) != true)) {
                this.IdField = value;
                this.RaisePropertyChanged("Id");
            }
        }
    }
    [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
    public string LastName {
        get {
            return this.LastNameField;
        }
        set {
            if ((object.ReferenceEquals(this.LastNameField, value) != true)) {
                this.LastNameField = value;
                this.RaisePropertyChanged("LastName");
            }
        }
    }
    [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=2)]
    public string FirstName {
        get {
            return this.FirstNameField;
        }
        set {
            if ((object.ReferenceEquals(this.FirstNameField, value) != true)) {
                this.FirstNameField = value;
                this.RaisePropertyChanged("FirstName");
            }
        }
    }
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName) {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null)) {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

It is important to explain here that the default behavior for adding a service reference is to create a those referenced objects  from the schema. This is a NEW definition for the class Customer. This way we implement the "Share schema, not classes" and the "Services are Autonomous" SOA tenets.

Here is where it gets tricky. Lets assume that we want to use the customer object we have got from the services:

Service1Reference.CustomerServiceClient proxy1 = new
    Client.Service1Reference.CustomerServiceClient();

Service2Reference.CustomerServiceClient proxy2 = new
    Client.Service2Reference.CustomerServiceClient();

Service1Reference.Customer cust1 = proxy1.GetCustomer(1);
Service2Reference.Customer cust2 = proxy1.GetCustomer(2);

I have highlighted the two "problematic" rows. We actually get two different objects, one from the definition in the Services1 and another from the Services2.

Which one should we use?

So, there are two options here:

  1. Create a mapper class that will map from one type to the other.
  2. Not share a schema rather share a class.

There are pros and cons to both methods, but you should be aware of both. Using the mapper will force you to write more code and mantain it but you will implement SOA tenets. If you select the second method you will surely write less code but you will ignore the SOA tenets.

Here is what you need to do to implement the seconds method:

When adding editing the service reference there is an Advanced button:

image

Clicking on it will open the advanced options dialog:

image

I am not going to talk about each option, rather focus on the Reuse types in referenced assemblies option. Basically it determines whether a WCF client will try to reuse that already exist in referenced assemblies instead of generating new types when a service is added or updated. By default, this option is checked.

There are two check boxes available:

  1. Reuse types in all referenced assemblies - When selected, all types in the Referenced assemblies list will be reused if possible. By default, this option is selected.
  2. Reuse types in specified referenced assemblies -  When selected, only the selected types in the Referenced assemblies list will be reused.

We will select the seconds option, but before continuing we will add a reference to the Common Project In order to use only a one definition of Customer. Now in the Advanced Dialog we will do the following:

image

Select the Reuse types in specified referenced assemblies and check the Common. The same goes with Service2. If you will look inside the Reference.cs class that was generated for us, you will see that the Customer definition was removed. The only definition we have is the one in the common:

Service1Reference.CustomerServiceClient proxy1 = new
    Client.Service1Reference.CustomerServiceClient();

Service2Reference.CustomerServiceClient proxy2 = new
    Client.Service2Reference.CustomerServiceClient();

Common.Customer cust1 = proxy1.GetCustomer(1);
Common.Customer cust2 = proxy1.GetCustomer(2);

Now, we have share a class and not the schema thus we have broke SOA but we wrote less code.

You can download the code I used here.

kick it on DotNetKicks.com

In Visual Studio Team Tester you can create a load test. The load test contains al least one scenario. In each scenario you define the think time, the number of users, the patterns and etc.

It is possible to create multiple scenarios per load test. Doing so it simple, all you need to do is right click on the scenario folder and select "Add Scenario", then follow the wizard and you have got yourself a load test with two scenarios.

image

So, the question is why would you do that? why do you need multiple scenarios per load test?

The main reason for that is model user load for different groups. To explain that I will use an example. Lets say that you have a certain number of users executing one set of tests and another set of users executing another set of tests. To further explain it lets assume that we have two groups for our web site, administrators and guests. You are more likely to have more guests then administrators and the targeted tests should also vary. So, to solve that issue you will create two scenarios one for the administrator group and the other for the guest.