@TessFerrandez
In my last post I wrote about a WCF hang I caused because of a newbie mistake. I also wrote that I would post about how to create a simple WCF service. This tutorial is mostly for myself so that I’ll remember what I did, but hopefully it might benefit some other people too…
The WCF service I will create here (MyGameService) is one that will keep track of all the games for my game site.
Creating a WCF Service and hosting it in IIS
1. In Visual Studio 2008, choose File / New / Web Site… / WCF Service and put it in the location http://localhost/MyGameService.
Note: You could have choose File / New / Project and Web / WCF Service Application as well but this would have created a file based one that you would later have to publish to IIS.
2. Since we want to call our service MyGameService rather than Service we have to make the following changes
a) Rename Service.svc to MyGameService.svc b) Rename App_Code/IService.cs to IMyGameService.cs c) Rename APP_Code/Service.cs to MyGameService.cs d) In IMyGameService.cs rename the interface IService to IMyGameService and press Shift+Alt+F10 to rename it throughout the project e) Do the same to rename the class Service to MyGameService in MyGameService.cs
a) Rename Service.svc to MyGameService.svc
b) Rename App_Code/IService.cs to IMyGameService.cs
c) Rename APP_Code/Service.cs to MyGameService.cs
d) In IMyGameService.cs rename the interface IService to IMyGameService and press Shift+Alt+F10 to rename it throughout the project
e) Do the same to rename the class Service to MyGameService in MyGameService.cs
f) change the markup in MyGameService.svc so that Service=”MyGameService” and CodeBehind=”~/App_Code/MyGameService.cs”
3. Remove all the contents of IMyGameService.cs except for
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; [ServiceContract] public interface IMyGameService { }
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text;
[ServiceContract] public interface IMyGameService { }
and all the contents of MyGameService.cs except for
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; public class MyGameService : IMyGameService { }
public class MyGameService : IMyGameService { }
now we have a clean slate to work with
4. Add methods [OperationContract] and classes [DataContract] to the service
The service will have 3 methods
void AddGame(Game g); Game GetGameByID(int id); List<Game> GetAllGames();
Since we are passing Games back and forth we will also have to create a DataContract for the type Game
To do so, add the following code to IMyGameService.cs
[ServiceContract] public interface IMyGameService { [OperationContract] void AddGame(Game g); [OperationContract] List<Game> GetAllGames(); [OperationContract] Game GetGameByID(int id); } [DataContract] public class Game{ [DataMember] public int ID { set; get; } [DataMember] public string Name { set; get; } [DataMember] public string Publisher { set; get; } public Game() { } }
[ServiceContract] public interface IMyGameService { [OperationContract] void AddGame(Game g);
[OperationContract] List<Game> GetAllGames();
[OperationContract] Game GetGameByID(int id); }
[DataContract] public class Game{ [DataMember] public int ID { set; get; } [DataMember] public string Name { set; get; } [DataMember] public string Publisher { set; get; } public Game() { } }
5. Implement the methods in MyGameService.cs
public class MyGameService : IMyGameService { static List<Game> _games = new List<Game>(); static object locker = new object(); #region IMyGameService Members public void AddGame(Game g) { g.ID = GetNextID(); lock (locker) { _games.Add(g); } } public List<Game> GetAllGames() { return _games; } public Game GetGameByID(int id) { lock (locker) { foreach (Game g in _games) { if (g.ID == id) return g; } } return null; } #endregion private int GetNextID() { int maxID = 0; lock (locker) { foreach (Game g in _games) { if (g.ID > maxID) maxID = g.ID; } } return maxID + 1; } }
public class MyGameService : IMyGameService { static List<Game> _games = new List<Game>(); static object locker = new object();
#region IMyGameService Members
public void AddGame(Game g) { g.ID = GetNextID(); lock (locker) { _games.Add(g); } }
public List<Game> GetAllGames() { return _games; }
public Game GetGameByID(int id) { lock (locker) { foreach (Game g in _games) { if (g.ID == id) return g; } } return null; }
#endregion
private int GetNextID() { int maxID = 0; lock (locker) { foreach (Game g in _games) { if (g.ID > maxID) maxID = g.ID; } } return maxID + 1; } }
Note: you can create stubs for the methods by marking IMyGameService and pressing shift+alt+f10
6. Browse to MyGameService.svc
Note: if there is no mapping for .svc you may get the following error A name was started with an invalid character. Error processing resource 'http://localhost/MyGameService/MyGameService.svc'....<%@ ServiceHost Language="C#" Debug="true" Service="MyGameService" CodeBehind="~/App_Code/MyGameService.cs" %> -^ To fix this, add a mapping for .svc in inetmgr for the default website or for this particular vdir mapping to C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll. You may need to reset IIS for the mapping to take effect
Note: if there is no mapping for .svc you may get the following error
A name was started with an invalid character. Error processing resource 'http://localhost/MyGameService/MyGameService.svc'....
<%@ ServiceHost Language="C#" Debug="true" Service="MyGameService" CodeBehind="~/App_Code/MyGameService.cs" %> -^
To fix this, add a mapping for .svc in inetmgr for the default website or for this particular vdir mapping to C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll. You may need to reset IIS for the mapping to take effect
Creating an ASP.NET Client that consumes the WCF service
1. Add a new web site to the WCF service solution called http://localhost/MyGameSite
2. Add a web reference to the WCF service (http://localhost/MyGameService/MyGameService.svc) and call the reference MyGameService
3. In the browser, where you browsed to MyGameService.svc copy the svcutil.exe command
svcutil.exe http://mymachine/MyGameService/MyGameService.svc?wsdl
open up a Visual Studio 2008 command line and run this to generate the files MyGameService.cs and output.config.
4. Add a new folder to your asp.net project called app_code and add the generated MyGameService.cs file to this directory
5. Add everything between <system.ServiceModel>… </system.ServiceModel> including the <system.ServiceModel>… </system.ServiceModel> tags from output.config to your web.config file, right before the closing </configuration> tag.
Now we are ready to call the WCF service from our ASP.NET application
6. Add two textboxes (txtName, txtPublisher), a button (btnAddGame) and a data grid to display games (grdGames) to default.aspx
7. Add the following code to default.aspx.cs
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { using (MyGameServiceClient gs = new MyGameServiceClient()) { grdGames.DataSource = gs.GetAllGames(); grdGames.DataBind(); } } } protected void btnAddGame_Click(object sender, EventArgs e) { using (MyGameServiceClient gs = new MyGameServiceClient()) { gs.AddGame(new Game() { Name = txtName.Text, Publisher = txtPublisher.Text }); grdGames.DataSource = gs.GetAllGames(); grdGames.DataBind(); } }
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { using (MyGameServiceClient gs = new MyGameServiceClient()) { grdGames.DataSource = gs.GetAllGames(); grdGames.DataBind(); } } } protected void btnAddGame_Click(object sender, EventArgs e) { using (MyGameServiceClient gs = new MyGameServiceClient()) { gs.AddGame(new Game() { Name = txtName.Text, Publisher = txtPublisher.Text });
Remember to use a using statement or to explicitly close the MyGameServiceClient, otherwise you will end up with the hang shown in my previous post.
And that is pretty much all there is to it.
A word of caution… you will be dealing with the same cross-domain/cross-application issues here as with normal web services, i.e. a potential for heavy serialization if you pass a lot of stuff back and forth, and other such issues, so be a bit careful when you design any apps that rely on sending data back and forth across domain or application boundaries.
Have fun,
Tess
If you want to cut out 80% of the configuration necessary for a WCF service, check out the Castle Windsor WCF Facility. This makes production services actually palatable to use in the real world. You can do it with StructureMap too, but you've got to some legwork first :)
Hi, After adding the mapping for .svc I get this error message:
This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.
Parameter name: item
Do you happen to know how to fix this?
where do you get this message? in inetmgr when you add the mapping between .svc and aspnet_isapi? or when you add the binding in web.config?
You might want to check out this blog entry
http://www.robzelt.com/blog/2007/01/24/WCF+This+Collection+Already+Contains+An+Address+With+Scheme+Http.aspx
and there seems to be a lot of notes on the subject when searching for "There can be at most one address per scheme in this collection.". I haven't ran into it myself though
Good post. But you may want to change the way how you consume a WCF service. It is recommended to use the try catch block instead of the using statement.
Because the closing bracket of the using statement does a close on the service, but if a communication error happens when close is called then that is not handled properly.
See this msdn post for more info http://msdn.microsoft.com/en-us/library/aa355056.aspx
Thanks for the tip, you are absolutely correct
Hi TessFerrandez,
i am vinoth. i am new to WCF. could u give any reference to create webservice in WCF and how to hosting the webservice and how to consuming the webservice in our Website.
If u can please help me