About
This project presents a simple, but fun “Make a Cat” Service Application. The service provides a factory creational design pattern to create and return a single Cat object (complete with a cute photo), or group of five cats in a CatList object (with multiple cats). It is hosted using IIS Express to quickly demo and test the service with a client.
A client “tester” windows form application tests the service and provides output to the user in a simple GUI.
Architecture
The demo project consists of these component topics:
- Make A Cat WCF Service Application
- Cat (Data Model Contract)
- IMakeACatService (Interface for Service)
- MakeACatService (Code that Implements the Service Interface)
- Client “Tester to Service” Windows Form Application
- Connected Service “Proxy Reference” MakeACatServiceRef
- Main Form GUI User Interface
- Form Code – Processes GUI User Interface
Make A Cat WCF Service Application
A WCF Service Application project was added to my Visual Studio solution. The code is available on GitHub here.
Cat (Data Model Contract)
The Cat class is a DataContract with five DataMember representing the name, breed, gender, age, and photo (bitmap) of the individual cat implemented as automatic properties. The Cat class also implements two constructors for the server-side factory creation pattern. The parameterized constructor is used to create a cat based on the client-side user input requested name, breed, gender, and age. A generic photo of that cat breed is set on the newly created cat object. Photos of the cat breeds are stored in the App_Data.photos project directory on the server and named to match the Breed enum CatBreed. The Cat DataContract also specifies two enumerations: CatBreed (five breeds) and GenderType (two genders). Lastly, the CatList class is defined to specify multiple entities that can be returned and inherits from List<Cat>. The code is available on GitHub here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
/// <summary> /// Cat /// Represents a Cat Object /// </summary> [DataContract] public class Cat { #region Properties /// <summary> /// Name of Cat /// </summary> [DataMember] public string Name { get; set; } /// <summary> /// Breed of Cat /// </summary> [DataMember] public CatBreed Breed { get; set; } /// <summary> /// Gender of Cat /// </summary> [DataMember] public GenderType Gender { get; set; } /// <summary> /// Age of Cat /// </summary> [DataMember] public int Age { get; set; } /// <summary> /// Photo of the Cat /// </summary> [DataMember] public Bitmap Photo { get; set; } #endregion Properties #region Constructors /// <summary> /// Default Constructor /// </summary> public Cat() { } /// <summary> /// Parameterized Constructor /// </summary> public Cat(string name, CatBreed breed, GenderType gender, int age) { Name = name; Breed = breed; Gender = gender; Age = age; // Photo Assembly asm = Assembly.GetExecutingAssembly(); string path1 = "MakeACatWCFServiceApplication.App_Data.photos."; string path2 = Breed.ToString(); string filepath = path1 + path2 + ".jpg"; Photo = new Bitmap(asm.GetManifestResourceStream(filepath)); } // end of constructor #endregion Constructors } // end of Cat class /// <summary> /// Collection of Cat Objects /// </summary> [CollectionDataContract] public class CatList : List<Cat> { } #region enums /// <summary> /// List of Cat Breeds /// </summary> public enum CatBreed { Abyssinian = 0, American_Shorthair, Bengal, British_Longhair, Maine_Coon } // end of enum /// <summary> /// List of Gender Types /// </summary> public enum GenderType { Male, Female } // end of enum #endregion enums |
IMakeACatService (Interface for Service)
The ServiceContract for the Make A Cat Service allows for two possible options: make single cat entity or make a list of five cat entities (CatList). The first OperationContract will return a single Cat object, representing a factory created Cat object with the name, breed, age, and gender that was requested by the client.
The second OperationContract will return a CatList (List<Cat>) with five factory created Cat objects. The second interface returns five cats with random names selected from each of the five breed categories. This interface and implementation could be further expanded to allow the client to specify the number of random Cats to create, but this was a short demo project to be done quickly in a day. The code is available on GitHub here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IMakeACatService" in both code and config file together. [ServiceContract] public interface IMakeACatService { /// <summary> /// Make One Cat /// Factory Implementation where the service creates a /// cat object from the specified parameters and returns /// a cat /// </summary> /// <param name="name">Name (string) of your cat</param> /// <param name="breed">Breed (CatBreed) of your cat</param> /// <param name="gender">Gender (GenderType) of your cat</param> /// <param name="age">Age (int) of your cat</param> /// <returns>A Cat object</returns> [OperationContract] Cat MakeACat(string name, CatBreed breed, GenderType gender, int age); /// <summary> /// Make A List of Cats /// Returns a full list of five random cats /// </summary> /// <returns>CatList (List<Cat>) object</returns> [OperationContract] CatList MakeCats(); } // end of interface |
MakeACatService (Code that Implements the Service Interface)
The service implementation code is a factory that accepts parameters and returns a built Cat object to the client. If the client requested multiple cats, it randomizes (name and age only) five Cat objects to create and return to the client. The multiple Cat factory implementation loads a list of possible cat names from a file in the project into a list using reflection. The static class LoadNames() takes care of loading the cat names from the file in the project directory on the server and putting them into memory. The Random number generator is also static on the class level to retain the same seeding that implements Random generator correctly. The code is available on GitHub here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
public class MakeACatService : IMakeACatService { private static Random m_Random = new Random(); private static List<string> m_CatNames = new List<string>(); /// <summary> /// Loads Cat Names from a File /// </summary> private static void LoadNames() { Assembly asm = Assembly.GetExecutingAssembly(); using (StreamReader sr = new StreamReader(asm.GetManifestResourceStream("MakeACatWCFServiceApplication.App_Data.catnames.txt"))) { while (!sr.EndOfStream) { string name = sr.ReadLine().Trim().ToLower(); if (!string.IsNullOrEmpty(name)) { m_CatNames.Add(string.Format("{0}{1}", char.ToUpper(name[0]), name.Substring(1))); } } } } // end of load names /// <summary> /// Makes a Cat /// </summary> /// <param name="name">Name (string) of the cat</param> /// <param name="breed">Breed (CatBreed) of the cat</param> /// <param name="gender">Gender (GenderType) of the cat</param> /// <param name="age">Age (int) of the cat</param> /// <returns></returns> public Cat MakeACat(string name, CatBreed breed, GenderType gender, int age) { Cat cat = new Cat(name, breed, gender, age); return cat; } /// <summary> /// Returns a List of 5 Cats /// Random Names /// One from each cat breed /// </summary> /// <returns>CatList a List of Cat type objects</returns> public CatList MakeCats() { // If no cat names, load the list if (m_CatNames.Count == 0) { LoadNames(); } CatList cats = new CatList(); //int myCatBreedMemberCount = Enum.GetNames(typeof(CatBreed)).Length; //int myGenderTypeMemberCount = Enum.GetNames(typeof(GenderType)).Length; cats.Add(new Cat(m_CatNames[m_Random.Next(0, m_CatNames.Count)], CatBreed.Abyssinian, GenderType.Male, m_Random.Next(1, 18))); cats.Add(new Cat(m_CatNames[m_Random.Next(0, m_CatNames.Count)], CatBreed.American_Shorthair, GenderType.Female, m_Random.Next(1, 18))); cats.Add(new Cat(m_CatNames[m_Random.Next(0, m_CatNames.Count)], CatBreed.Bengal, GenderType.Male, m_Random.Next(1, 18))); cats.Add(new Cat(m_CatNames[m_Random.Next(0, m_CatNames.Count)], CatBreed.British_Longhair, GenderType.Female, m_Random.Next(1, 18))); cats.Add(new Cat(m_CatNames[m_Random.Next(0, m_CatNames.Count)], CatBreed.Maine_Coon, GenderType.Female, m_Random.Next(1, 18))); return cats; } // end of method } // end of class |
Client “Tester to Service” Windows Form Application
The client “tester to service” is a simple windows form application project in the same solution that connects to the “Make A Cat Service” by use of a proxy generated by SVCUTIL. The client program will use this proxy to test each OperationContract or method available in the ServiceContract and return the results to the user on the form window.
The user enters in cat details in the form window, then selects “Make A Cat” button. The form will validate the user input and then pass the data to the service to receive a newly created Cat object. If the user clicks the “Make 5 Cats!” button, the form does not need to validate or send any parameters to build the factory list of Cat objects, it just receives the Cat objects.
After the service builds and sends either single entity or five Cat objects back to the client, the windows form GUI will update and add the names of the Cat objects to the list. The user can then select on the individual Cat name and it will populate the details in the entry boxes above and change the photo to the correct breed of cat. Note: The Photo is a bitmap that was stored on the server and transmitted in the DataContract for the Cat Photo property. The code is available on GitHub here.
Connected Service “Proxy Reference” MakeACatServiceRef
I used the simple “Add Service Reference” wizard to create a Service Reference to an existing service in my Visual Studio solution. After selecting the “Discover” button on the wizard, it was able to first load, instance, and then recognize the Make A Cat service and build the service reference using the exposed meta-exchange data “WSDL”. See the demo section for more information on how to use SVCUTIL without the wizard to generate client “tester” proxies to an existing service. The auto-generated code is available on GitHub here.
Main Form and Program
The main program in the client “tester” windows application first connects to the Make A Cat Service using a client proxy with classes built from the “Add Service Reference” wizard. Based on this proxy, it then can call upon and retrieves either a single Cat or an enumerable list of Cats (five). The Program handles the user interface events, button clicks, validation, service calls, etc. The code is available on GitHub here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
public partial class frmMain : Form { // Keeps track of all your cats created public static List<Cat> mycats = new List<Cat>(); public frmMain() { InitializeComponent(); InitializeGUI(); } private void label1_Click(object sender, EventArgs e) { } private void frmMain_Load(object sender, EventArgs e) { } /// <summary> /// method InitializeGUI() /// Description: This sets up the GUI for the first time when /// the form loads. /// </summary> private void InitializeGUI() { // Clear Input Controls txtName.Text = string.Empty; numAge.Value = 1; cmbBreed.Text = string.Empty; cmbGender.Text = string.Empty; // Clear Output Controls lstCats.Items.Clear(); } // end of InitializeGUI() /// <summary> /// Make a Cat! /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMakeACat_Click(object sender, EventArgs e) { // Validate the Input if(!ValidateInput()) { MessageBox.Show("Please Enter Cat Details", "Error"); return; } // Assemble the Cat Attributes from the Form string name = txtName.Text; int age = (int)numAge.Value; CatBreed breed = (CatBreed)Enum.Parse(typeof(CatBreed),cmbBreed.Text); GenderType gender = (GenderType)Enum.Parse(typeof(GenderType), cmbGender.Text); // Make a Proxy to the Service MakeACatServiceClient proxy = new MakeACatServiceClient(); // Call the Service Cat cat = proxy.MakeACat(name, breed, gender, age); // Add to the Master Cat List mycats.Add(cat); // Update the GUI UpdateGUI(); } // end of method /// <summary> /// Validate the User Form Inputs /// </summary> /// <returns>True if Valid, False Otherwise</returns> public bool ValidateInput() { if(String.IsNullOrEmpty(txtName.Text)) { return false; } if (String.IsNullOrEmpty(cmbBreed.Text)) { return false; } if (String.IsNullOrEmpty(cmbGender.Text)) { return false; } return true; } // end of method /// <summary> /// Update the GUI with the List of Cats /// </summary> private void UpdateGUI() { lstCats.Items.Clear(); if (mycats.Count != 0) { foreach (Cat cat in mycats) { string str = String.Format("{0}",cat.Name); lstCats.Items.Add(str); } } } // end of UpdateGUI() /// <summary> /// Get Five Cats /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMakeCats_Click(object sender, EventArgs e) { // Make a Proxy to the Service MakeACatServiceClient proxy = new MakeACatServiceClient(); // Call the Service CatList cats = proxy.MakeCats(); // Add to the Master Cat List foreach (Cat cat in cats) { mycats.Add(cat); } // Update the GUI UpdateGUI(); } // end of method /// <summary> /// Select List Box Item Event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lstCats_SelectedIndexChanged(object sender, EventArgs e) { int selectedIndex = lstCats.SelectedIndex; // Object has not been initialized if ((selectedIndex < 0) || (mycats.Count == 0)) return; Cat cat = mycats[selectedIndex]; // if not null if (cat != null) { txtName.Text = cat.Name; numAge.Value = cat.Age; cmbBreed.Text = cat.Breed.ToString(); cmbGender.Text = cat.Gender.ToString(); picCat.Image = cat.Photo; } } // end of method } // end of class |
Demo
Code
The entire project code repository is available on GitHub here.