In this blog, I am going to illustrate a Client-Server architecture to have a server-side as a self-hosted service using WCF, consumed by WPF client.
Let's take an XBox game example here, where the requirement is to develop a service to provide a list of games (name, description, and rating) and allow the user to rate each game from the UI. We will break this into two parts. The first part is to create a server-side service to provide a list of Games and the second part is to consume the same service at the UI side developed using WPF platform. I will not be using any database here on the server-side to get data from the database (only static data is used). If you want to use entity framework to create the table etc., you can refer to this
article which explains how to create SQL database and work around it.
Let's start with Visual Studio to create three .NET projects:
- Game class i.e. DataContract and Datamemeber (.net class library project).
- Game Service i.e. ServiceContract and OperationContract (.net class library project, refer to the first project).
- and the third one is a console application to host game service (console application using .net framework).
With this design, we are trying to break into multiple layers, i.e., Database, service layer and hosting service. Let's start one by one in detail with a code snippet.
Server-side model objects could be your persistent object. Here, you can add your DB layer to load an object from the database and mark as datacontra.
Here is the game class server model object.
- [DataContract]
- public class XboxGame
- {
- [DataMember]
- public string Title { get; set; }
-
- [DataMember]
- public string Description { get; set; }
-
- [DataMember]
- public int Rating { get; set; }
-
- public XboxGame()
- {
- }
- }
Second step is to define Service and operation contract.
- [ServiceContract]
- public interface IGameService
- {
- [OperationContract]
- IList<XboxGame> GetGames();
-
- [OperationContract]
- void Update(XboxGame g);
- }
Implementation of Service,
- public class GameService : IGameService
- {
-
- static List<XboxGame> games = new List<XboxGame>()
- {
- new XboxGame() { Title = "Games of thrones", Description = "Games of.......", Rating = 5 },
- new XboxGame() { Title = "Step Up for kinnect", Description = "Integer.......", Rating = 1 },
- new XboxGame() { Title = "Dead island", Description = "Viamurs.......", Rating = 3 }
- };
-
-
- public void Update(XboxGame g)
- {
- var game = games.FirstOrDefault(s => s.Title == g.Title);
- if (game != null)
- {
- game.Description = g.Description;
- game.Rating = g.Rating;
- }
- }
-
- public IList<XboxGame> GetGames()
- {
- return games;
- }
- }
And the final step to is host the service using TCP binding and define the base address in config file and contract. You can find many articles on the ABCs of WCF and here is one of them from
c# corner. Here is the implementation of the self hosting service.
- static void Main(string[] args)
- {
- using (ServiceHost host = new ServiceHost(typeof(GameService)))
- {
-
- ServiceMetadataBehavior behavior = new ServiceMetadataBehavior { HttpGetEnabled = true };
- host.Description.Behaviors.Add(behavior);
-
-
- host.AddServiceEndpoint(typeof(IGameService), new NetTcpBinding { Security = { Mode = SecurityMode.None } }, nameof(GameService));
-
- host.Open();
- Console.WriteLine("GameService hosted");
- Console.WriteLine("Press any key to stop service");
- Console.ReadKey();
- }
- }
Build and run the host.exe to launch the service. Once the service is hosted successfully you should check the url of this service, default service url should be using 9950 port and this is the url for this example: "net.tcp://localhost:9950/GameService". Now, we can start the second part of this article; i.e., creating client and consuming the above service. Let's create a new VS project for WPF Windows Application to consume the service using MVVM pattern.
The first step isa to create a proxy channel for wcf game service. Once proxy object is created you can make as many calls via that service to get the data as you wish. Currently I am setting security as none in all places, I will take full security as another article to explain how security can be implemented in these types of applications using Public/Private keys.
Here is code to create proxy of Service to get the data from the server.
- public class ServiceFactory<T> where T : class
- {
- private T _service;
-
- public T GetService(string address)
- {
- return _service ?? (_service = GetServiceInstance(address));
- }
-
- private static T GetServiceInstance(string address)
- {
- var binding = new NetTcpBinding();
- binding.Security.Mode = SecurityMode.None;
- EndpointAddress endpoint = new EndpointAddress(address);
-
- return ChannelFactory<T>.CreateChannel(binding, endpoint);
- }
- }
The second step is to create view Model for Game Model object and Bind this view model with xmal.
Here, we need to two view models, one for collection; i.e., Full view, and another view model is to represent each game in every row.
- public class GamesViewModel
- {
- private readonly IGameService service;
-
- public GamesViewModel()
- {
- GameList = new ObservableCollection<GameViewModel>();
- var serviceFactory = new ServiceFactory<IGameService>();
- service = serviceFactory.GetService("net.tcp://localhost:9950/GameService");
- var games = service.GetGames();
- foreach (var game in games)
- {
- GameList.Add(new GameViewModel(game, this));
- }
- }
-
- public ObservableCollection<GameViewModel> GameList { get; set; }
-
- public void UpdateProperty(GameViewModel gameViewModel)
- {
- service.Update(gameViewModel.Model);
- }
- }
-
- public class RelayCommand : ICommand
- {
- private Action<object> execute;
- private Func<object, bool> canExecute;
-
- public event EventHandler CanExecuteChanged
- {
- add { CommandManager.RequerySuggested += value; }
- remove { CommandManager.RequerySuggested -= value; }
- }
-
- public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
- {
- this.execute = execute;
- this.canExecute = canExecute;
- }
-
- public bool CanExecute(object parameter)
- {
- return this.canExecute == null || this.canExecute(parameter);
- }
-
- public void Execute(object parameter)
- {
- this.execute(parameter);
- }
- }
- public class GameViewModel : INotifyPropertyChanged
- {
- public RelayCommand ClickEventHandler { get; private set; }
-
- private readonly GamesViewModel _parent;
- private string description;
- private int rating;
-
- public XboxGame Model;
-
- public string Description
- {
- get { return description; }
- set
- {
- if (description != value)
- {
- description = value;
- Model.Description = value;
- _parent.UpdateProperty(this);
- OnPropertyChanged(nameof(Description));
- }
- }
- }
-
- public int Rating
- {
- get { return rating; }
- set
- {
- if (rating != value)
- {
- rating = value;
- Model.Rating = value;
- _parent.UpdateProperty(this);
- OnPropertyChanged(nameof(Rating1));
- OnPropertyChanged(nameof(Rating2));
- OnPropertyChanged(nameof(Rating3));
- OnPropertyChanged(nameof(Rating4));
- OnPropertyChanged(nameof(Rating5));
- }
- }
- }
-
- public bool Rating1 => Model.Rating >= 1;
-
- public bool Rating2 => Model.Rating >= 2;
-
- public bool Rating3 => Model.Rating >= 3;
-
- public bool Rating4 => Model.Rating >= 4;
-
-
- public bool Rating5 => Model.Rating >= 5;
-
- public GameViewModel(XboxGame model, GamesViewModel parent)
- {
- Model = model;
- ClickEventHandler = new RelayCommand(OnRatingClick);
- _parent = parent;
- description = model.Description;
- rating = model.Rating;
- }
-
- private void OnRatingClick(object obj)
- {
- this.Rating = int.Parse(obj.ToString());
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
I hope you liked this blog. Let me know about any questions you might have in the comments section. Thanks!