Wednesday, May 28, 2008

WCF Server/Client Chat Sample


WCF Server/Client Chat


Download


Screen Shots



This is a simple and smart WCF Client/Server Chat sample, Amit Gupta (visitor to my blog) asked how to convert a
client/server sockets app. into client/server WCF app., which got me interested to make this sample.



It consists of two apps. both are windows forms, the server one includes the contracts and implements the service.
The client one implements the callcack.



WCF server/client sample features:

  • Handling concurrency

  • Handling server state

  • Handling client state

  • Checking server availability

  • Reliable sessions

  • Asynchronous operations



Code will be as follows:



  • Server:

    • form1.cs

    • app.config



  • Client:

    • form1.cs




Server


Form1.cs



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;

namespace Server
{
[ServiceContract(CallbackContract=typeof(ISampleChatCallback), SessionMode=SessionMode.Required)]
public interface ISampleChat
{
[OperationContract(IsInitiating=true, IsOneWay=true)]
void Connect(string name);

[OperationContract(IsOneWay = true)]
void SayToServer(string name, string msg);

[OperationContract(IsTerminating = true, IsOneWay = true)]
void Disconnect(string name);
}


public interface ISampleChatCallback
{
[OperationContract(IsOneWay = true)]
void SayToClient(string msg);
}



[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, InstanceContextMode=InstanceContextMode.Single)]
public partial class Form1 : Form, ISampleChat
{
ServiceHost host;
private Dictionary< string, ISampleChatCallback> clients = new Dictionary< string, ISampleChatCallback>();
private object syncObj = new object();

public Form1()
{
InitializeComponent();
EnableControls(false);
}

private void buttonStart_Click(object sender, EventArgs e)
{
host = null;
host = new ServiceHost(this);
host.Opened += new EventHandler(host_Opened);
host.Closed += new EventHandler(host_Closed);
host.Faulted += new EventHandler(host_Faulted);

//start listening..
host.Open();
}

void host_Faulted(object sender, EventArgs e)
{
HandleHost();
}

void host_Closed(object sender, EventArgs e)
{
HandleHost();
}

void host_Opened(object sender, EventArgs e)
{
HandleHost();
}

private void HandleHost()
{
switch (host.State)
{
case CommunicationState.Closed:
labelStatus.Text = "Closed";
EnableControls(false);
break;
case CommunicationState.Faulted:
labelStatus.Text = "Faulted";
EnableControls(false);
host.Abort();
break;
case CommunicationState.Opened:
labelStatus.Text = "Opened";
EnableControls(true);
break;
}
}

private void EnableControls(bool opened)
{
if (opened)
{
buttonStart.Enabled = false;
buttonStop.Enabled = true;
buttonSend.Enabled = true;
richTextBox1.Enabled = true;
textBox1.Enabled = true;
listBox1.Enabled = true;
}
else
{
buttonStart.Enabled = true;
buttonStop.Enabled = false;
buttonSend.Enabled = false;
richTextBox1.Enabled = false;
textBox1.Enabled = false;
listBox1.Enabled = false;
}
}

private void buttonStop_Click(object sender, EventArgs e)
{
host.Close();
}

private void buttonSend_Click(object sender, EventArgs e)
{
//send message to all clients
foreach (ISampleChatCallback cb in clients.Values)
{
cb.SayToClient("Server : " + textBox1.Text.ToString());
}
richTextBox1.Text += "\n" + "Server : " + textBox1.Text.ToString();
textBox1.Text = "";
}

private ISampleChatCallback CurrentCallback
{
get
{
return OperationContext.Current.GetCallbackChannel();
}
}

#region ISampleChat Members

public void Connect(string name)
{
if (!clients.ContainsKey(name))
{
lock (syncObj)
{
clients.Add(name, CurrentCallback);
listBox1.Items.Add(name);
}

richTextBox1.Text += "\n" + name + " connected..";


//u may want to tell other clients that someone just connected
foreach (ISampleChatCallback cb in clients.Values)
{
cb.SayToClient("Client " + name + " connected.");
}
}


}

public void Disconnect(string name)
{
if (clients.ContainsKey(name))
{
lock (syncObj)
{
clients.Remove(name);
listBox1.Items.Remove(name);
}
}

richTextBox1.Text += "\n" + name + " disconnected..";

//u may want to tell other clients that someone just disconnected
foreach (ISampleChatCallback cb in clients.Values)
{
cb.SayToClient("Client " + name + " disconnected.");
}
}

public void SayToServer(string name, string msg)
{
//here u get the message from the client
//do whatever u want..
richTextBox1.Text += "\n" + name + " : " + msg;

//u may want to tell other clients that someone said something
foreach (ISampleChatCallback cb in clients.Values)
{
cb.SayToClient(name + " : " + msg);
}
}

#endregion
}
}



App.config



< ?xml version="1.0" encoding="utf-8" ?>
< configuration>
< system.serviceModel>

< services>
< service name="Server.Form1" behaviorConfiguration="serviceBehaviorConfiguration">
< host>
< baseAddresses>
< add baseAddress="net.tcp://localhost:4477/SampleWCFChat/"/>
< add baseAddress="http://localhost:4478/SampleWCFChat/"/>
< /baseAddresses>
< /host>
< endpoint address="tcp"
binding="netTcpBinding"
bindingConfiguration="tcpBindingConfiguration"
contract="Server.ISampleChat"/>

< endpoint address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange"/>
< /service>
< /services>
< bindings>
< netTcpBinding>
< binding name="tcpBindingConfiguration"
closeTimeout="00:00:05"
maxBufferSize="1048576"
maxBufferPoolSize="1048576"
maxConnections="10"
maxReceivedMessageSize="1048576"
openTimeout="00:00:05"
receiveTimeout="01:00:00"
sendTimeout="01:00:00"
transferMode="Buffered">
< readerQuotas maxArrayLength="1048576" maxBytesPerRead="1048576" maxStringContentLength="1048576"/>
< reliableSession enabled="true" inactivityTimeout="01:00:00"/>
< /binding>
< /netTcpBinding>
< /bindings>
< behaviors>
< serviceBehaviors>
< behavior name="serviceBehaviorConfiguration">
< serviceDebug includeExceptionDetailInFaults="true"/>
< serviceMetadata httpGetEnabled="true"/>
< /behavior>
< /serviceBehaviors>
< /behaviors>


< /system.serviceModel>
< /configuration>



Client


Form1.cs



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace Client
{
public partial class Form1 : Form, SampleService.ISampleChatCallback
{
public Form1()
{
InitializeComponent();
EnableControls(false);
}


SampleService.SampleChatClient proxy;
private string name = string.Empty;
private delegate void MyInvoker();

#region ISampleChatCallback Members

public void SayToClient(string msg)
{
richTextBox1.Text += "\n" + msg;
}

public IAsyncResult BeginSayToClient(string msg, AsyncCallback callback, object asyncState)
{
throw new NotImplementedException();
}

public void EndSayToClient(IAsyncResult result)
{
throw new NotImplementedException();
}

#endregion


private void EnableControls(bool connected)
{
if (connected)
{
buttonDisconnect.Enabled = true;
buttonConnect.Enabled = false;
buttonSend.Enabled = true;
richTextBox1.Enabled = true;
textBoxMsg.Enabled = true;
}
else
{
richTextBox1.Text = "";
richTextBox1.Enabled = false;
textBoxMsg.Enabled = false;
buttonDisconnect.Enabled = false;
buttonConnect.Enabled = true;
buttonSend.Enabled = false;
}
}

private void buttonDisconnect_Click(object sender, EventArgs e)
{
proxy.DisconnectAsync(name);
richTextBox1.Text = "";
}

private void buttonSend_Click(object sender, EventArgs e)
{
proxy.SayToServerAsync(name, textBoxMsg.Text.ToString());
textBoxMsg.Text = "";
}

private void buttonConnect_Click(object sender, EventArgs e)
{
//u may want to skip CheckServer to boost speed
if (CheckServer())
{
proxy = null;
InstanceContext context = new InstanceContext(this);
proxy = new Client.SampleService.SampleChatClient(context);

proxy.InnerDuplexChannel.Opened += new EventHandler(InnerDuplexChannel_Opened);
proxy.InnerDuplexChannel.Closed += new EventHandler(InnerDuplexChannel_Closed);
proxy.InnerDuplexChannel.Faulted += new EventHandler(InnerDuplexChannel_Faulted);

name = textBoxName.Text.ToString();
proxy.ConnectAsync(name);
}
else
{
MessageBox.Show("Sorry, Server is not available");
}
}

private bool CheckServer()
{
MetadataExchangeClient mexClient;
bool serverIsUp = false;
try
{
string address = "net.tcp://localhost:4477/SampleWCFChat/mex";
mexClient = new MetadataExchangeClient(new Uri(address), MetadataExchangeClientMode.MetadataExchange);
MetadataSet metadata = mexClient.GetMetadata();

serverIsUp = true;
}
catch
{
serverIsUp = false;
}

return serverIsUp;
}

void InnerDuplexChannel_Faulted(object sender, EventArgs e)
{
if (InvokeRequired)
{
this.Invoke(new MyInvoker(HandleProxy));
return;
}
HandleProxy();
}

void InnerDuplexChannel_Closed(object sender, EventArgs e)
{
if (InvokeRequired)
{
this.Invoke(new MyInvoker(HandleProxy));
return;
}
HandleProxy();
}

void InnerDuplexChannel_Opened(object sender, EventArgs e)
{

if (InvokeRequired)
{
this.Invoke(new MyInvoker(HandleProxy));
return;
}
HandleProxy();
}

private void HandleProxy()
{
switch (proxy.State)
{
case CommunicationState.Closed:
labelStatus.Text = "Disconnected";
EnableControls(false);
break;
case CommunicationState.Faulted:
labelStatus.Text = "Faulted";
EnableControls(false);
break;
case CommunicationState.Opened:
labelStatus.Text = "Connected";
EnableControls(true);
break;
}
}
}
}

Wednesday, April 23, 2008

A SilverLight-WCF Chat



kick it on DotNetKicks.com

Introduction



This is very simple and basic SilverLight-WCF chat application that uses basicHttpBinding and a timer to call the
WCF service in order to refresh the SilverLight client each certain amount of time.





Technique


The solution consists of:




  • ASP.NET Web Project


    • ASP.NET page to host the SilverLight application

    • WCF Service which contains two generic lists to hold online chatters and messages history



  • SilverLight Project (Client)



SilverLight client calls the Join() method and starts the timer, the timer ticks every two seconds
and calls GetChatters() and GetMessages() methods, client now is free to call
Say() method to send message to other clients or call Leave() method to disconnect
from the service and stop the timer.



WCF service responds to client calls to add or remove Client or Message from
the generic lists, or send these lists back to the client.



The Code



WCF Service


This is the service contract



[ServiceContract]
public interface IbasicChatService
{
[OperationContract(IsOneWay = false)]
bool Join(Chatter _chatter);

[OperationContract(IsOneWay = true)]
void Say(Message _msg);

[OperationContract(IsOneWay = false)]
List< Chatter> GetChatters();

[OperationContract(IsOneWay = false)]
List< Message> GetMessages();

[OperationContract(IsOneWay = true)]
void Leave(Chatter _chatter);
}


This is the client and message data contracts



[DataContract]
public class Chatter
{
private string _name;
private DateTime _time;

[DataMember]
public string Name
{
get { return _name; }
set { _name = value; }
}

[DataMember]
public DateTime Time
{
get { return _time; }
set { _time = value; }
}
}

[DataContract]
public class Message
{
private string _sender;
private string _content;
private DateTime _time;

[DataMember]
public string Sender
{
get { return _sender; }
set { _sender = value; }
}

[DataMember]
public string Content
{
get { return _content; }
set { _content = value; }
}

[DataMember]
public DateTime Time
{
get { return _time; }
set { _time = value; }
}
}



This is the service implementation



[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,
ConcurrencyMode=ConcurrencyMode.Multiple)]
public class basicChatService : IbasicChatService
{

private List< Chatter> chatters = new List< Chatter>();
private List< Message> messages = new List< Message>();

private object syncObj = new object();


#region IbasicChatService Members

public bool Join(Chatter _chatter)
{
foreach (Chatter chtr in this.chatters)
{
if (chtr.Name == _chatter.Name)
{
return false;
}
}

lock (syncObj)
{
this.chatters.Add(_chatter);

Message msg = new Message();
msg.Sender = "Service";
msg.Content = "---- " + _chatter.Name + " joined chat ----";
msg.Time = DateTime.Now;

this.messages.Add(msg);
}

return true;
}

public void Say(Message _msg)
{
lock (syncObj)
{
this.messages.Add(_msg);
}
}

public List< Chatter> GetChatters()
{
return this.chatters;
}

public List< Message> GetMessages()
{
return this.messages;
}

public void Leave(Chatter _chatter)
{

foreach (Chatter chtr in this.chatters)
{
if (chtr.Name == _chatter.Name)
{
this.chatters.Remove(chtr);

if (this.chatters.Count < 1)
{
this.messages.Clear();
return;
}
Message msg = new Message();
msg.Sender = "Server";
msg.Content = "---- " + _chatter.Name + " leftt chat ----";
msg.Time = DateTime.Now;

this.messages.Add(msg);

return;
}
}
}

#endregion
}


To integrate SilverLight application with WCF service you have to use basicHttpBinding as followed in the
service configuarion file



< system.serviceModel>
< serviceHostingEnvironment aspNetCompatibilityEnabled="true">
< /serviceHostingEnvironment>
< services>
< service behaviorConfiguration="SilverlightApp_Host.basicChatServiceBehavior"
name="SilverlightApp_Host.basicChatService">
< host>
< baseAddresses>
< add baseAddress="http://localhost:6464/localsystem"/>
< /baseAddresses>
< /host>
< endpoint address=""
binding="basicHttpBinding"
contract="SilverlightApp_Host.IbasicChatService">
< identity>
< dns value="localhost"/>
< /identity>
< /endpoint>
< endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
< /service>
< /services>
< behaviors>
< serviceBehaviors>
< behavior name="SilverlightApp_Host.basicChatServiceBehavior">
< serviceMetadata httpGetEnabled="true"/>
< serviceDebug includeExceptionDetailInFaults="true"/>
< /behavior>
< /serviceBehaviors>
< /behaviors>
< bindings>
< basicHttpBinding>
< binding name="basicBinding"
closeTimeout="00:00:20"
maxBufferPoolSize="1048576"
maxBufferSize="1048576"
maxReceivedMessageSize="1048576"
openTimeout="00:00:20"
receiveTimeout="01:00:00"
sendTimeout="00:01:00"
transferMode="Buffered">
< readerQuotas maxArrayLength="1048576" maxBytesPerRead="1048576"
maxStringContentLength="1048576"/>
< security mode="None">
< transport clientCredentialType="Windows"/>
< /security>
< /binding>
< /basicHttpBinding>
< /bindings>
< /system.serviceModel>



SilverLight Client



I'm a very bad designer so I'll let you imagine any design for this application which of course will be better than
this, this is just two list boxes to hold the online chatters and messages history coming from the service, other
controls are three buttons to join, leave the chat or send a message, and two textboxes for chatter name, and message.



This is the application xaml code



< UserControl x:Class="SilverlightApp.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
< Grid x:Name="LayoutRoot" >

< Grid.Background>
< LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
< GradientStop Color="Black" Offset="0"/>
< GradientStop Color="LavenderBlush" Offset="1"/>
< /LinearGradientBrush>
< /Grid.Background>

< Grid.RowDefinitions>
< RowDefinition Height="50"/>
< RowDefinition Height="25"/>
< RowDefinition Height="25"/>
< RowDefinition Height="200"/>
< RowDefinition Height="30"/>
< RowDefinition Height="50"/>
< /Grid.RowDefinitions>
< Grid.ColumnDefinitions>
< ColumnDefinition Width="70"/>
< ColumnDefinition Width="360"/>
< ColumnDefinition Width="120"/>
< ColumnDefinition Width="70"/>
< /Grid.ColumnDefinitions>

< TextBlock x:Name="labelStatus"
Margin="8, 5, 2, 2"
Grid.Row="1"
Grid.Column="2"
FontFamily="Consolas"
FontSize="13"
Foreground="White">Offline< /TextBlock>

< Grid x:Name="layoutLogin" Grid.Row="2" Grid.Column="1">
< Grid.RowDefinitions>
< RowDefinition Height="25" />
< /Grid.RowDefinitions>
< Grid.ColumnDefinitions>
< ColumnDefinition Width="80"/>
< ColumnDefinition Width="160"/>
< ColumnDefinition Width="*"/>
< /Grid.ColumnDefinitions>

< TextBlock FontFamily="Consolas"
Margin="4, 2, 2, 0"
Grid.Row="0"
Grid.Column="0"
FontSize="12"
Foreground="White">User Name:< /TextBlock>

< TextBox x:Name="textboxName"
Margin="2, 2, 2, 2"
Grid.Row="0"
Grid.Column="1">< /TextBox>

< Button x:Name="buttonJoin"
Background="Transparent"
Margin="2, 2, 2, 2"
Grid.Row="0"
Grid.Column="2"
Click="buttonJoin_Click"
Content="Join">< /Button>

< /Grid>

< Button x:Name="buttonLeave"
Background="Transparent"
Margin="2, 2, 2, 2"
Click="buttonLeave_Click"
Grid.Row="2"
Grid.Column="2"
Content="Leave">< /Button>

< ListBox x:Name="listBoxMsgs"
LayoutUpdated="listBoxMsgs_LayoutUpdated"
Margin="2, 2, 2, 2"
Grid.Row="3"
Grid.Column="1">< /ListBox>

< ListBox x:Name="listBoxNames"
Margin="2, 2, 2, 2"
Grid.Row="3"
Grid.Column="2">< /ListBox>

< TextBox x:Name="textboxMsg"
Margin="2, 2, 2, 2"
Grid.Row="4"
Grid.Column="1">< /TextBox>

< Button x:Name="buttonSend"
Background="Transparent"
Margin="2, 2, 2, 2"
Grid.Row="4"
Grid.Column="2"
Click="buttonSend_Click"
Content="Send">< /Button>

< /Grid>
< /UserControl>



SilverLight client implementation consists of some feilds, constructor, connection event handlers, private methods, and UI event handlers


Feilds



namespace SilverlightApp
{
public partial class Page : UserControl
{

#region Feilds

//TIMER
DispatcherTimer _timer = null;

SVC.IbasicChatServiceClient proxy = null;
SVC.Chatter localChatter = null;

//List to hold online chatters
List< SVC.Chatter> chatters = new List< SilverlightApp.SVC.Chatter>();

//List to hold messages history
List< SVC.Message> messages = new List< SilverlightApp.SVC.Message>();

//To enable listbox auto scroll
bool flag = false;

#endregion

...



Page constructor



public Page()
{
InitializeComponent();
buttonLeave.IsEnabled = false;
buttonSend.IsEnabled = false;
textboxMsg.KeyDown += new KeyEventHandler(textboxMsg_KeyDown);

//Create Timer and set interval
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(2);
_timer.Tick += new EventHandler(_timer_Tick);
}



Connection event handlers




#region Connection Event Handlers

void proxy_JoinCompleted(object sender, SilverlightApp.SVC.JoinCompletedEventArgs e)
{
if (e.Result)
{
labelStatus.Text = "Online";
this._timer.Start();
}
else
{
proxy.Close();
labelStatus.Text = "Name Found";
}
}

void proxy_LeaveCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
proxy.Close();
}

void proxy_GetMessagesCompleted(object sender, SilverlightApp.SVC.GetMessagesCompletedEventArgs e)
{
listBoxMsgs.Items.Clear();
foreach (SVC.Message msg in e.Result)
{
//This will not scroll the listbox
//listBoxMsgs.Items.Add(msg.Sender + " : " + msg.Content);

//Auto scroll, AddItem() is a private method
AddItem(msg.Sender + " : " + msg.Content);
flag = true;
}

}

void proxy_GetChattersCompleted(object sender, SilverlightApp.SVC.GetChattersCompletedEventArgs e)
{
listBoxNames.Items.Clear();
foreach (SVC.Chatter chtr in e.Result)
{
listBoxNames.Items.Add(chtr.Name);
}
}

void InnerChannel_Opened(object sender, EventArgs e)
{
HandleProxy();
}

void InnerChannel_Faulted(object sender, EventArgs e)
{
HandleProxy();
}

void InnerChannel_Closed(object sender, EventArgs e)
{
HandleProxy();
}


#endregion



Private methods



#region Private Methods

public void AddItem(String messageText)
{
listBoxMsgs.Items.Add(new ListBoxItem { Content = messageText });
}

private void Join()
{
proxy = null;
//CREATE PROXY
proxy = new SilverlightApp.SVC.IbasicChatServiceClient();
proxy.InnerChannel.Closed += new EventHandler(InnerChannel_Closed);
proxy.InnerChannel.Faulted += new EventHandler(InnerChannel_Faulted);
proxy.InnerChannel.Opened += new EventHandler(InnerChannel_Opened);

//CREATE LOCAL CLIENT
this.localChatter = new SilverlightApp.SVC.Chatter();
this.localChatter.Name = textboxName.Text.ToString();
this.localChatter.Time = DateTime.Now;

//JOIN()
proxy.JoinAsync(this.localChatter);
proxy.JoinCompleted +=
new EventHandler< SilverlightApp.SVC.JoinCompletedEventArgs>(proxy_JoinCompleted);
}

private void Send()
{
if (proxy != null && proxy.State == CommunicationState.Opened)
{
SVC.Message msg = new SilverlightApp.SVC.Message();
msg.Sender = this.localChatter.Name;
msg.Content = textboxMsg.Text.ToString();
msg.Time = DateTime.Now;

proxy.SayAsync(msg);
textboxMsg.Text = "";
}
else
{
HandleProxy();
}
}

private void HandleProxy()
{
if (proxy != null)
{
switch (proxy.State)
{
case CommunicationState.Closed:
proxy = null;
labelStatus.Text = "Offline";
buttonJoin.IsEnabled = true;
buttonLeave.IsEnabled = false;
buttonSend.IsEnabled = false;
listBoxMsgs.Items.Clear();
listBoxNames.Items.Clear();
break;
case CommunicationState.Closing:
break;
case CommunicationState.Created:
break;
case CommunicationState.Faulted:
proxy.Abort();
proxy = null;
labelStatus.Text = "Offline";
buttonJoin.IsEnabled = true;
buttonLeave.IsEnabled = false;
buttonSend.IsEnabled = false;
listBoxMsgs.Items.Clear();
listBoxNames.Items.Clear();
break;
case CommunicationState.Opened:
buttonJoin.IsEnabled = false;
buttonLeave.IsEnabled = true;
buttonSend.IsEnabled = true;
labelStatus.Text = "Online";
break;
case CommunicationState.Opening:
break;
default:
break;
}
}
else
{
//Join();
}
}

#endregion


And finally the UI event handlers



#region UI Events

void _timer_Tick(object sender, EventArgs e)
{
//Refresh online chatters and messages by calling GetChattersAsync() and GetMessagesAsync()
proxy.GetChattersAsync();
proxy.GetChattersCompleted +=
new EventHandler< SilverlightApp.SVC.GetChattersCompletedEventArgs>(proxy_GetChattersCompleted);

proxy.GetMessagesAsync();
proxy.GetMessagesCompleted +=
new EventHandler< SilverlightApp.SVC.GetMessagesCompletedEventArgs>(proxy_GetMessagesCompleted);
}

void textboxMsg_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
Send();
}
}

private void buttonJoin_Click(object sender, RoutedEventArgs e)
{
Join();
}

private void buttonSend_Click(object sender, RoutedEventArgs e)
{
Send();
}

private void buttonLeave_Click(object sender, RoutedEventArgs e)
{
this._timer.Stop();
if (proxy != null && proxy.State == CommunicationState.Opened)
{
proxy.LeaveAsync(this.localChatter);
proxy.LeaveCompleted +=
new EventHandler< System.ComponentModel.AsyncCompletedEventArgs>(proxy_LeaveCompleted);
}
else
{
HandleProxy();
}
}

private void listBoxMsgs_LayoutUpdated(object sender, EventArgs e)
{
if (flag && listBoxMsgs.Items.Count > 1)
{
listBoxMsgs.ScrollIntoView(listBoxMsgs.Items[listBoxMsgs.Items.Count - 1]);
flag = false;
}
}

#endregion




Other Stuff


Enable cross domain calls for SilverLight application


In order to enable cross domain calls just copy this file clientaccesspolicy.xml to your host root, if you use IIS then
copy the file to WWWRoot folder and then restart IIS. (file is included in the source code).

Tuesday, April 15, 2008

WCF WPF Internet Chat Application with File Transefer

kick it on DotNetKicks.com

Try The Chat Online




Chat Login


Chat Login

See the article on CodeProject for better reading

Download Source Code.

About Online Chat..


As I let the service opened 24x7 , I expected to see some people who will try the demo chat online, this picture shows some (I wished I could talk to them, Thank you for trying).


Try Chat Online

Friday, March 7, 2008

ASP.NET Ajax Chat Web Application


Introduction




I've developed a simple chat web application that deals with xml files to store information about online chatters,
using ASP.NET, C#, Linq to XML, Ajax -Anthem Framework-.




Chat UI


Technique




The application includes a class to handle the xml file by some methods to create the xml or load it, to save info,
modify it, or remove it.
The public methods of this class are:

  • ---------------------------------------------------------------------------

  • void Join(string userName, DateTime dateTime)

  • void Say(string userName, string msg, DateTime dateTime)

  • void Leave(string userName, DateTime dateTime)

  • ---------------------------------------------------------------------------

  • List< string > GetOnlineNames()

  • int GetNumberOfOnlines()

  • List< string > GetMessagesHistory()

  • ---------------------------------------------------------------------------





The Client-side calls (asynchronously using ajax) these methods Join(), Say(), Leave().


And a timer on the Server-side ticks every fixed time (2 seconds in our app.),
to read the xml (by calling these methods GetOnlineNames(), GetNumberOfOnlines(), GetMessagesHistory())
and refresh our controls (asynchronously).

The Code


Starting with our XHandle.cs class which is like a chat room, lets see its members:

using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;

public class XHandle
{
private string _path;
private string _xPageName;
private string _FullPath;

private XDocument _xDoc;
private bool _xDocCreated;

private XElement _usersRoot;
private int _onlineUsers;

....
}


The constructor job is to:


Know the path of the App_Data folder (_path),
and the name of the xml file (_xPageName), to set the _FullPath
that we will be using shortly.


Check to see if the xml file is already created, or create it if it is not.

public XHandle(string _path)
{
this._path = _path;
this._xDocCreated = false;
this._xPageName = "Default";
this._FullPath = this._path + @"\" + this._xPageName + ".xml";
this._onlineUsers = 0;

//Check the xml page if already exist
LoadXPage();

//or create it if it doesnt
if (!_xDocCreated)
{
CreateXPage();
}

}

public XHandle(string _path, string ChatRoomName)
{
this._path = _path;
this._xDocCreated = false;
this._xPageName = ChatRoomName;
this._FullPath = this._path + @"\" + this._xPageName + ".xml";

//Check the xml page if already exist
LoadXPage();

//or create it if it doesnt
if (!_xDocCreated)
{
CreateXPage();
}

}


Private Methods


The LoadXPage() and CreateXPage() are private methods to load or create
the xml file and set the _xDocCreated bool to true or false;


private void CreateXPage()
{
_xDoc = new XDocument();
XDeclaration dec = new XDeclaration("1.0", "utf-8", "yes");
_xDoc.Declaration = dec;
_usersRoot = new XElement("users");
_xDoc.Add(_usersRoot);
_xDoc.Save(_FullPath);
_xDocCreated = true;
}

private void LoadXPage()
{
try
{
_usersRoot = XElement.Load(_FullPath);
_xDocCreated = true;
}
catch
{
_xDocCreated = false;
}
}


Creating the xml file will end up with this:


XML File




Public Methods


When a user joins the chat, we save their information as name, message, and dateTime,
when a user say something we also save their information as name, message, and dateTime,
and when they leave we just remove any info that corresponds to their name.


This is the xml file when a user named Ahmed joins the chat


join




This is the xml file when a user named Ahmed says hello


say




And finally this is the xml file when the user(s) leave


XML File





The three methods Join(..), void Say(..), void Leave(..):

public void Say(string userName, string msg, DateTime dateTime)
{
XElement user = new XElement("user");
XElement elementName = new XElement("name", userName);
XElement elementLastMsg = new XElement("message", msg);
XElement elementDate = new XElement("date", dateTime.ToString());
user.Add(elementName);
user.Add(elementLastMsg);
user.Add(elementDate);
_usersRoot.Add(user);
_usersRoot.Save(_FullPath);
}

public void Join(string userName, DateTime dateTime)
{
string systemMsg = userName + " joined chat room.";
this.Say(userName, systemMsg, dateTime);
}

public void Leave(string userName, DateTime dateTime)
{
var user = from o in _usersRoot.Elements("user")
where (string)o.Element("name").Value == userName
select o;

user.Remove();

_usersRoot.Save(_FullPath);
}




Other public methods are methods to get the names, number of all online chatters,
and the messages history.


public List< string> GetOnlineNames()
{
List< string> names = new List< string>();
var users = (from o in _usersRoot.Elements("user")
select o).Distinct();

foreach (var user in users)
{
if (!names.Contains(user.Element("name").Value))
{
names.Add(user.Element("name").Value);
}
}
_onlineUsers = names.Count;

return names;
}

public int GetNumberOfOnlines()
{
var users = (from o in _usersRoot.Elements("user")
select o).Distinct();
List< string> names = new List< string>();
foreach (var user in users)
{
//Filter the names to avoid duplicates
if (!names.Contains(user.Element("name").Value))
{
names.Add(user.Element("name").Value);
}
}
if (names.Count > 0)
{
_onlineUsers = names.Count;
return names.Count;
}
_onlineUsers = 0;
return 0;
}

public List< string> GetMessagesHistory()
{
List< string> messages = new List< string>();
var users = (from o in _usersRoot.Elements("user")
where o.Element("message").Value != string.Empty
orderby DateTime.Parse(o.Element("date").Value) ascending
select o).Distinct();
foreach (var user in users)
{
string fullString = user.Element("name").Value + " : " + user.Element("message").Value;
if (!messages.Contains(fullString))
{
messages.Add(fullString);
}
}
return messages;
}



Well, now we are done with our class, we have the ability to get the online names, the number of them,
and the messages history (this means if someone started the chat at x time and another one joined the chat
after 30 minutes from x, the last one will get the messages history since the chat has been started..).



Moving to ChatRoom.aspx



Design


In the Page_Load event, we are going to:

  • Register the page to the Anthem Manager to allow it to be called from client-side script.

  • Get an instance of the XHandle class.

  • Get the number of online chatters if there is.

  • Set a fixed time (may be 2 seconds) as the timer interval

  • Subscribe to the Tick event

  • Attach the refreshing controls methods to delegates, So we can invoke them asynchronously.




public partial class ChatRoom : System.Web.UI.Page
{
XHandle xmll;

List< string> names;
List< string> msgs;

private delegate void AsyncCallingNames();
private delegate void AsyncCallingMessages();

AsyncCallingNames callerNames;
AsyncCallingMessages callerMessages;

protected void Page_Load(object sender, EventArgs e)
{
Anthem.Manager.Register(this);

xmll = new XHandle(Server.MapPath("App_Data"), "FirstChatRoom");
//Get chat room name from user or chat admin

int x = xmll.GetNumberOfOnlines();
if (Session["userName"] != null)
{
LabelError.Text = "Online, Users Online: " + x.ToString();
}
else
{
LabelError.Text = "Offline, Users Online: " + x.ToString();
}

Timer1.Interval = 2;
//I set it to 1 second, and it wroked well

Timer1.Tick += new EventHandler(Timer1_Tick);

callerNames = new AsyncCallingNames(this.RefreshListNames);
callerMessages = new AsyncCallingMessages(this.RefreshMessages);
}

.....

}



Now we have 2 jobs:

  • Join, say and leave, This is done by:

    • Join button click event handler

    • Send button click event handler

    • onunload client-side script event handler



    • protected void ButtonJoin_Click(object sender, EventArgs e)
      {
      if (Session["userName"] == null)
      {
      Session["userName"] = TextBoxName.Text.ToString();
      xmll.Join(Session["userName"].ToString(), DateTime.Now);

      TextBoxName.Enabled = false;

      Timer1.StartTimer();

      TextBoxType2.Focus();
      }
      }
      protected void ButtonSend_Click(object sender, EventArgs e)
      {
      if (Session["userName"] != null)
      {
      string name = (string)Session["userName"];
      string msg = TextBoxType2.Text.ToString();
      xmll.Say(name, msg, DateTime.Now);
      TextBoxType2.Text = "";
      TextBoxType2.Focus();
      }
      else
      {
      LabelError.Text = "You have to join with a name first..";
      }
      }

      [Anthem.Method]
      public void Leave()
      {
      Timer1.StopTimer();

      if (Session["userName"] != null)
      {
      string name = (string)Session["userName"];
      xmll.Leave(name, DateTime.Now);

      LabelError.Text = "Offline";
      }
      }




  • Refresh our controls in timer tick event handler:

    • ListBox of chatters names

    • ListBox of messages



    • void Timer1_Tick(object sender, EventArgs e)
      {
      if (Session["userName"] != null)
      {
      IAsyncResult resultN = callerNames.BeginInvoke(null, null);
      if (!resultN.IsCompleted)
      {
      callerNames.EndInvoke(resultN);
      }

      IAsyncResult resultM = callerMessages.BeginInvoke(null, null);
      if (!resultM.IsCompleted)
      {
      callerMessages.EndInvoke(resultM);
      }
      TextBoxType2.Focus();
      }
      else
      {
      Timer1.StopTimer();
      TextBoxType2.Text = "You have to join with a name first..";
      }
      }


      private void RefreshListNames()
      {
      ListBox1.Items.Clear();
      names = xmll.GetOnlineNames();
      foreach (var name in names)
      {
      ListBox1.Items.Add(name);
      }
      }

      private void RefreshMessages()
      {
      ListBox2.Items.Clear();
      msgs = xmll.GetMessagesHistory();
      foreach (var msg in msgs)
      {
      ListBox2.Items.Add(msg);
      }
      }






Leave()


The last thing is how to call the Leave() method from Client-side script

< body önunload="Leave(); return false;">
< script type="text/javascript">
function Leave()
{
Anthem_InvokePageMethod('Leave', null, null);
}

< /script>


Reports


I'll really appreciate your try of this code and any problems you may get, to make this application better.

Monday, January 28, 2008

WCF and Callbacks - How to generate proxy using svcutil tool

Many of us tried to implement even a small simple of WCF service and a callback implementation, which (at least to me) I couldn't
get it to work. I've looked here,
there,
and some many others and I couldn't create a proxy which I use in my client.

In order to make the WCF service able to make a Callback to clients (if some client fired some events.., or even if the service decides to
because of an operation it monitors), you have to create a proxy (on service standards) so the client can be able to implement the ICallback interface.

How can I create this proxy?
There is two solutions for this,


  • First: Use a tool called SvcUtil.exe to generate a proxy.cs and app.config file from the service, so I can use both in my client.

  • Second: After making the service, start it, in the client add a service reference to thats you have just started.


Generate proxy.cs and app.config files
To use the svcutil.exe tool follow this:

  • Start > All Programs > Visual Studio 200x > Visual Studio Tools > Visual Studio 200x Command Prompt

  • type cd.. and switch to this path C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

  • type sn.exe -Vr svcutil.exe Run svcutil tool

  • now we can use svcutil, switch to the path where your service is Where is svcutil.exe

  • if the service is .svc start it then type svcutil http://localhost:8080/CallbackAppNAME/YOURSERIVCENAME.svc?wsdl
    Use svcutil tool


  • if the service is .exe type svcutil YOURSERVICE.exe

  • there is gonna be file .wsdl and .xsd, type svcutil fileName.wsdl filename.xsd /language:C# /out:Proxy.cs /config:app.config



The generated files can be found here C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin (this where the svcutil tool is).
Note: There are more options you can use with svcutil tool like:
merging new configuration file with a one already exists, use this /mergeConfig.
generating a proxy with asynchronous methods, use this /a

Add a service reference
You can just add a service reference from your client:

  • With Visual Studio, start your ready .svc service.

  • In the client solution right click to add a service reference and just point to the service.

  • This will make a reference to call the service directly.

Thursday, January 24, 2008

Design Patterns - Observer Pattern

Introduction

This article explains the Observer patterns which is one of the C# Design Patterns, the article provides a very simple implementation so its complexity can be easily understood.

The observer pattern makes an object that if its state changed somehow, other instances will be notified (or updated) automatically, In other words, it is used to keep track of many objects.
observer
Implementation

Imagine if we have cards game, some tables, and some players..
On a table.., in the game context the suit of cards is changed time by time and we want to notify the players each time the
suit is changed.

Note* this is not a complete logic for a cards game, its an example.

The player object is going to have a function Update() which will be called by the notifier, and data:
  • Player Number (Player Identity) int

  • Current Suit string

  • Current Table Object Table



How can the player be notified? we pass the (always changes) object of the table as a parameter in the Update() function,
So the player (object) can see the new differences in the table (object).

This is the Player object..

interface IPlayer
{
void Update(Table _table);
}

class Player : IPlayer
{

public void Update(Table _table)
{
this._table = _table;
_currentSuit = _table.currentSuit;
Console.WriteLine("Player '" + _PlayerNo + "' notified that current suit is " + _currentSuit);
}

private int _PlayerNo;
private Table _table;
private string _currentSuit;

public Player(int _PlayerNo)
{
this._PlayerNo = _PlayerNo;
}

public Table CurrentTable
{
get { return _table; }
}

}



In our table object you want to add players or remove players (or count them for some reasons..)
So in the table object you'll have:
  • An array list of the players objects List<>

  • Current Suit string

  • Table Number int

  • Add Player Function AddPlayer()

  • Remove Player Function RemovePlayer()

  • Notify Function Notify()



The notify function is sending the updated object (table) to all players objects..


abstract class Table
{
private string _currentSuit;
private int _tableNo;
private List<> players = new List<>();

public Table(int _tableNo, string _currentSuit)
{
this._tableNo = _tableNo;
this._currentSuit = _currentSuit;
}
public void AddPlayer(Player _player)
{
players.Add(_player);
}

public void RemovePlayer(Player _player)
{
players.Remove(_player);
}

public string currentSuit
{
get { return _currentSuit; }
set
{
_currentSuit = value;
Notify();
}
}

public void Notify()
{
foreach (Player _player in players)
{
_player.Update(this);
}
}
}

class ReadyTable : Table
{
public ReadyTable(int _tableNo, string _currentSuit) : base(_tableNo, _currentSuit)
{ }
}



And the Main()

class Program
{
static void Main(string[] args)
{
//Create Table
ReadyTable table = new ReadyTable(1, "Spade");
Console.WriteLine("Starting table 1 with suit spades, suit will be changing in 3 seconds");

//Create Players
List players = new List(4);
for (int i = 0; i < 4; i++)
{
players.Add(new Player(i));
table.AddPlayer(players[i]);
}

Thread.Sleep(3000);
//Change the suit and all players will be notified
table.currentSuit = "Hearts";

Thread.Sleep(6000);
table.currentSuit = "Diamonds";

Console.ReadLine();
}
}

Design Patterns - The Factory Method

Introduction


This article explains the Factory Method Pattern, which is one of the C# Design Patterns.

This pattern gives you the ability to create a factory (instance) that will instantiate many relatedobjects.



When to use the Factory Method?

  • A class can’t anticipate which kind of class of objects it must create.

  • A class uses its subclasses to specify which objects it creates.

  • You want to localize the knowledge of which class gets created.


Cooper - Introduction to Design Patterns in C#



You can think of this like if you have some different implementation for 1 parent object, and
then you have some other different creators for these instances.., so we will have a factory
that decides what creators and decide what instances does every creator instantiate.

The Pattern



Imagine we have a class called Message that sends some contents through the Send() function.

And we have some types of messages (Email, sms, .. etc.) each of which has a special implementation
of the send() function.

Now how to decide which type (instance) of message we will send if using a cell phone or a PC?

So, this means we have different machines (cell, PC, Laptop..), and each machine will instantiate
specific types of messages.


Message



  • The best way is to make an abstract class called Message and include an abstract method called Send(), So
    we can easily implement it.

    abstract class Message
    {
    abstract public void Send();
    }

  • Example of different implementations..

    class Email : Message
    {
    public override void Send()
    {
    Console.WriteLine(this.GetType().Name);
    }
    }
    class VoiceMail : Message
    {
    public override void Send()
    {
    Console.WriteLine(this.GetType().Name);
    }
    }
    class SMS : Message
    {
    public override void Send()
    {
    Console.WriteLine(this.GetType().Name);
    }
    }
    class MMS : Message
    {
    public override void Send()
    {
    Console.WriteLine(this.GetType().Name);
    }
    }

Machine


  • Now to make the machine.. Its the the factory that will decide which instances (types) of messages to instantiate,
    so if we have more than 1 machine, we can inherit from the machine and decide in the concrete factory.

    abstract class Machine
    {
    //The array of objects
    private List< Message > _messages = new List< Message >();
    public Machine()
    {
    CreateMachines();
    }

    //The function that each machine will override
    abstract public void CreateMachines();

    public List< Message > Messaages
    {
    get
    {
    return _messages;
    }
    }
    }

  • Cell Phone and PC example..

    class CellPhone : Machine
    {
    public override void CreateMachines()
    {
    //Instantiating objects
    Messaages.Add(new SMS());
    Messaages.Add(new MMS());
    }
    }

    class PC : Machine
    {
    public override void CreateMachines()
    {
    //Instantiating objects
    Messaages.Add(new Email());
    Messaages.Add(new VoiceMail());
    }
    }


  • And this how we can use the factory..

    class Program
    {
    static void Main(string[] args)
    {
    Machine[] machines = new Machine[2];
    //The constructor is the one who calls CreateMachine()
    machines[0] = new CellPhone();//A machine
    machines[1] = new PC();//Another machine..

    foreach (Machine _machine in machines)
    {
    Console.WriteLine("--" + _machine.GetType().Name);
    foreach (Message _message in _machine.Messaages)
    {
    _message.Send();
    }
    }
    }
    }




The Code



namespace Factory_Method
{
class Program
{
static void Main(string[] args)
{
Machine[] machines = new Machine[3];
machines[0] = new CellPhone();
machines[1] = new PC();
machines[2] = new Laptop();

foreach (Machine _machine in machines)
{
Console.WriteLine("--" + _machine.GetType().Name);
foreach (Message _message in _machine.Messaages)
{
_message.Send();
}
}
}
}
abstract class Message
{
abstract public void Send();
}

class Email : Message
{
public override void Send()
{
Console.WriteLine(this.GetType().Name);
}
}
class VoiceMail : Message
{
public override void Send()
{
Console.WriteLine(this.GetType().Name);
}
}
class SMS : Message
{
public override void Send()
{
Console.WriteLine(this.GetType().Name);
}
}
class MMS : Message
{
public override void Send()
{
Console.WriteLine(this.GetType().Name);
}
}

abstract class Machine
{
private List< Message > _messages = new List< Message >();
public Machine()
{
CreateMachines();
}

abstract public void CreateMachines();
public List< Message > Messaages
{
get
{
return _messages;
}
}
}

class CellPhone : Machine
{
public override void CreateMachines()
{
Messaages.Add(new SMS());
Messaages.Add(new MMS());
}
}

class PC : Machine
{
public override void CreateMachines()
{
Messaages.Add(new Email());
Messaages.Add(new VoiceMail());
}
}

class Laptop : Machine
{
public override void CreateMachines()
{
Messaages.Add(new Email());
Messaages.Add(new VoiceMail());
}
}


}

Wednesday, January 23, 2008

Design Patterns - Singleton

Introduction


This article explains the Sngileton Pattern which is one of The C# Design Patterns.

The singleton pattern is used to make a class that has always only one instance, how can this be useful?

There is times that you want to have only one instance of a specific class like for example a chat window,
you want to allow each chatter to have only one opened chat window.



The Code


This can be easily done by returning one instance of the class and allow a global access to it. How?


  • make the constructor private (or protected if you plan to inherit this class before instantiating it).


    class ChatWindow
    {
    private ChatWindow() { }
    }

  • Second make a static refrence to the class


    class ChatWindow
    {
    //this is static so we can access it
    //in the next point
    private static ChatWindow chatObject;

    private ChatWindow() { }
    }

  • Third make a static function that will check if the object has been instantiated or not,
    if not, it will instantiate a new one and assign it to chatObject.


    class ChatWindow
    {
    private static ChatWindow chatObject;
    private static object syncLock = new object();

    private ChatWindow() { }

    public static ChatWindow GetObject()
    {
    if (chatObject == null)
    {
    lock (syncLock)
    {
    if (chatObject == null)
    {
    chatObject = new ChatWindow();
    }
    }
    }
    return chatObject;
    }
    }



So I can always get only the same instance,

this is the Main..

class Program
{
static void Main(string[] args)
{
ChatWindow TheOnlyChatObject = ChatWindow.GetObject();
if (TheOnlyChatObject != null)
{ Console.WriteLine("Object instantiated"); }
ChatWindow sameObject = ChatWindow.GetObject();
if (sameObject == TheOnlyChatObject)
{ Console.WriteLine("A new object is the same object"); }
}
}

Design Patterns - Abstract Factory


Introduction:


This article explains the Abstract Factory pattern of the Design Patterns, and implements an example.
The Abstract Pattern is used to give you the ability to return one of related (grouped) objects of classes
through a factory object, The factory object can return one of a family classes and can also instantiate objects of
another family classes.



The Code


We are going to explain this on Investments, There is types of investment like stocks and bonds.
The investment itself can be raised and can fall, then the stocks investment for example can be raised and can fall.
This is done through the investment object (which is the factory).



The investment class is abstract, and has two abstract (not implemented) methods: CreateRaise(), and CreateFall() , each of which is type of
raiseInvestment and fallInvestment respectively.


abstract class invsetment
{
public abstract raiseInvestment CreateRaise();
public abstract fallInvestment CreateFall();
}

abstract class fallInvestment
{
public abstract int Minus(int current);
}

abstract class raiseInvestment
{
public abstract int Plus(int current);
}



So what I need now is to implement fallInvestment and raiseInvestment classes..



/// Beside Fall and Raise classes we can add
/// new class to calculate the interest related
/// changes for example, with a static method
/// to directly calculate the interest.

class Fall : fallInvestment
{
public override int Minus(int current)
{
return current - 1;
}
}

class Raise : raiseInvestment
{
public override int Plus(int current)
{
return current + 1;
}
}

Well, Now you have to decide what types of investments you want to make..

There can be Stocks and Bonds, each is an investment so each can inherits investment class.

To inherit investment class you have to override the CreateRaise() and CreateFall abstract
methods (which are in the investment class) and return a new instance of Raise and Fall classes.


class stocks : invsetment
{

public override raiseInvestment CreateRaise()
{
return new Raise();
}

public override fallInvestment CreateFall()
{
return new Fall();
}
}

class bonds : invsetment
{

public override raiseInvestment CreateRaise()
{
return new Raise();
}

public override fallInvestment CreateFall()
{
return new Fall();
}
}


Because here I have instantiated Raise and Fall classes they will be the most derived in the instantiated object class
thats inherited from fallInvestment or raiseInvestment, and the compiler will take the most derived (when overriding).

And actually here we have implemented fallInvestment and raiseInvestment (abstract classes) by the Fall and Raise
(concrete classes), inside stocks and\or bonds classes.



In my client level, the client is the class thats instantiates objects, call functions,..

In my client I want to make instances of raiseInvestment and fallInvestment by calling
investment.CreateRaise() or investment.CreateFall().

But how can I get the refrence of the decided investment?.. it can be passed as a parameter to the constructor
of the client when instantiating the client.

when i get these two instances of raiseInvestment and fallInvestment i can then call these fucntions
raiseInvestment.Plus() and fallInvestment.Minus() and I'll getthe overrided functions because they
have been the most derived.


class clientOnInvestment
{
private fallInvestment _fallInvestment;
private raiseInvestment _raiseInvestment;
private int currentBalance;

public clientOnInvestment(invsetment _investment, int currentBalance)
{
_fallInvestment = _investment.CreateFall();
_raiseInvestment = _investment.CreateRaise();
this.currentBalance = currentBalance;
}

public void doFall()
{
currentBalance = _fallInvestment.Minus(currentBalance);
}

public void doRaise()
{
currentBalance = _raiseInvestment.Plus(currentBalance);
}

public int Balance
{
get
{
return currentBalance;
}
}
}



and Finally, the Main


class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to Investments");

Console.WriteLine("Creating Stocks............................");

invsetment myStocks = new stocks();
clientOnInvestment myClientS = new
clientOnInvestment(myStocks, 0);//set starting balance to 0

Console.WriteLine("Current Balance = {0}", myClientS.Balance);
Console.WriteLine("Raising balance.........");

myClientS.doRaise();

Console.WriteLine("Current Balance = {0}", myClientS.Balance);
Console.WriteLine("Falling balance.........");

myClientS.doFall();

Console.WriteLine("Current Balance = {0}", myClientS.Balance);


Console.WriteLine("Creating Bonds.............................");

invsetment myBonds = new bonds();
clientOnInvestment myClientB = new
clientOnInvestment(myBonds, 0);//set starting balance to 0

Console.WriteLine("Current Balance = {0}", myClientB.Balance);
Console.WriteLine("Raising balance.........");

myClientB.doRaise();

Console.WriteLine("Current Balance = {0}", myClientB.Balance);
Console.WriteLine("Falling balance.........");

myClientB.doFall();

Console.WriteLine("Current Balance = {0}", myClientB.Balance);

}

}