jfbeaulieu.com

... Pushing the limits of programming

  • Increase font size
  • Default font size
  • Decrease font size

How to fetch work item(s) from specific Team Project using the TFS API

E-mail Print PDF

Team Foundation Server

Work Item Tracking with the Team Foundation Server SDK 

Team Foundation 2010 is a complete solution for collaborative software development, offering source control, data collection, reporting and project tracking. TFS is available as a stand-alone software or can be integrated to visual studio, that is Visual Studio Team System (VSTS).


TFS Overview

TFS is comprised of 5 main parts. It's got a project management part, a work item tracking part, a version control part, a reporting part and a team build part. In this article, we are focusing on the work item tracking part.

Most of the activity in TFS revolves around an entity we call a WORK ITEM. A work item is simply a single unit of work that needs to be completed. There are multiple different work item types available to be used depending on the situation and the chosen Microsoft┬« Solution Framework (MSF). More specifically, there are two main MSF choices available:  for Agile Software Development (MSF Agile) or MSF for CMMI┬« Process Improvement (MSF CMMI). For instance, MSF Agile contains the following work item types:

  • Bug. Represents a problem or potential problem in your application.
  • Risk. Represents a possible event or condition that would have a negative impact on your project.
  • Scenario. Represents a single path of user interaction through the system.
  • Task. Represents the need for a member of the team to do some work.
  • Quality of Service Requirement. Represents a requirement constraining how the system should work.

The API

The Team Foundation Server SDK or TFS API, allows a developer to integrate .NET software development with TFS 2010 and allows to explore the extensibility, adaptability and integration characteristics of the various components of TFS.

If you are already familiar with TFS, you already know that on the TFS server, there can be multiple Collections each containing multiple Team Projects. Each of these Team Projects may contains Work Items. Let's begin by posing this problem: 

I am trying to query a single team project in the main TfsTeamProjectCollection which contains 194 Team Projects in total. Each Team Project contains many Work Items. I know exactly how to get a WorkItem individually by Id from a WorkItemStore by using the GetWorkItem method on the WorkItemStore. The thing is, that by doing this, the API queries the TFS database for that particular Work Item. Considering you have to obtain a large list of work items, this approach is way too slow, there must be a better way to do this. Let's start out by coding the basic code we need in order to query TFS, by first creating a WorkItemStore.

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

private static Uri _collectionUri;
private static TfsTeamProjectCollection _projectCollection;
private static WorkItemStore _workItemStore;
private static Microsoft.TeamFoundation.WorkItemTracking.Client.Project _timeSheetProject;

private static Uri CreateCollectionUri()
{
	if (_collectionUri == null)
	{
		// Create a new Collection URI using the TFS_COLLECTION_URI
		_collectionUri = new Uri(TfsCollectionUri);
	}
	return _collectionUri;
}

private static TfsTeamProjectCollection CreateProjectCollection(Uri collectionUri)
{
	// Create a new Team Project Collection
	_projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(collectionUri);

	return _projectCollection;
}

private static WorkItemStore CreateWorkItemStore(TfsTeamProjectCollection projColl)
{
	return new WorkItemStore(projColl);
}
		
public static WorkItemStore GetWorkItemStore
{
	get {
		return _workItemStore ?? (_workItemStore = CreateWorkItemStore(CreateProjectCollection(CreateCollectionUri())));
	}
}

Now that we have the property GetWorkItemStore, which creates a new WorkItemStore if the global variable _workItemStore is null, we can start creating public methods for other classes to call in order to obtain list of Work Items.



public static List<WorkItem> GetWorkItemListForAreaPath(string areaPath)
{
	_workItemStore = GetWorkItemStore;
	WorkItemCollection workItemCollection = _workItemStore.Query(
			" SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
			" FROM WorkItems WHERE [System.TeamProject] = '" + TfsProjectKey + "'" +
			" AND [System.WorkItemType] = '" + TfsWitProject + "'" +
			" AND [System.AreaPath] = '" + areaPath + "'" +
			" ORDER BY [System.Id]");


	var workItemList = new List<WorkItem>();


	foreach (WorkItem wi in workItemCollection)
	{
		workItemList.Add(wi);
	}


	return workItemList;
}


public static List<WorkItem> GetWorkItemListById(string idList)
{
	_workItemStore = GetWorkItemStore;
	WorkItemCollection workItemCollection = _workItemStore.Query(
			" SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] " +
			" FROM WorkItems WHERE [System.TeamProject] = '" + TfsProjectKey + "'" +
			" AND [System.WorkItemType] = '" + TfsWitProject + "'" +
			" AND [System.Id] In (" + idList + ")" +
			" ORDER BY [System.Id]");


	var workItemList = new List<WorkItem>();


	foreach (WorkItem wi in workItemCollection)
	{
		workItemList.Add(wi);
	}


	return workItemList;
}


Since each single call to the Team Foundation Server is costly, you want to limit the amount of times you call the GetWorkItemListById and GetWorkItemListForAreaPath methods. Instead of limiting your query to populate the WorkItemCollection, it's better to obtain more items and to filter the unwanted ones later from the list like so:


protected List<DisplayItemsEntity> PopulateDisplayItemList(List<ReportItems> reportItems)
{
   List<WorkItem> tfsWorkItems = TfsHelper.GetWorkItemListById(GetWorkItemIdListFromReportItems(reportItems));

   return (
	   from item in reportItems
	   let tfsWorkItem = tfsWorkItems.Find(workItem => workItem.Id == item.WorkItemId)
	   select SetCurrentValues(item, tfsWorkItem, null, false)
	   ).ToList();
}

protected String GetWorkItemIdListFromReportItems(List<ReportItems> reportItems)
   {
	   string idList = string.Empty;

	   for (int i = 0; i < reportItems.Count; i++)
	   {
		   idList += reportItems[i].WorkItemId.ToString();
		   if (i < reportItems.Count - 1)
			   idList += ",";
	   }
	   return idList;
   }


What we are doing here is to creating a method called PopulateReportItems in order to display items in a grid for instance. The Report Items are each associated with a Work Item in TFS so in order to set the current values of each Report Items, we have to obtain the corresponding Work Item from TFS. Instead of querying TFS once for each Work Item, which would be a major flaw in performance, it is better to obtain the list of Work Item Ids that we need and then querying TFS with this list of Ids.


Last Updated on Tuesday, 26 March 2013 15:52  

The DAO J2EE pattern

E-mail Print PDF

Java

J2EE

Data Access Object J2EE pattern

In the family of J2EE patterns, there is one pattern that handles especially data access and manipulation. Let's say that you would like to encapsulate all the database access logic in a separate layer, a persistence layer, this would be the job for the DAO pattern! The DAO pattern will manage the connection with the data source to obtain and store data. This is a very cool way to separate low-level data access logic from business logic. The business logic will be in our model of the MVC pattern, a core principle in J2EE. The idea is to build DAO classes that are adapted according to the source and that provide CRUD (create, read, update, delete) operation for each data source.

The DAO pattern is one of the main J2EE design patterns in use today and contains the following elements: A DAO interface or abstract class, one or many concrete DAO classes that implements or extends the DAO interface or abstract class and finally, data transfer objects to transport data to and from clients. The concrete DAO class will contain all of the logic for data access and manipulation from a specific data source. In this article, we will see how we can implement this in J2EE with code example and UML modeling.

Let's take a look at the class diagram representing the DAO pattern's relationships:

DAO pattern

Here is an explanation on each of the entities:

BusinessObject

The BusinessObject represents the data client. It is the object that requires access to the data source to obtain and store data. A BusinessObject may be implemented as a session bean, entity bean, or some other Java object, in addition to a servlet or helper bean that accesses the data source.

DataAccessObject

The DataAccessObject is the primary object of this pattern. The DataAccessObject abstracts the underlying data access implementation for the BusinessObject to enable transparent access to the data source. The BusinessObject also delegates data load and store operations to the DataAccessObject.

DataSource

This represents a data source implementation. A data source could be a database such as an RDBMS, OODBMS, XML repository, flat file system, and so forth. A data source can also be another system (legacy/mainframe), service (B2B service or credit card bureau), or some kind of repository (LDAP).

TransferObject

This represents a Transfer Object used as a data carrier. The DataAccessObject may use a Transfer Object to return data to the client. The DataAccessObject may also receive the data from the client in a Transfer Object to update the data in the data source.

Reference: Core J2EE Patterns

http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html


The MVC pattern

The MVC pattern is an integral part of J2EE and this example is no exception. Here is how we can picture the architecture of a J2EE web application:

MVC Pattern


In this example, our BusinessObject will be a JavaBean from the model of the MVC:

SpectacleBean.java

package model;

import java.sql.SQLException;
import java.util.ArrayList;

import persistance.DAO;
import persistance.SpectacleDAO;

public class SpectacleBean {

    private int id;
    private String nom;
    private String artiste;
    private String photo = "images/nopic.jpg";
    private ArrayList<RepresentationBean> representations = new ArrayList<RepresentationBean>();
    private DAO<SpectacleBean> spectacleDAO = null;
    
    public SpectacleBean(int id, String nom, String artiste) {
        this.id = id;
        this.nom = nom;
        this.artiste = artiste;

        generateRepresentations();
    }

    public String getPhoto() {
        return photo;
    }

    public void setPhoto(String photo) {
        this.photo = photo;
    }

    public String getArtiste() {
        return artiste;
    }

    public void setArtiste(String artiste) {
        this.artiste = artiste;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public int size() {
        return representations.size();
    }

    public RepresentationBean get(int index) {
        return representations.get(index);
    }

    public int getBilletsRestant() {
        int restant = 0;
        for (RepresentationBean r : representations) {
            restant += r.getNbrBilletsDispo();
        }
        return restant;
    }

    public void addRepresentation(RepresentationBean r) {
        representations.add(r);
    }

    private void generateRepresentations() {
        if (spectacleDAO == null) {
            spectacleDAO = new SpectacleDAO();
        }
        try {
            spectacleDAO.find(this);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

As you probably can see, this is a classic JavaBean class with private atributes and public getters and setters. There is a private method called generateRepresentations() that will be used to trigger the database access. First, an object of type DAO is instanciated, then a call on it's find() method is made to obtain results from the database.

The DAO pattern

Moving on, we now have a good idea of what is the model and it's purpose in the J2EE architecture. Quoting Craig Larman in his book "Applying UML and Patterns" : In this design, assume that we will make all persistent object classes extend a PersistenObject class, that provides common technical services for persistence:

Craig Larman










What is the DAO pattern? Once again, the DAO will allow us to separate the high-level business logic layer from the low-level persistence. The persistence layer is, in fact, in our storage system and the business layer corresponds to our Java objects mapped to our base. The DAO pattern is used to add a set of objects whose role will be to: CRUD (create, read, update, delete).

DAO.java

package persistance;

import java.sql.SQLException;

/**
 * DAO (J2EE Pattern)
 *
 * @param object type
 */
public abstract class DAO<T> {
    
    public abstract T find(T obj) throws SQLException;
    
    public abstract T create(T obj) throws SQLException;;

    public abstract void update(T obj) throws SQLException;;

    public abstract void delete(T obj) throws SQLException;;
}

Now, let us create a concrete derived class that will extend the DAO class. This will be the DataAccessObject class from the diagram above.  For accessing data, we will be using JDBC:

SpectacleDAO.java

package persistance;

import java.sql.ResultSet;
import java.sql.SQLException;

import model.RepresentationBean;
import model.SalleBean;
import model.SpectacleBean;
import persistance.DAO;

public class SpectacleDAO extends DAO<SpectacleBean>{

    @Override
    public SpectacleBean create(SpectacleBean spectacle) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void delete(SpectacleBean spectacle) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public SpectacleBean find(SpectacleBean spectacle) throws SQLException {
        RepresentationBean representation = null;
        ResultSet rs = SQLite.getInstance().query(
                "SELECT * FROM representations WHERE id_spectacle=" + spectacle.getId()
                        + "");
        if (rs != null) {
            try {
                while (rs.next()) {
                    representation = new RepresentationBean(rs.getInt("id"));
                    representation.setDate(rs.getString("date"));
                    representation.setPrix(rs.getInt("prix"));
                    representation.setSalle(new SalleBean(rs.getInt("id_salle")));
                    spectacle.addRepresentation(representation);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        rs.close();
        return spectacle;
    }

    @Override
    public void update(SpectacleBean spectacle) {
        // TODO Auto-generated method stub
        
    }
}

As shown above, we have a utility class that is a Singleton named ConnectionSQLite. This represends the DataSource in the diagram above. This is where our connections and statements are going to be made to the database. We are obviously using an SQLite database for this exercise:

SQLite.java

package persistance;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQLite {

    private static Connection conn = null;
    private static Statement stat = null;

    private static SQLite instance = null;
    private static String path = "";

    private SQLite() {
        try {
            Class.forName("org.sqlite.JDBC");
        } catch (ClassNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        ConnectionPool.getInstance();
    }

    public static SQLite getInstance() {
        if (instance == null)
            instance = new SQLite();
        return instance;
    }

    public static void setPath(String p) {
        path = p;
    }

    public static String getPath() {
        return path;
    }

    public ResultSet query(String sql) {
        ResultSet rs = null;
        try {
            rs = SQLite.generateStatement().executeQuery(sql);
        } catch (SQLException e) {
            System.err.println("[SQL]" + e.getMessage());
        }

        if (conn == null) {
            throw new NullPointerException();
        }
        // release the connection back to the pool
        ConnectionPool.release(conn);
        return rs;
    }

    public void update(String sql) {
        try {
            SQLite.generateStatement().executeUpdate(sql);
        } catch (SQLException e) {
            System.err.println("[SQL]" + e.getMessage());
        }
    }

    public static Statement generateStatement() {
        try {
            conn = ConnectionPool.getConnection();
            if (conn != null) {
                stat = conn.createStatement();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        return stat;
    }
}

We did not cover the TransferObject from the class diagram above. This serves as a container that doesn't have any behavior besides storage and retrieval of it's own data. This is also a Core J2EE Pattern, taken from Sun: "...use aTransfer Object to encapsulate the business data. A single method call is used to send and retrieve the Transfer Object".  We have now covered all entities in the DAO Pattern.

Now, from this point, we can observe that another pattern is being used in this context. It is the Object Pool pattern [GoF] and is implemented in the persistence package as well. Basically, a connection pool is used to store connections in an array list and to reuse them when not in use. The connection pool is a Singleton, and is used strictly to give Connection objects to the DataSource.

I will not get into too much detail with the Object Pool pattern, as there are many frameworks out there that already implement this: C3P0, Apache DBCP, etc. Do not reinvent the wheel, but take a look at how the Connection Pool below works to understand the basics of Object Pooling.

ConnectionPool.java

/**
 * The Object Pool Pattern by GoF
 *  
 */
package persistance;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;

public class ConnectionPool {

    private static ArrayList<connection> connectionPoolList;

    private static int instanceCount;
    private static int maxInstances;
    private static Connection poolClass;
    private static ConnectionPool instance;

    public static ConnectionPool getInstance() {
        if (instance == null) {
            instance = new ConnectionPool();
        }
        return instance;
    }

    private ConnectionPool() {

        ConnectionPool.setMaxInstances(10);
        connectionPoolList = new ArrayList<connection>();

        try {
            Class.forName("org.sqlite.JDBC");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Connection conn = null;

        // populate connection pool
        for (int i = 0; i <= maxInstances - 1; i++) {
            try {
                conn = ConnectionPool.createConnection();
                connectionPoolList.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
        }
    }

    private static int getSize() {
        synchronized (connectionPoolList) {
            return connectionPoolList.size();
        }
    }

    private static int getInstanceCount() {
        return instanceCount;
    }

    private static int getMaxInstances() {
        return maxInstances;
    }

    private static void setInstanceCount(int instanceCount) {
        ConnectionPool.instanceCount = instanceCount;
    }

    private static void setMaxInstances(int maxInstances) {
        ConnectionPool.maxInstances = maxInstances;
    }

    public static Connection getConnection() throws SQLException {
        synchronized (connectionPoolList) {
            Connection thisConnection = removeObject();

            if (thisConnection != null) {
                return thisConnection;
            }
            if (getInstanceCount() < getMaxInstances()) {
                // pool is empty, 
                // allocate a new object, thus increasing the size of the pool.
                ConnectionPool.setMaxInstances(maxInstances++);
                return createConnection();
            }
            return null;
        }
    }

    private static Connection createConnection() throws SQLException {
        String dbPath = SQLite.getPath() + "/GTI525.db";
        Connection newConnection = DriverManager.getConnection("jdbc:sqlite:"
                + dbPath);
        ConnectionPool.setInstanceCount(instanceCount + 1);
        return newConnection;
    }

    private static Connection removeObject() {
        if (connectionPoolList.size() > 0) {

            poolClass = connectionPoolList.get(ConnectionPool.getSize() - 1);
            connectionPoolList.remove(ConnectionPool.getSize() - 1);
            ConnectionPool.setInstanceCount(instanceCount - 1);
            return poolClass;
        }
        return null;
    }

    public static void release(Connection conn) {
        if (conn == null) {
            throw new NullPointerException();
        }
        if (poolClass != conn) {
            String actualClassName = conn.getClass().getName();
            throw new ArrayStoreException(actualClassName);
        } // if is instance
        connectionPoolList.add(conn);
        ConnectionPool.setInstanceCount(instanceCount + 1);
    }
}

Last Updated on Tuesday, 13 December 2011 13:38
 

ModalPopupExtender on PostBack

E-mail Print PDF


AJAX

.NET




ASP.NET: Preventing ModalPopupExtender from closing during/after PostBack

Interaction with a Web application can be dealt with with either a synchronous or asynchronous request from the client to the server. With traditionnal DHTML, the client asks to view a page and the server transmits to the client the generated response. This is a synchronous request.  Asynchronous requests utilize a set of technology standards commonly referred to as AJAX (Asynchronous JavaScript and XML). By keeping the screen static, AJAX gives the impression to the user that he is staying in the same application, searching for information on the server without disturbing the user.

The ASP.NET AJAX toolkit is a very nice set of control extenders that can be implemented inside a web application. In this article, we are going to explore modal popup dialog extender control of Ajax control toolkit and how to prevent a postback to the server from closing an opened popup.

Asynchronous requests


If a postback is required, you can simply assign the postback to the Ok/Cancel button to trigger it and the page will re-render. Now, suppose we want to open a second popup after the Ok/Cancel popup that does a postback, what happens during a postback with the ModalPopupExtender is that it will close, unwantingly. Let's take a look at the markup in ASP.NET:

    <asp:modalpopupextender
        runat="server"
        BehaviorID="confirmPopup"
        ID="confirmPopup"
        TargetControlID="butConfirm" 
        PopupControlID="ConfirmView"
        BackgroundCssClass="modalBackground"
        OnOkScript="OnOk();" 
        OnCancelScript="OnCancel();"
        OkControlID="yesButton" 
        CancelControlID="noButton">
    </asp:modalpopupextender


Now we want to link the Ok button to a javascript function that will lauch a postback to the server:



// Confirm popup Ok button

function OnOk() {
    $('#confirmPopup').hide();
    ClickSaveButton();      // does a postback
    ShowGridPopup();        
}

function ShowGridPopup() {
    if (getID() == "Popup1") {
        ShowGridPopup1();
    } else if (getID() == "Popup2") {
        ShowGridPopup2();
    }
}

function ClickSaveButton() {
    var _id = $('a[id$="butSave"]').attr("ID");
    __doPostBack(_id.replace("_", "$"), '');
}


Now, clicking on the Ok button will call the OnOk() function, in turn will hide that popup and call ClickSaveButton() which will do a postback to the server by obtaining the ID of the Save button that normally does this. We are switching the underscore for dollar-signs because we are using Master Pages which will alter the ClientIDs that will be generated to HTML. ShowGridPopup() will then attempt to show another popup, without success because there is still a postback underway. The postback lasts longer than it takes to call the function to show the second popup and this is why it fails. Here is the solution to this problem:

You have to create a new HiddenField controller from the server-side in ASP that will be used to store the ID of the ModalPopupExtender that you want to show after postback, it is set to null if there is no popup to be shown.

<!-- Grid Popup ID: to obtain the grid popup ID from server-side -->
<asp:hiddenfield id="gridPopupID" runat="server" value="">
</asp:hiddenfield>

In the javascript code, we need to set the ID to the HiddenField before we use the save event

// Confirm popup Ok button
function OnOk() {
    $('#confirmPopup').hide();                          // hides the current confirmation popup
    $("#<%= gridPopupID.ClientID %>").val(getID());     // set the ID to the hiddenField.
    ClickSaveButton();                                  // simulates the click of the save button
}

Now, in the code behind, all we need to do is check the value of the HiddenField text field and we can just do a .Show() on the correct popup accordingly.

Protected Sub OnSaveAssociation(ByVal sender As Object, ByVal e As EventArgs) Handles butSaveAssociation.Click

' ommited code: save changes to back end

            ' determine which popup to show
            Dim _id As String = gridPopupID.Value
            Select Case _id
                Case "Popup1"
                    gridPopup1.Show()
                    gridPopupID.Value = Nothing
                Case "Popup2"
                    gridPopup2.Show()
                    gridPopupID.Value = Nothing
            End Select

End Sub



Last Updated on Saturday, 24 December 2011 15:00
 

The basics of WCF explained

E-mail Print PDF

WCF at large: distributed systems

WCF is a solftware development kit (SDK) for developing and deploying services on Windows and was first released as part of .NET v3.0. Firstly, let's review what a service is. A service is a unit of functionality exposed to the world. It is basically the next logical step of evolution in regards from functions to objects to components to services. An SOA, or service-oriented application or architecture is by definition that every component of a system should be a service, and the system should be composed of several loosely-coupled services, that is, several services that are each independent of each other and the modification of one would not affect the others. A system here is referred to as a single unit of a program that serves a business process. The services can be local or remote and can be deployed by different organizations using different technologies. 

 In WCF, every service is associated with a unique address. The address provides two important elements: the location of the service and the transport protocol, used to communicate with the service. In WCF, all services expose contracts. The contract is a platform-neutral and standard way of describing what the service does. WCF defines four different types of contracts: Service contracts, Data contracts, Fault contracts and Message contracts. In WCF, there are multiple aspects of communication with any given service, and there are many possible communication patterns. WCF groups together sets of communication aspects in bindings. A binding is merely a consistent, canned set of choices regarding the transport protocol, message encoding, communication pattern, reliability, security, transaction propagation and interoperability.

WCF explanation 

The client of a service is the party consuming its functionality. It can be a Windows Console, Windows Form, ASP.NET page, WPF, etc. The client communicate by sending and receiving messages. Between WCF clients and services, interaction is done with SOAP (Simple Object Access Protocol)  which is an RPC protocol based on XML and which can be used to authorize an object to invoke a method of an object physically located on another server. With WCF, the client never interacts directly with a service. Instead, it uses a proxy as an intermediate to forward calls to the service. 

WCF explanation 

A first WCF example

Let's start by creating a simple program using Visual Studio 2008 to demonstrate how WCF works. First, let's create a new project of the Visual C# type, we will select WCF for creating a WCF service class library. We will name the project AMTServiceLibrary, because we are going to create a solution based on the AMT train network in the Montreal area.


 WCF project

 Now, doing this will create a skeleton WCF service library. We will erase the files generated by Visual Studio so that we can create our own implementation to understand this process a little better.

Skeleton

 OK. Now that these two classes are deleted, we are going to create a new class within this project. This class will be used to contain the data that needs to be transmitted between the client and the service.


We will create an Id, Origin, Destination, Submitter, Comments and TimeSubmitted fields to this class. In order to be able to use these fields in the WCF contract, we need to annotate this class with the [DataContract] attribute. A Service contract describe which operations the client can perform on the service. The DataContract attribute is defined in the System.Runtime.Serialization namespace. Each and every field that was created must be preceded by the [DataMember] attribute if we want them to be included within this Data contract.


DataContract 

Now we have a fully configured WCF Data Contract that we will be able to use withing WCF Service Contracts. To do this, we are going to add another class, in fact, this is not a class but an interface. We will call it ITrainService.

ITrainService

Now, here we have defined three methods that will be implemented in the corresponding TrainService class. The SubmitTrain() method will be used to create a new train, the GetTrains() method will be used to obtain a list of all trains from the service that have been created. Finally, the RemoveTrain() method will be used to remove a train from the list using it's id. This time, in order to make this a WCF Service Contract, we will need to include another namespace in this class which we will do by adding "using System.ServiceModel". This way, we can annotate our class with the [ServiceContract] attribute. Only interfaces or classes decorated with this attribute will be considered WCF contracts. We now have to annotate every method that we wish to include in our Service Contract with the [OperationContract] attribute. There are some restrictions as to what can be decorated with the [OperationContract] attribute, but we will not go into this in detail.

Now it's time to implement our contract onto our service. To do this, let's create another class and name it TrainService.cs

TrainService

We have to make this class inherit the interface that we have just created previously and implement the methods that were initially defined. Now we see something new here, the [ServiceBehavior] annotation. WCF defines to types of declarative service-side behaviors, governed by two corresponding attributes. In the ServiceBehaviorAttribute you can set the InstanceContextMode of the service. This property defines how new service objects are created.  The option we will using in this project is "Single" or Singleton: Only one InstanceContext object is used for all incoming calls and is not recycled subsequent to the calls. If a service object does not exist, one is created.

In this implementation, we will have a new train added to a list of trains. Each new train will be attributed a new id, in this case a Guid (Globally unique identifier) that will be used to identify each train. The GetTrains() method will be used to obtain the list of trains and the RemoveTrain() method will go through the list and find the train by it's ID to remove it.

Our project is now complete, we will need to define the Endpoints.

Every service is associated with an address that defines where the service is, a binding that defines how to communicate with the service, and a contract that defines what the service does. This three-way governing the service is easy to remember as the ABC of the service. WCF formalizes this relationship in the form of an endpoint. The endpoint is the fusion of the address, contract, and binding. 

Endpoints

This screen is obtained by right-clicking on the App.config file in the Solution Explorer. The first Endpoint here has to be associated with the AMTServiceLibrary.ITrainService which is a .dll in the /bin/Debug folder. The second Endpoint is set to IMetadataExchange, also known as MEX. The MEX endpoint supports an industry standard for exchanging metadata, represented in WCF by the IMetadataExchange interface.

Service Endpoint 

Now that this is done, we can go ahead and build and run the project. What happens when we run a WCF Service Library project, the WCF Service Host will be launched as well as the WCF Test Client. The test client will communicate with the WCF host and will access the data from our service.

Test Client 

From here, we can define all the fields of our train using the SubmitTrain method of the ITrainService. After invoking these fields, we obtain a NullObject in response, which is normal since the method returns void. 

 WCF Test client

After adding a couple of trains in this way, we can now obtain a list of trains by using the GetTrains() method:

GetTrains 

Now, we can even try to remove a train by copy and pasting the Id using the RemoveTrain() method.

 

After doing this, we can now observe that we have one train that has been removed from our list by invoking the GetTrains() method once more.

References 

  • Programming WCF Services 3rd edition, O'Reilly 
  • Pluralsight screencasts on youtube 
  • csharp-online.net


Last Updated on Thursday, 21 July 2011 16:08
 

Main Menu

Who's Online

We have 1 guest online