Sep 212008
 

WCF never ceases to amaze me. Around every corner is another fascinating use for WCF, and much forethought on Microsoft’s part to make it look and behave great. I wanted to expose my services to my AJAX functions on my web site. I did not want to change my class library because it is used by other clients. I could just add the service classes to this web site, but why re-do when you can re-use.

If you have an existing WCF Service Library, you will need to expose it with the AspNetCompatibilityRequirementsMode.Allowed attribute on the service class to make it visible to ASP.NET clients. To avoid changing your service library in any way, the easiest thing to do is to add a new class to your web site that inherits from your service class. In this example, my existing service library uses the JeepServices namespace. Notice there is no implementation in this class. It is simply a placeholder for the real service implementation with the compatibility attribute attached.

    1 using System.ServiceModel;

    2 using System.ServiceModel.Activation;

    3 

    4 [ServiceBehavior]

    5 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    6 public class WebHttpService : JeepServices.Service

    7 {

    8 }

Now that I have a ASP.NET compatible service, I need to expose it to the web site clients. Create a service file (.svc), and change the Service and CodeBehind attributes to point to the .svc file. The last thing you need is the Factory attribute. This notifies WCF of this service, eliminating the need for a configuration file entry for the service endpoint. In fact, you don’t even need the <system.servicemodel> in your configuration file at all. This is because it is only hosted as a web script, and cannot be called outside of the web site.

    1 <%@ ServiceHost Language=“C#” Debug=“true” Service=“WebHttpService” CodeBehind=“~/App_Code/WebHttpService.cs”

    2     Factory=“System.ServiceModel.Activation.WebScriptServiceHostFactory” %>

 

In your web page you will need a few things. First your will need a ScriptManager with a ServiceReference to the .svc file. You will then need the Javascript functions to make the call (DoJeepWork), handle the success message (OnJeepWorkSucceeded), and handle the failure message (OnJeepWorkFailed). Notice in DoJeepWork that you don’t call the service by it’s service name WebHttpService, you call it by the ServiceContract namespace and name. For this example, my interface has ServiceContract attributes Namespace = “JeepServices”, and Name = “JeepServiceContract”. Now you just wire up a ASP.NET control’s OnClientClick or an input or anchor tag’s onclick to DoJeepWork() and you are good to go.

    1 <%@ Page Language=“C#” AutoEventWireup=“true” CodeFile=“Default.aspx.cs” Inherits=“_Default” %>

    2 

    3 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

    4 “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    5 <html xmlns=“http://www.w3.org/1999/xhtml”>

    6 <head runat=“server”>

    7     <title>Test page</title>

    8 

    9     <script type=“text/javascript”>

   10         function DoJeepWork() {

   11             JeepServices.JeepServiceContract.DoWork(OnJeepWorkSuccedeed, OnJeepWorkFailed);

   12         }

   13         function OnJeepWorkSuccedeed(res) {

   14             document.getElementById(“<%= this.lblMessage.ClientID %>”).innerText = res;

   15         }

   16         function OnJeepWorkFailed(error) {

   17             // Alert user to the error.   

   18             alert(error.get_message());

   19         }

   20     </script>

   21 

   22 </head>

   23 <body>

   24     <form id=“form1″ runat=“server”>

   25     <div>

   26         <asp:ScriptManager runat=“server”>

   27             <Services>

   28                 <asp:ServiceReference Path=“~/Services/WebHttpService.svc” InlineScript=“false” />

   29             </Services>

   30         </asp:ScriptManager>

   31         <asp:Label ID=“lblMessage” runat=“server” Text=“No work has been done” />

   32         <a href=“javascript:void(0); DoJeepWork()”>Do Work</a>

   33     </div>

   34     </form>

   35 </body>

   36 </html>

 

Mission accomplished! Here you’ve seen how to expose an existing WCF service library without changing any code in the library itself. Adding two files allowed the service to be exposed to your AJAX clients. Best of all, there is no configuration file changes to make.

Useful Links:

Sep 172008
 

Hosting an MSMQ service is a little bit different than the other bindings. Since WCF is using MSMQ as a transport mechanism, you must setup the queues, permissions, and bindingConfigurations to allow this to happen. Surprisingly, MSDN has a good sample article that goes into sufficient detail on how to set this up for the 3.5 WF-WCF-CardSpace samples.

I have read in other articles that the AppPool must have an interactive identity and that the queue names needed to match the name of the .svc file. I did not find this to be the case. I was able to use the NetworkService account for my AppPool after adding receive and peek permissions for NetworkService on my queue. Communication between client and WAS worked fine with my service file named WasServices.svc and my queue address as net.msmq://localhost/private/QueuedService1.

You can download my solution with the following link: WasServices.zip (78K)

Additional Info:

Sep 132008
 

It has taken me weeks to get WAS (Windows Activation Service) working. Finally, tonight, my long hours of research has paid off. After everything I tried, it turned out to be a general IIS7 issue caused by a stray http reservation that I probably entered months ago during some testing. As I primarily use the built-in development server for web development, I rarely crank up an IIS site on my development machine.


This post by Phil Haack helped me fix my IIS install:


http://haacked.com/archive/2007/05/21/the-iis-7-team-rocks.aspx


I have been cursing IIS7, Vista, and WAS for weeks. I should have been cursing my own lack of IIS7 knowledge all along. Now that it’s working, I am a big fan of WAS. From the tone of recent forum responses and blog posts, very few people are using WAS. Maybe it is due to Windows Server 2008 being so new. Not many people have Vista workstations for development and all Windows Server 2008 servers to deploy to. Knowing how many problems I had, I can only assume others are experiencing the same thing. The only real info available right now is pre-release articles and MVP posts about the new features with a sneak peak example on how to get it to work. Even MSDN doesn’t show how to use an existing WCF Service Library with WAS. They just walk through a WsHttpBinding example as a new WCF web site served up by WAS.


I’m posting the details so others will maybe see that it’s really not that hard. For this example I want to expose this service with the NetTcpBinding to prove that it is not IIS hosting the service. I used the WCF Service Library project template for my WCF service, and named the project WasServices. So the lame Service1 service is all I have in the library. I made no changes to the project and built it in release mode to get the DLL. Some posts and articles out there say that the only way to get WAS to work is to have an HTTP-based WCF web site. This is simply not true. You just need to have an application set up in IIS.


Here is the steps to success:


1. Enable the required Windows Features to wake up IIS7 and WAS. You will find these in the helpful links below.


2. Configuration file C:WindowsSystem32inetsrvconfigapplicationHost.config must be modified to enable the required protocols on your web site and application. You can modify the file yourself, or use command-line utilities.



To enable net.tcp on the web site, if it is not already:


%windir%system32inetsrvappcmd.exe set site “Default Web Site” -+bindings.[protocol='net.tcp',bindingInformation='808:*']


To enable net.tcp on your application (my app is named WasServices) within that web site, if it is not already:


%windir%system32inetsrvappcmd.exe set app “Default Web Site/WasServices” /enabledProtocols:http,net.tcp



Here is an exerpt from the applicationHost.config file showing the site and application settings:




  151             <site name=”Default Web Siteid=”1serverAutoStart=”true“>


  152                 <application path=”/“>


  153                     <virtualDirectory path=”/physicalPath=”%SystemDrive%inetpubwwwroot” />


  154                 </application>


  155                 <application path=”/WasServicesapplicationPool=”WasHostingenabledProtocols=”http,net.tcp“>


  156                     <virtualDirectory path=”/physicalPath=”C:inetpubwwwrootWasServices” />


  157                 </application>


  158                 <bindings>


  159                     <binding protocol=”net.tcpbindingInformation=”808:*” />


  160                     <binding protocol=”net.pipebindingInformation=”*” />


  161                     <binding protocol=”net.msmqbindingInformation=”localhost” />


  162                     <binding protocol=”msmq.formatnamebindingInformation=”localhost” />


  163                     <binding protocol=”httpbindingInformation=”*:80:” />


  164                 </bindings>


  165             </site>


3. Prepare the application in your application folder (C:inetpubwwwrootWasServices)



Create a service file (WasServices.svc) that points to your existing WCF service library:




    1 <%@ ServiceHost Service=“WasServices.Service1″ %>


 


Create a web.config file that specifies the service’s endpoints:





    1 <?xml version=”1.0encoding=”utf-8“?>


    2 <configuration>


    3     <system.serviceModel>


    4         <services>


    5             <service name=”WasServices.Service1


    6                     behaviorConfiguration=”MEX“>


    7                 <endpoint address=”wsHttp


    8                           binding=”wsHttpBinding


    9                           contract=”WasServices.IService1“/>


   10                 <endpoint address=”netTcp


   11                           binding=”netTcpBinding


   12                           bindingConfiguration=”NetTcpBinding_Common


   13                           contract=”WasServices.IService1“/>


   14                 <endpoint address=”mex


   15                           binding=”mexHttpBinding


   16                           contract=”IMetadataExchange” />


   17             </service>


   18         </services>


   19         <behaviors>


   20             <serviceBehaviors>


   21                 <behavior name=”MEX“>


   22                     <serviceMetadata httpGetEnabled=”true“/>


   23                 </behavior>


   24             </serviceBehaviors>


   25         </behaviors>


   26         <bindings>


   27             <netTcpBinding>


   28                 <binding name=”NetTcpBinding_Common“>


   29                     <reliableSession enabled=”true“/>


   30                     <security mode=”None“/>


   31                 </binding>


   32             </netTcpBinding>


   33         </bindings>


   34     </system.serviceModel>


   35 </configuration>


 


Place the release-compiled DLL created from the WCF Service Library in a new folder named Bin.


4. At this point, you can browse and see the familiar “You have created a service.” page for Service1.


5. Write your proxy file and config file.



WAS and IIS7 decide the address for your service, and it is not intuitive.




    1 <?xml version=”1.0encoding=”utf-8” ?>


    2 <configuration>


    3     <system.serviceModel>


    4         <client>


    5             <endpoint address=”net.tcp://localhost/WasServices/WasServices.svc/netTcp


    6                       binding=”netTcpBinding


    7                       bindingConfiguration=”NetTcpBinding_IService1


    8                       contract=”WasServices.IService1


    9                       name=”NetTcpBinding_Common” />


   10         </client>


   11         <bindings>


   12             <netTcpBinding>


   13                 <binding name=”NetTcpBinding_Common“>


   14                     <reliableSession enabled=”true” />


   15                     <security mode=”None” />


   16                 </binding>


   17             </netTcpBinding>


   18         </bindings>


   19     </system.serviceModel>


   20 </configuration>


 


The /netTcp at the end of the address is due to the address specified in the service’s web.config file. The address was given there as simply netTcp. This is because IIS7 and WAS decide your address based on the available bindings and ports you specified in the applicationHost.config file using appcmd.exe. Since my enabled protocols are http and net.tcp and the only open tcp port is 808, you will not see a port number in the address. The same would go for my wsHttpBinding since the only allowable port is 80.


I’m proud to be the fourth, and maybe final, member of the “Got WAS to work” club. If anyone wants to join, and needs help to get in… please let me know.




Here are some helpful links for those of you having problems:


Sep 072008
 

I really need to read up on new features when a major release comes out. Just a few weeks ago I learned of a great “new” SQL 2005 function… ROW_NUMBER(). Just in time since SQL 2008 is already out.

For me, this function means a lot less temp tables. I would typically create a temp table with an ID INT IDENTITY(1,1) column to create an DisplayOrder, BatchID, etc. used to group or join on later. Books Online describes the function as “Returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition.” The syntax is simple, and looks like:

ROW_NUMBER() OVER (ORDER BY ID DESC)

For this example, the data I want to bring back with a DisplayOrder column looks like:

pers_subs_data

Without ROW_NUMBER(), using a table variable with an identity column:

DECLARE @Subs TABLE (DisplayOrder INT IDENTITY(1,1), [Address] VARCHAR(100), Operation VARCHAR(50), [Contract] VARCHAR(50))

INSERT INTO @Subs ([Address], Operation, [Contract])
SELECT [Address]
    , Operation
    , [Contract]
FROM PersistentSubscribers
WHERE Operation = ‘OnEvent2′
ORDER BY ID DESC

SELECT * FROM @Subs

With ROW_NUMBER(), look how beautiful:

SELECT DisplayOrder = ROW_NUMBER() OVER (ORDER BY ID DESC)
    , [Address]
    , Operation
    , [Contract]
FROM PersistentSubscribers
WHERE Operation = ‘OnEvent2′

The results from both methods looks like:

 

row_number_results