How To: Create a WCF Service for ASMX Client
In this scenario we have a .NET windows application accessing a .NET 2.0 ASMX Web Service, lets look at how we can upgrade the web service with out causing any interruptions to the front end client. Our goal is to create a WCF service with both anonymous and authenticated methods which can be used instead of the web service.
Lets look at the existing ASMX Service in this sample. It has two methods namely GetStockValue and GetCompanyDetails.
public class StockService : System.Web.Services.WebService { /// <summary> /// Returns the value of the stock for the specified symbol /// </summary> /// <param name="stockSymbol">Symbol of the company</param> /// <returns>Stock value in dollars</returns> [WebMethod] public int GetStockValue(string stockSymbol) { if (stockSymbol.ToLower() == "msft") return 40; else return 10; } /// <summary> /// Returns the company details for the specified stock symbol /// </summary> /// <param name="stockSymbol">Symbol of the company</param> /// <returns>Company with its full name and CEO's name</returns> [WebMethod] public Company GetCompanyDetails(string stockSymbol) { //ensuring that the user is authenticated if (User.Identity.IsAuthenticated && User.Identity.Name != "") { if (stockSymbol.ToLower() == "msft") return new Company("Microsoft Corporation", "Steve Ballmer"); } return new Company(); } } |
Note that the second method is an authenticated method.
Now lets check the client code, please note that the client code simply creates a web reference to the above ASMX Service and calls the specific web methods. The URL for the service is stored in the app.config file of the application. This is very important, as we will see later how this affects our transition.
private void button1_Click(object sender, EventArgs e) { StockService.StockService service = new WCFBlogPost.StockService.StockService(); MessageBox.Show("Stock Price for MSFT is $" + service.GetStockValue(textBox1.Text).ToString()); } private void button2_Click(object sender, EventArgs e) { StockService.StockService service = new WCFBlogPost.StockService.StockService(); service.Credentials= System.Net.CredentialCache.DefaultCredentials; StockService.Company msft = service.GetCompanyDetails(textBox1.Text); MessageBox.Show("CEO of company \"" + msft.CompanyName + "\" is \"" + msft.CEO + "\""); } |
So why cant we just create a WCF service and use it instead of the ASMX service. So lets create a WCF service.
public class StockService : IStockService { #region IStockService Members public int GetStockValue(string stockSymbol) { if (stockSymbol.ToLower() == "msft") return 40; else return 10; } public Company GetCompanyDetails(string stockSymbol) { if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated && System.Threading.Thread.CurrentPrincipal.Identity.Name != "") { if (stockSymbol.ToLower() == "msft") return new Company("Microsoft Corporation", "Steve Ballmer"); } return new Company(); } #endregion } |
In order to run this service with our requirements we need to couple of things first.
- We need to enable windows authentication in IIS
- Remove the MEX endpoint from web.config as it requires anonymous authentication
Now lets change the URL in app.config to new WCF service and run it. Immediately you are hit with a 404 error, even though the service exists you get a 404 exception. This is due to the incompatibilities with the type of binding used. Lets change the binding from default wsHttpBinding to basicHttpBinding and see what happens. Now you get a different error, essentially there is contract mismatch between the service and the client. This is due to the message format and reply actions of the service. So lets use XMLSerializerFormatAttribute on the WCF service interface. Similarly, each of our action does not have any URI which also causes an exception, we will update the OperationContractAttribute to include Action and ReplyAction parameters.
[ServiceContract] [XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document)] public interface IStockService { [OperationContract(Action = "https://tempuri.org/GetStockValue", ReplyAction = "https://tempuri.org/GetStockValue" )] int GetStockValue(string stockSymbol); [OperationContract(Action = "https://tempuri.org/GetCompanyDetails", ReplyAction = "https://tempuri.org/GetCompanyDetails")] Company GetCompanyDetails(string stockSymbol); } |
Once we make the above changes to the WCF service interface, now the client should be able to seamlessly talk to the WCF service.
Couple of things to note in the process.
- Usage of basicHttpBinding for the service binding
- Removal of MEX endpoint for enabling windows authentication
- Usage of XmlSerializerFormatAttribute to enable proper messaging format
- Usage of Action/ReplyAction parameters to provide namespace support
By doing this, we have not changed the ASMX client code in anyway, just changed the web service URL.
MSDN link to similar article https://msdn.microsoft.com/en-us/library/ms751433.aspx
Link to expose WCF service as ASMX https://kjellsj.blogspot.com/2006/12/how-to-expose-wcf-service-also-as-asmx.html
Comments
- Anonymous
July 15, 2008
Thank you!I ran into the exact same problem when I converted my asmx service to WCF, when calling from the client that cannot change. And this post helped me solve it. - Anonymous
September 03, 2016
Many Thanks Man,