Separating a Web Service's Implementation from the ASMX File
A fellow said recently that he wanted to build a "monster web service" with over 20 classes and over 20 methods (well, not THAT monster, but not Hello World). He said:
Is there any way to provide my consumers with a single end-point (.asmx) exposing these methods from several different class files? I can't see my dev team all working on a single ASMX file...what am I missing here?
It's easy to make the assumption that the ASMX file has some magic and that everything needs to go in it. But the ASMX is just an endpoint like an ASPX or ASMX. It gives IIS and ASP.NET something to think about, but it's just a broker - even less - it's a front.
DasBlog has a Web Services interface, thanks to Clemens and the crew before Omar and I, and here's the contents of it's EditService.asmx.cs:
public class EditService : EditServiceImplementation
That's it. Seriously. It lives in our main Web Project. So, how does this work? Well, look at what the class it's derived from. It's not System.Web.Services.WebService (yet), it's EditServiceImplementation.
RULE: Don't mix your implementation with your presentation
A Web Services endpoint is arguably just a presentation of some logic. Hopefully that logic exists somewhere that's NOT the ASMX file. The ASMX file is just a way to call something somewhere else.
For example, here's part of the source for EditServiceImplementation.cs that's in a totally different assembly and project.
public class EditServiceImplementation : WebService
public string CreateEntry(Entry entry, string username, string password)
SiteConfig siteConfig = SiteConfig.GetSiteConfig();
throw new ServiceDisabledException();
if (SiteSecurity.Login(username, password).Role != "admin")
throw new Exception("Invalid Password");
// ensure that the entryId was filled in
if (entry.EntryId == null || entry.EntryId.Length == 0)
entry.EntryId = Guid.NewGuid().ToString();
ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);
SaveEntry(entry, "", null, siteConfig, logService, dataService);
This shows EditServiceImplementation (remember, NOT in the ASMX.cs file) deriving from WebService. It also shows the CreateEntry method that is marked as a [WebMethod] and exposed to the outside world.
Note that this method takes an Entry, a username and a password. Internally it checks to see if Web Services are enabled, tries to log the user in then calls the existing .NET API method "SaveEntry".
SaveEntry already existed. the CreateEntry WebMethod is a stateless rollup of Login and SaveEntry.
So, in this example:
WebService Request -> EditService.asmx -> EditService class, deriving from EditServiceImplementation -> Validate the User, etc -> Broker to existing SaveEntry API.
We leverage the existing dasBlog DataService, we easily create other endpoints (foo.asmx) in a number of ways, the implementation doesn't even live in the Web Project, and even then, the Implementation is ten lines of code.
Don't let your [perception] of the ASMX file cramp your style. You can add a few small layers and not only make development of your Web Service easy, but also friendly for large development groups. Idea: If you're concerned about collision, folks can test and work on their own atomic endpoints before rolling everything up into one WSDL. Also, remember Contract First.