Monday, May 2, 2011

Silverlight 4.0 Using MVVM

How To: Implement MVVM (Model-View-ViewModel) in Silverlight 4.0

The MVVM is a design pattern, typically used in Silverlight and WPF application development. The Model-View-ViewModel (MVVM) pattern helps you to cleanly separate the business and presentation logic of your application from its user interface (UI). The Model-View-ViewModel (MVVM) pattern provides a flexible way to build Silverlight applications that promotes code re-use, simplifies maintenance and supports testing.

Using the MVVM pattern, the UI of the application and the underlying presentation and business logic is separated into three separate classes: the View, which encapsulates the UI and UI logic; the View Model, which encapsulates presentation logic and state; and the Model, which encapsulates the application's business logic and data.

An example of the how the Model, View and ViewModel components relate to each other is shown in the following diagram:

Picture1

In the MVVM pattern, the View encapsulates the UI and any UI logic, the ViewModel encapsulates presentation logic and state, and the Model encapsulates business logic and data. The View interacts with the ViewModel through Data Binding, Commands, and Change Notification events. The ViewModel Queries, Observes, and Coordinates updates with the Model. It also converts and validates data as necessary for display in the View.

The following figure explains the same:Picture1

The Model: – The Model in MVVM represents typically Business Data and Business Logic. The BL takes care of retrieval and management of application data. It also takes care of data validations and data consistency required for the business application. Typically, the model represents the client-side domain model for the application. Model should not contain any client specific behavior or any application specific logic. Typically it contains ADO.NET Entity Framework, WCF Data Service or WCF RIA Service.

Model supports, property and collection change notification with the help of INotifyPropertyChanged and INotifyCollectionChanged interfaces. Collection objects are generally derived from ObservableCollection<T>, which in turn has INotifyCollectionChanged interface implemented. To support data validation one can use IDataErrorInfo or INotifyDataErrorInfo interface by creating partial class/s for business model.

These interfaces allow Silverlight data binding to be notified when values change so that the UI can be updated. They support data validation and error reporting in the UI layer.

The View: – Normally, View is used to present Model what user is looking for. Typically this is done with the help of XAML UI Elements i.e. User Controls or Custom Controls in Silverlight. The code-behind for this XAML page contains a constructor calling InitializeComponent method. Optionally, it could contain code for some complex animation or if XAML is not sufficient to express some UI Logic.

The ViewModel: - The ViewModel in the MVVM pattern encapsulates the presentation logic and data for the view. VM has no direct knowledge of View or View’s specific implementation or Type. The responsibility of VM is to provide all required Properties and Commands, to which View is interested in. Following diagram explains what one can have in ViewModel.

Picture1

Let’s get into the step-by-step implementation of MVVM in Silverlight 4.0 application. For this implementation I have used the following architecture.

Picture1


Building a Silverlight application using MVVM

You can download complete source code for this exercise Here.

1. Create a blank solution for .NET Framework 4.0 in Visual Studio 2010. Name it SilverlightUsingMVVM. In this solution we will create following projects to implement MVVM in Silverlight application.

  • ·SilverlightUsingMVVM.Model – This is a simple class library, which will hold our Business Data and Business Logic. We will use Entity Framework to create Business Data. You may add few partial classes to provide data validation.
  • SilverlightUsingMVVM.Service – This is an IIS hosted WCF service class library, which will provide a communication channel between our Business Data Model and the Silverlight ViewModel layer.
  • SilverlightUsingMVVM.View – This is a Silverlight application project with its Web hosting project named SilverlightUsingMVVM.View.Web.

Note: - This exercise assumes that you are using SQL Server or SQL Express 2008 with TrainingDB database in it. Also you have Products table with couple of rows of data. If not, please create a database named TrainingDB in your SQL Server / Express and then run the following script to create the required Products table under it.

USE [TrainingDB]
GO

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Products]') AND type in (N'U'))
DROP TABLE [dbo].[Products]
GO

USE [TrainingDB]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Products](
[ID] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[Description] [varchar](200) NULL,
[Price] [money] NOT NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO





2. Right-click on the solution Add > New Prodjct > Visual C# > Windows > Class Library. Name it SilverlightUsingMVVM.Model. Delete Class1.cs file from the project.



3. Right-click on SilverlightUsingMVVM.Model project Add > New Item > Data > ADO.NET Entity Data Model, name it TrainingDB.edmx and click Add. Make sure Generate from Database option is selected and click Next. Click New Connection, put .\SQLEXPRESS as server name and select TrainingDB as database name, click Test Connection. If Text Connection succeeded click Ok. Click Next, expand Tables node and select Products(dbo) table, click Finish to add the Entity Framework Data Model for our Business Data.



4. Close the .edmx diagram.



5. Save all your work and build the solution.



6. Now, right-click on the solution Add > New Project > WCF > WCF Service Application, name it SilverlightUsingMVVM.Service click Add.



7. Right-click on IService1.cs and rename it to IProductService.cs, click Yes in the message box.



8. Right-click on Service1.svc and rename it to ProductService.svc.



9. Open ProductService.cs class right-click on Service1 class name select Refactor > Rename, change name to ProductService, click Ok. Click Apply in the next window.



10. Right-click on References node Add Reference, select .NET tab and select System.Data.Entity.dll and click Add. Again right-click on References node Add Reference click Projects and select SilverlightUsingMVVM.Model and click Ok.



11. Clean the class by deleting everything in it as follows:




namespace SilverlightUsingMVVM.Service
{
public class ProductService : IProductService
{
}
}





12. Open IProductService.cs file. Delete everything in it as follows:




namespace SilverlightUsingMVVM.Service
{
[ServiceContract]
public interface IProductService
{
}
}





13. Right-click on SilverlightUsingMVVM.Service project, Add > New Item > Visual C# > XML File, name it clientaccesspolicy.xml and click Add. We are adding this file because our WCF service in not a part of Silverlight application web project.



14. Copy the following xml code to this file.




<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>"
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>





15. Open IProductService.cs file. Add following using statement to this interface.



using SilverlightUsingMVVM.Model;



16. Add the following method with OperationContract attribute to this interface.




using System.Collections.Generic;
using System.ServiceModel;
using SilverlightUsingMVVM.Model;

namespace SilverlightUsingMVVM.Service
{
[ServiceContract]
public interface IProductService
{
[OperationContract]
List<Product> GetAllProducts();

[OperationContract]
bool InsertProduct(Product product);
}
}





17. Switch back to ProductService.cs file and implement IProductService interface to this class as follows:




using System;
using System.Collections.Generic;
using System.Linq;
using SilverlightUsingMVVM.Model;
using System.ServiceModel.Activation;

namespace SilverlightUsingMVVM.Service
{
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
public class ProductService : IProductService
{
TrainingDBEntities db;

public ProductService()
{
db = new TrainingDBEntities();
}

public List<Product> GetAllProducts()
{
return db.Products.ToList();
}

public bool InsertProduct(Product product)
{
try
{
db.Products.AddObject(product);
db.SaveChanges();
return true;

}
catch (Exception e)
{
return false;
}
}
}
}





18. Open App.config file from SilverlightUsingMVVM.Model project and copy following lines:



 





<connectionStrings>
<add name="TrainingDBEntities" connectionString="metadata=res://*/TrainingDB.csdl|res://*/TrainingDB.ssdl|res://*/TrainingDB.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\sqlexpress;initial catalog=TrainingDB;integrated security=True;pooling=False;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>




19. Now open Web.config file from SilverlightUsingMVVM.Service project and paste it as follows:




<configuration>
<connectionStrings>
<add name="TrainingDBEntities" connectionString="metadata=res://*/TrainingDB.csdl|res://*/TrainingDB.ssdl|res://*/TrainingDB.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\sqlexpress;initial catalog=TrainingDB;integrated security=True;pooling=False;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
<system.web>





Note: If you will miss this step, after completion of the entire solution you will get Remote Server Error: Not Found.



20. Close all .config files that are opened.



21. Build the solution one more time to check that things are placed correctly.



22. Back to solution explorer, right-click Add > New Project > Silverlight > Silverlight Application, name it SilverlightUsingMVVM.Viewer and click Ok.



23. After project gets created close MainPage.xaml as this is not the right time to work with it.



24. Right-click on References node of SilverlightUsingMVVM.Viewer project and click Add Service Reference. Click Discover button. If you get http://localhost:<protnumber>/ProductService.svc in Address bar, click Go to host and find the service. If you get 1 service(s) found at address …, change the namespace to ProductServiceReference and click Ok. This will add all the required proxy classes of the service that you have.



25. Now we will create a service wrapper which will hold the service proxy and provide communication with the service. To do this add a folder named ServiceWrapper to SilverlightUsingMVVM.Viewer project by right-clicking on this project Add > New Folder.



26. Add an interface named IServiceWarpper to this folder. Right-click on the folder Add > New Item > Code > Class, name it IServiceWrapper.cs and click Add.



27. Change the word class to interface. Your code should look like:





   1: namespace SilverlightUsingMVVM.Viewer.ServiceWrapper



   2: {



   3:     public interface IServiceWrapper



   4:     {



   5:  



   6:     }



   7: }




28. Adding this interface will provide more loosely coupled architecture for our application.



29. Add the following code to this interface.




using System;
using SilverlightUsingMVVM.Viewer.ProductServiceReference;

namespace SilverlightUsingMVVM.Viewer.ServiceWrapper
{
public interface IServiceWrapper
{
void GetAllProducts(EventHandler<GetAllProductsCompletedEventArgs> callbackHandler);
void InsertProduct(Product product, EventHandler<InsertProductCompletedEventArgs> callbackHandler);
}
}





30. These are two methods our ViewModel will use to make a call to our WCF service.



31. Right-click on the same ServiceWrapper folder Add > Class, name it ServiceWarpper.cs and click Add.



32. Implement IServiceWrapper interface to this class and add the following code to it.




using System;
using SilverlightUsingMVVM.Viewer.ProductServiceReference;

namespace SilverlightUsingMVVM.Viewer.ServiceWrapper
{
public class ServiceWarpper : IServiceWrapper
{
ProductServiceClient proxy;
public ServiceWarpper()
{
proxy = new ProductServiceClient();
}

public void GetAllProducts(EventHandler<GetAllProductsCompletedEventArgs> callbackHandler)
{
proxy.GetAllProductsCompleted += callbackHandler;
proxy.GetAllProductsAsync();
}

public void InsertProduct(Product product, EventHandler<InsertProductCompletedEventArgs> callbackHandler)
{
proxy.InsertProductCompleted += callbackHandler;
proxy.InsertProductAsync(product);
}
}
}





33. Next is, let’s create a coming class whose object will perform a given specific Action.



34. Right-click on SilverlightUsingMVVM.Viewer project Add > New Folder, name it Commanding.



35. We will create a RelayCommand class to perform this Action. Right-click on this folder Add > Class, name it RelayCommand.cs and click Add.



36. Implement ICommand interface to this class and add the following code to the class.




using System;
using System.Windows.Input;

namespace SilverlightUsingMVVM.Viewer.Commanding
{
public class RelayCommand : ICommand
{
private readonly Action actionHandler;
private bool isActionable;

public RelayCommand(Action action)
{
actionHandler = action;
}

public bool IsActionable
{
get
{
return isActionable;
}
set
{
if (value != isActionable)
{
isActionable = value;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}

public bool CanExecute(object parameter)
{
//return IsActionable;
return true;
}

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)
{
actionHandler();
}
}
}





37. Ok, now let’s put our focus on defining ViewModel layer of this architecture.



38. Right-click on SilverlightUsingMVVM.Viewer project Add > New Folder, name it ViewModel.



39. Right-click on this folder Add > Class, name it ViewModelBase.cs. This class will provide property change notification.



40. Implement INotifyPropertyChanged interface to this class and add the following code to it.




using System.ComponentModel;


namespace SilverlightUsingMVVM.Viewer.ViewModel
{
public class ViewModelBase : INotifyPropertyChanged
{
public bool IsDesignTime
{
get
{
return DesignerProperties.IsInDesignTool;
}
}

public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}





41. Right-click on ViewModel folder Add > Class, name it ProductsViewModel.cs.



42. Inherit this class from ViewModelBase class created in the previous step and add the following code to it.




using System.Collections.ObjectModel;
using SilverlightUsingMVVM.Viewer.ProductServiceReference;
using SilverlightUsingMVVM.Viewer.Commanding;
using SilverlightUsingMVVM.Viewer.ServiceWrapper;

namespace SilverlightUsingMVVM.Viewer.ViewModel
{
public class ProductsViewModel : ViewModelBase
{
public ProductsViewModel()
: this(new ServiceWarpper())
{

}
public ProductsViewModel(IServiceWrapper wrapper)
{
Product prd = new Product();
CurrentProduct = prd;
if (!IsDesignTime)
{
if (wrapper != null)
{
psWrapper = wrapper;
GetAllCustomers();
WireCommands();
}
}
}

#region Properties
private IServiceWrapper psWrapper;
private ObservableCollection<Product> products;
public ObservableCollection<Product> Products
{
get
{
return products;
}
set
{
if (products != value)
{
products = value;
OnPropertyChanged("Products");
}
}
}
private Product currentProduct;
public Product CurrentProduct
{
get
{
return currentProduct;
}
set
{
if (currentProduct != value)
{
currentProduct = value;
OnPropertyChanged("CurrentProduct");
}
}
}
#endregion

#region Commands
public RelayCommand InsertProductCommand
{
get;
private set;
}
#endregion

private void GetAllCustomers()
{
psWrapper.GetAllProducts((sender, args) => Products = args.Result);
}
private bool status;
private void InsertProduct()
{
psWrapper.InsertProduct(CurrentProduct, (sender, args) => status = args.Result);
Products.Add(CurrentProduct);
}

private void WireCommands()
{
InsertProductCommand = new RelayCommand(InsertProduct);
}

}
}





43. This class is a key class in our MVVM architecture, as it exposes all the required Properties and Commands to our View.



44. Now it’s time to create a View for Silverlight Application. In our case MainPage.xaml will act as a View for Products. So open MainPage.xaml.



45. Add the following code to this MainPage.xaml, observe highlighted part of the code.




<UserControl x:Class="SilverlightUsingMVVM.Viewer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightUsingMVVM.Viewer.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<UserControl.Resources>
<local:ProductsViewModel x:Key="vmProducts"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource vmProducts}}">
<sdk:TabControl Height="278" HorizontalAlignment="Left" Margin="10,10,0,0" Name="tabControl1" VerticalAlignment="Top" Width="378">
<sdk:TabItem Header="Product List" Name="tabItem1">
<Grid>
<sdk:DataGrid Grid.Row="0" ItemsSource="{Binding Products, Mode=TwoWay}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="10,10,0,0" Name="dataGrid1" VerticalAlignment="Top">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Product Id" Binding="{Binding ID, Mode=TwoWay}"/>
<sdk:DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=TwoWay}"/>
<sdk:DataGridTextColumn Header="Description" Binding="{Binding Description, Mode=TwoWay}"/>
<sdk:DataGridTextColumn Header="Price" Binding="{Binding Price, Mode=TwoWay}"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</sdk:TabItem>
<sdk:TabItem Header="Add New Product" Name="tabItem2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Product ID: " HorizontalAlignment="Right"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtProductID" Width="200" HorizontalAlignment="Left" Text="{Binding Path=CurrentProduct.ID, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Product Name: " HorizontalAlignment="Right"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtProductName" Width="200" HorizontalAlignment="Left" Text="{Binding Path=CurrentProduct.Name, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Description: " HorizontalAlignment="Right"/>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtProductDescription" Width="200" HorizontalAlignment="Left" Text="{Binding Path=CurrentProduct.Description, Mode=TwoWay}"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Product Price: " HorizontalAlignment="Right"/>
<TextBox Grid.Row="3" Grid.Column="1" x:Name="txtProductPrice" Width="200" HorizontalAlignment="Left" Text="{Binding Path=CurrentProduct.Price, Mode=TwoWay}"/>
<Button Grid.Row="4" Grid.Column="1" Content="Save Product" Width="200" HorizontalAlignment="Left" Command="{Binding InsertProductCommand}"/>
</Grid>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</UserControl>





46. That’s it. Run your Silverlight application by pressing F5, watch the values in DataGrid on the Tab1 Product Details, switch to Tab2 Add New Product, fill the form and click Save Product button. Again switch back to Tab1 Product Details and observer last entry in the DataGrid. You have your newly added product listed in the grid.

Tuesday, April 12, 2011

What’s New in WCF 4.0

Windows Communication Foundation i.e WCF 4.0 comes with wide range of new features on top of WCF 3.5. Following table highlights few of the main features.
Feature Description
Service Discovery Discovery of WCF Service hosted within network using ad-hoc and managed service discovery behaviors, which conform to the standard WS-Discovery protocol.
Simplified Configuration Simplification of the WCF configuration section through support for default endpoints, binding and behavior configurations.
Routing Service Provides features for content-based routing, protocol bridging, and error handling.
REST Improvements Enhancements to the WCF 3.5 Web programming model with some additional features that simplify REST service development.
Workflow Services Provides integration of WCF with WF to implement declarative long-running workflow services.
First we will focus on Service Discovery feature in WCF 4.0 -
In some of the typical SOA environments, there are services whose runtime location is dynamic and constantly changing. Consider environments where different types of service-enabled devices are constantly joining and leaving the network as part of the overall application solution. In this situation you need a client/s to dynamically discover the runtime location of service endpoints.
WS-Discovery is a specification that defines a SOAP-based protocol for dynamically discovering the location of service endpoints at runtime. The client/s probe for service endpoint that match certain criteria. Once the client discovers the service, it continues to invoke it as with regular WCF calls.
Discovery relies on UDP (User Datagram Protocol). Unlike TCP, UDP is a connectionless protocol, and no direct connection is required between the packet’s sender and the receiver. The client uses UDP to broadcast discovery requests for any endpoint supporting a specified contract type. UDP discovery endpoints that the services support will receive these requests and provide response back to client.
To understand Service Discovery concept, let’s start a step by step walk-through to create, host, discover and consume a WCF Service.
1. Open Visual Studio 2010 click File > New > Project. Select .NET Framework 4. Select Visual C# > Windows > Class Library. Name the project as DiscoverableServiceContract and click Add.
2. Delete Class1.cs file, as we don’t need this.
3. Right-click on References node, select Add Reference… Click .NET tab and select System.ServiceModel and click Add to add this assembly into your project.
We have added this assembly to create a WCF contract.
4. Right-click on project name Add > New Item > Code > Interface. Name this interface as IDiscoverableService.cs and click Add.
5. Add following code to this interface -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace DiscoverableServiceContract
{
    [ServiceContract]
    public interface IDiscoverableService
    {
        [OperationContract]
        string SayHello(string uName);
    }
}
6. Save your work and build the project.
This way our Discoverable Service Contract is ready to be used by Service provider and Service Consumer.
7. For the simplicity, we will add three more projects to the same solution as DiscoverableService, DiscoverableServiceHost and ServiceClient.
8. So right-click on the solution name Add > New Project. Click Visual C# > Windows > Class Library and name it DiscoverableService, click Add.
9. Delete Class1.cs and right click on References node, click Recent tab select System.ServiceModel and click Ok.
10. Again right-click on References node, click Projects tab and select DiscoverableServiceContract, click Ok.
11. Right-click on DiscoverableService project Add > Class. Name this class as DiscoverableService.cs. Add the following code to this newly added class -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DiscoverableServiceContract;

namespace DiscoverableService
{
    public class DiscoverableService : IDiscoverableService
    {
        public string SayHello(string uName)
        {
            return string.Format("Hello {0}. Message received at {1}", uName, DateTime.Now.ToString());
        }
    }
}
12. Build the DiscoverableService project. Now your service is ready to host. So let’s create a host project. we will host this service using Console Application.
13. Right-click on the solution name Add > New Project, select Visual C# > Windows > Console Application. Name it as DiscoverableServiceHost click Ok.
14. Right-click References node, click Projects tab and select DiscoverableServiceContract and DiscoverableService projects, click Add.
15. Again right-click References node, click Recent tab and select System.ServiceModel, click Add.
16.To add a configuration file to this host project, right-click on the project name Add > New Item > General > Application Configuration File and click Add.
17. Add the following xml code to this App.config file -
<configuration>
  <system.serviceModel>
    <services>
      <service name="DiscoverableService.DiscoverableService">
        <endpoint address="discoverableService" binding="netTcpBinding" contract="DiscoverableServiceContract.IDiscoverableService"/>
        <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9001"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceDiscovery/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
18. Make DiscoverableServiceHost project as Start up Project and add the following code to Program.cs class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using DiscoverableService;
using DiscoverableServiceContract;
namespace DiscoverableServiceHost
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(DiscoverableService.DiscoverableService)))
            {
                host.Open();
                Console.WriteLine("Discoverable Service Hosted...");
                Console.WriteLine("Press a key to stop service");
                Console.ReadLine();
                host.Close();
            }
        }
    }
}
19. Save all your work, build the project and press ctrl+f5 to run the host. If every thing is OK and in place you should get out pout as follows -
image
20. Press a key to stop the service.
Now it’s time to create a Client.
21. Right-click on the solution Add > New Project > Visual C# > Windows > Console Application and name it ServiceClient.
22. Right-click on this project’s References node Add Reference > Projects, select DiscoverableServiceContract and click Add. Again add System.ServiceModel and System.ServiceModel.Discovery assembly to your client project.
We are using same contract because of couple of things -
1. Our client will be in the same network where the service is hosted.
2. We want to discover service without adding service reference to our client project.
23. To add configuration file, right-click on client project name Add > New Item > General > Application Configuration File, click Ok.
24. Add the following xml code to this App.config file -
<configuration>
  <system.serviceModel>
    <client>
      <endpoint binding="netTcpBinding" contract="DiscoverableServiceContract.IDiscoverableService" name="DiscoverableService" />
      <endpoint name="udpDiscoveryEndpoint" kind="udpDiscoveryEndpoint" />
    </client>
  </system.serviceModel>
</configuration>
25. Now to create proxy of service without adding service reference to out client project, we have to add a class named DiscoverableService.cs to client project.
26. Right-click on client project name Add > Class, name it DiscoverableService.cs and click Ok.
27. Add the following code to this DiscoverableService.cs class -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using DiscoverableServiceContract;
namespace ServiceClient
{
    public class DiscoverableService : ClientBase<IDiscoverableService>, IDiscoverableService
    {
        public DiscoverableService(string endpointConfigurationName) : base(endpointConfigurationName)
        {
        }
        public string SayHello(string uName)
        {
            return base.Channel.SayHello(uName);
        }
    }
}
28. Now add following code to Program.cs class of client project -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Discovery;
using System.ServiceModel;
using DiscoverableServiceContract;
namespace ServiceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Finding IDiscoverableService Service...");
            DiscoveryClient discoveryClient = new DiscoveryClient("udpDiscoveryEndpoint");
            FindCriteria findCriteria = new FindCriteria(typeof(IDiscoverableService));
            FindResponse findResponse = discoveryClient.Find(findCriteria);
            if (findResponse.Endpoints.Count > 0)
            {
                Console.WriteLine("Found IDiscoverableService service at {0}", findResponse.Endpoints[0].Address);
            }
            EndpointAddress address = findResponse.Endpoints[0].Address;
            DiscoverableService proxy = new DiscoverableService("DiscoverableService");
            proxy.Endpoint.Address = address;
            Console.WriteLine("Invoking ICalculator service at {0}", address);
            string result = proxy.SayHello("UserNameOne");
            Console.WriteLine("Result :{0}", result);
            Console.ReadLine();
        }
    }
}
30. Now it’s time to discover service dynamically. Make sure your service is running, if not press ctrl+f5 to start it. Then go to solution explorer right-click on client project name Debug > Start New Instance.
31. If every thing is gone right, you should get an out put as follows -
image
32. Bingo we have done it.
Key Concepts of Service Discovery -
1. works with UDP protocol only.
2. service config should have
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
and
<behavior>
<serviceDiscovery/>
</behavior>
3. client config should have
<endpoint name="udpDiscoveryEndpoint" kind="udpDiscoveryEndpoint"/>
4. DiscoveryClient class –> udpDiscoveryEndpoint
5. FindCriteria class –> contract type
6. FindResponse findResponse = discoveryClient.Find(findCriteria)
Note:- You can find service asynchronously using FindAsync() method of discoveryClient object. Try it!!!
All the best.
In the next episode I will talk about Service Announcements.