Scott Hanselman

Separating a Web Service's Implementation from the ASMX File

March 20, '05 Comments [5] Posted in ASP.NET | Web Services | DasBlog
Sponsored By

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:

[WebService(Namespace="urn:schemas-newtelligence-com:dasblog:edit-services")]

 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

{

    [WebMethod]

    public string CreateEntry(Entry entry, string username, string password)

    {

        SiteConfig siteConfig = SiteConfig.GetSiteConfig();

        if (!siteConfig.EnableEditService)

        {

            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);

 

        return entry.EntryId;

    }

    //SNIP...

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.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Sunday, 20 March 2005 21:00:01 UTC
I must be missing something... because I don't see how this is useful. There is a 1-1 relationship between the contract that the asmx ultimately exposes and what the implementation class implements....

So, you've moved the implementation into another assembly/project... I'm missing the value-add.

Sunday, 20 March 2005 22:01:04 UTC
In this example, yes, "CreateEntry" is roughty the same as the underlying "SaveEntry," but surely you can see that you could create composite methods in the Web Service, return different types, do data transformations that are different from the underlying DAL.

You can also make different endpoints (ASMX) of differnet versions containting different "broker" layers that call whatever DAL or DALs you have available.

The post means to remind folks that you don't need to put everything in the ASMX and expose your inner workings.
Scott Hanselman
Sunday, 20 March 2005 23:35:12 UTC
Indeed. However, in the example, there's no delegation since the WebMethod attr is on the implementation class method binding it to the asmx contract.

I was confused by the example.
Monday, 21 March 2005 01:15:23 UTC
Great point Richard. Perhaps the dasBlog code isn't the best example. A better one would be to have stub methods in the ASMX file carry the attribute and delegate into the implementation class(es)!
Scott Hanselman
Monday, 21 March 2005 01:42:05 UTC
Fair Enough ;-)
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.