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:

 Posted by at 4:21 pm
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:

 Posted by at 1:48 am
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:


 Posted by at 4:58 am
Aug 312008
 

Why clutter your inbox with error messages? Why make special code provisions for users to receive error messages via email? Why not log your error messages and have users subscribe to receive them in their favorite RSS aggregator?


If you are logging your exceptions already, you may find it easier to provide a syndication service. The process is ridiculously simple, and starts by creating a new project using the “Syndication Service Library” template. This template creates everything for you. All you need to do now is fill the SyndicationFeed with SyndicationItem objects.


Add a new class file called Feeds.cs:

 



    1 using System;


    2 using System.Linq;


    3 using System.ServiceModel;


    4 using System.ServiceModel.Syndication;


    5 using System.ServiceModel.Web;


    6 


    7 namespace SyndicationService


    8 {


    9     [ServiceContract]


   10     [ServiceKnownType(typeof(Atom10FeedFormatter))]


   11     [ServiceKnownType(typeof(Rss20FeedFormatter))]


   12     public interface IFeeds


   13     {


   14         [OperationContract]


   15         [WebGet(UriTemplate = “{type}?env={env}&app={app}”, BodyStyle = WebMessageBodyStyle.Bare)]


   16         SyndicationFeedFormatter CreateFeed(string type, string env, string app);


   17     }


   18 


   19     public class Feeds : IFeeds


   20     {


   21         public SyndicationFeedFormatter CreateFeed(string type, string env, string app)


   22         {


   23             SyndicationFeed feed = CreateSyndicationFeed(type, env, app);


   24 


   25             // Return ATOM or RSS based on query string


   26             // rss -> http://localhost:8000/Feeds/Errors?env=Production&app=MyAppName


   27             // atom -> http://localhost:8000/Feeds/Errors?env=Production&app=MyAppName&format=atom


   28             string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[“format”];


   29             SyndicationFeedFormatter formatter = null;


   30             if (query == “atom”)


   31             {


   32                 formatter = new Atom10FeedFormatter(feed);


   33             }


   34             else


   35             {


   36                 formatter = new Rss20FeedFormatter(feed);


   37             }


   38 


   39             return formatter;


   40         }


   41 


   42         private static SyndicationFeed CreateSyndicationFeed(string type, string env, string app)


   43         {


   44             SyndicationFeed feed;


   45             switch (type.ToLower())


   46             {


   47                 case “errors”:


   48                     feed = CreateErrorsFeed(type, env, app);


   49                     break;


   50                 default:


   51                     feed = new SyndicationFeed(


   52                         String.Format(“Feed is unavailable – Type: {0} / Environment: {1} / Application: {2}”,


   53                         type, env, app), null, null);


   54                     break;


   55             }


   56             return feed;


   57         }


   58 


   59         private static SyndicationFeed CreateErrorsFeed(string type, string env, string app)


   60         {


   61             ApplicationLogDataContext db = new ApplicationLogDataContext();


   62 


   63             SyndicationFeed feed = new SyndicationFeed


   64             {


   65                 Title = new TextSyndicationContent(String.Format(“{0} {1} {2}”, env, app, type)),


   66                 Description = new TextSyndicationContent(


   67                     String.Format(“Application error syndication for the {0} applicaiton ({1}).”, app, env)),


   68                 Items = from e in db.Exceptions


   69                         where e.Environment == env && e.Application == app


   70                         select new SyndicationItem


   71                         {


   72                             Title = new TextSyndicationContent(e.Message),


   73                             Content = new TextSyndicationContent(e.StackTrace)


   74                         }


   75             };


   76             return feed;


   77         }


   78     }


   79 }



Modify the App.config file:

 



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


    2 <configuration>


    3     <configSections>


    4     </configSections>


    5     <connectionStrings>


    6         <add name=”SyndicationService.Properties.Settings.ApplicationLogConnectionString


    7             connectionString=”Data Source=Scorpion;Initial Catalog=ApplicationLog;Integrated Security=True


    8             providerName=”System.Data.SqlClient” />


    9     </connectionStrings>


   10     <system.serviceModel>


   11         <services>


   12             <service name=”SyndicationService.Feeds“>


   13                 <host>


   14                     <baseAddresses>


   15                         <add baseAddress=”http://localhost:8000/” />


   16                     </baseAddresses>


   17                 </host>


   18                 <endpoint contract=”SyndicationService.IFeeds


   19                           address=”Feeds


   20                           binding=”webHttpBinding


   21                           behaviorConfiguration=”WebHttpBinding_Common“/>


   22             </service>


   23         </services>


   24         <behaviors>


   25             <endpointBehaviors>


   26                 <behavior name=”WebHttpBinding_Common“>


   27                     <webHttp/>


   28                 </behavior>


   29             </endpointBehaviors>


   30         </behaviors>


   31     </system.serviceModel>


   32 </configuration>



You will need to adjust your project’s Debug options to have command arguments that look similar to the following to F5-debug your service.



“/client:iexplore.exe” “/clientArgs:http://localhost:8000/Feeds/Errors?env=Production&app=GeoTracker”


Press F5 to test it out.


Here is the IE7 RSS viewer:


IE7_RSS_Viewer


Here is your RSS aggregator viewing the same feed:


RSS_Aggregator


You will, of course, want to add some additional information to the content of your SyndidationItem, a bogus phrase works for this example.


Also, it is unusual that you would care to keep your exception details around for a long period of time. Since this is a syndicated feed of application errors, you should make special arrangements to archive or delete your exception log on a regular basis. This will not only keep your insert and select times low, but will also alleviate the burden placed on a new subscriber when all of the exceptions from the database appear at once. An alternative would also be to modify the LINQ in the code above to only bring back exceptions from the last 7-60 days depending on your counts. I already archive my exceptions to a master exception repository for all environments by way of an ETL job. This way I can report on my errors without disturbing the live environments too.

 Posted by at 8:37 am
Aug 272008
 

I decided to come out of my cave and look around 3.5 a bit. I haven’t read much about extension methods, but find them quite useful. They are nothing more than a syntactically superior static helper method. Let’s look at a quick example so I can get back to coming up with more excuses to use them everywhere.


I like to batch my database calls as much as possible to avoid repeated opening/closing of connections, etc. To do this, I pass a bunch of ID values into a stored procedure as a comma-separated string. In the stored proc, I break the string apart with everyone’s favorite table-valued function fn_MakeTable() to make a table of IDs. Then I can JOIN, UPDATE, or INSERT as needed.


So let’s say I have a collection of Orders which I can easily convert to an array of OrderID integers with LINQ. My new best friend to create a comma-separated string of OrderIDs is the following.



    1 using System;


    2 using System.Configuration;


    3 


    4 namespace Common


    5 {


    6     public static class ArrayHelper


    7     {


    8         public static string ToCsv<T>(this T[] array)


    9         {


   10             Converter<T, string> converter = (t) =>


   11                 {


   12                     return t.ToString();


   13                 };


   14             return ToCsv(array, converter);


   15         }


   16 


   17         public static string ToCsv<T>(this T[] array, Converter<T, string> converter)


   18         {


   19             CommaDelimitedStringCollection csv = new CommaDelimitedStringCollection();


   20             foreach (T t in array)


   21             {


   22                 csv.Add(converter(t));


   23             }


   24             return csv.ToString();


   25         }


   26     }


   27 }


 


You’ll see that I have two ToCsv() methods. The first takes a generic array using the this keyword and uses .ToString() as a default converter to string. The second method requires you to additionally pass in a converter to convert the object of type T to a string. Take those converted strings, add them to a CommaDelimitedStringCollection and .ToString() that collection to a full CSV string of integer values.


There are two ways to call these extension methods. The first is the more familiar way. Since they are really nothing more than static helper methods, call them just like any other:



   14             int[] array = { 123, 456 };


   15             string csv = Common.ArrayHelper.ToCsv(array);


 


The second is the more elegant and more intuitive way. Call it as if it was built into the Framework:



   14             int[] array = { 123, 456 };


   15             string csv = array.ToCsv();


 


You may be wondering, what if I write a method that matches the signature of a built-in method like .ToString(). Well, the built-in methods take precedence over extension methods, so array.ToString() will still appear as System.Int32[]. To get your new meaning of .ToString(), you just have to call it in the static helper method way detailed above.


For a generic array of T, you will likely want to provide your own Converter if T’s .ToString() method does not display the information you want to show in the CSV string. Below is a lame example of a converter. It takes the int value, converts it to the char value.



   21             Converter<int, string> converter = (i) =>


   22             {


   23                 return ((char)i).ToString();


   24             };


   25             string csv = array.ToCsv(converter);


 


I think something so simple, and definitely re-usable, would benefit any developer.

 Posted by at 2:07 am
Aug 242008
 

Our friends at Microsoft may have slipped one in on us. After installing the 3.5 Framework Service Pack 1, it appears that you no longer need the [DataContract] or [DataMember] attributes on your DataContracts and DataMembers. I’m not sure what the motivation was for this “enhancement”, but it caused some trouble for me the other day.

For this example I will be using the base project VS2008 gives you when you create a new WCF Service Library. I am simply adding a NestedType to the CompositeType given in the base project.

Before installing SP1, having code as it appears below would cause an error during Metadata Exchange that reads something like “Metadata contains a reference that cannot be resolved”. Notice that CompositeType‘s NestedObject is marked as [DataMember] and also notice that the NestedType class is not marked as [DataContract] and has no [DataMember] attributes. Adding [DataContract] on NestedType and [DataMember] on IsVisible will clear this error and everything will work as expected. 

   24     [DataContract]

   25     public class CompositeType

   26     {

   27         bool boolValue = true;

   28         string stringValue = “Hello “;

   29         NestedType nestedObject = new NestedType();

   30 

   31         [DataMember]

   32         public bool BoolValue

   33         {

   34             get { return boolValue; }

   35             set { boolValue = value; }

   36         }

   37 

   38         [DataMember]

   39         public string StringValue

   40         {

   41             get { return stringValue; }

   42             set { stringValue = value; }

   43         }

   44 

   45         [DataMember]

   46         public NestedType NestedObject

   47         {

   48             get { return nestedObject; }

   49             set { nestedObject = value; }

   50         }

   51     }

   52 

   53     public class NestedType

   54     {

   55         bool isVisible = false;

   56 

   57         public bool IsVisible

   58         {

   59             get { return isVisible; }

   60             set { isVisible = value; }

   61         }

   62     }

 

The same code in use after SP1 will not cause this error. WCF will interpret from CompositeType‘s [DataContract] attribute and NestedObject‘s [DataMember] attribute that you meant to put [DataContract] on NestedType. So what’s the big deal, right? WCF is doing me a solid by guessing at what I meant to do. To me, this violates the repeated opt-in theme present in WCF. For every other important decision, the developer must write code to opt-in to a feature. For example, TransactionFlow defaults to false so we don’t use the client’s incoming transaction with explicitly writing code that says to do so.

This is clearly not on the same level as TransactionFlow. But why does it assume something about my objects? Why does it assume that every member of my object should be a DataMember?

I noticed this new “feature” when troubleshooting some code that had different namespace names specified in the DataContract attribute. Since the NestedType did not have a [DataContract] attribute, the namespace was using the original namespace name. The equivalent of CompositeType came through correctly, but the NestedObject had no value.

 Posted by at 1:57 am
Jun 292008
 

Enterprise applications store their data in a relational database. Our code reads the data stored in tables with many complex joins and business rule laden queries. We take the results of those queries and construct an equally complex business entity that is used by our application logic. Most developers, myself excluded, hate working with the database. Writing, modifying, or even seeing T-SQL causes some developers to itch. LINQ to SQL serves as a partially effective Hydrocortisone to relieve the itch. But they still need to maintain the schema, write SQL-mindful LINQ queries, and deal with the constant DataContext updates.

 

Imagine a world where you no longer need to translate your complex business entities to and from relational tables.  A world where there is no database backing store. A world where we create our business entities and store them in memory. Even better, in memory on a shared resource. Does it sound like an inconceivable futuristic developer heaven? Well it probably is, but this is really cool stuff in the works.

 

Enter the Microsoft project code-named “Velocity.” The blurb on the overview page reads:


“Velocity” is a distributed in-memory application cache platform for developing scalable, high-performance applications. “Velocity” can be used to cache any CLR object and provides access through simple APIs. The primary goals for “Velocity” are performance, scalability and availability.


I have been working with the Digipede Network, the leading grid computing software solution, for a few months. The Velocity architecture sounds remarkably similar to Digipede’s. I have seen the great benefits of the Digipede Network and have high expectations for Velocity.


The Digipede Network, for those of you that haven’t seen it yet, consists of a central Digipede Server and one or many Digipede Agents. The server receives client requests and assigns tasks to the agents. The client uses the Digipede API to communicate with the server. The API pretty much wraps client-to-server and server-to-client WSE2 web service calls. This architecture allows you to take almost any CPU-intensive process and spread the workload among tens or hundreds of commodity or server grade machines. The result is a very high performing and easily scaled system with few code changes from what you do today.


Digipede Network Diagram:


Digipede Network Diagram


Digipede only works in this configuration, while Velocity has two proposed deployment models. You can have a “caching tier”, similar to Digipede’s Server and Agent configuration, or you can house Velocity as a Caching Service directly in IIS7. I don’t know how communications will be handled between the client API and the “caching tier”, but I assume it will be some sort of service calls (WCF perhaps). All CLR objects stored in the Velocity cache must be marked [Serializable] just as task worker classes must be to work with Digipede.


The Velocity API looks simple enough too. It exposes intuitive Get() and Put() methods where you call the cache by name. I can see how versioning of the cached objects might get tricky. Your application will also need a new configSection that specifies the deployment mode, locality, and also contains the list of cache hosts. As this is a distributed solution, the standard virtual machine playground doesn’t work too well to really test this out.


This looks promising, and I’ll be following the progress of the project closely.


Download Velocity


Download the Velocity CPT 1 samples

 Posted by at 4:11 am
Mar 272008
 

I was looking for guidance on this topic, and came up with nothing. I’m sure people are doing this, but can’t find any info. For anyone looking like I was, here’s how to do it.


It’s much simpler than I imagined, thanks to WCF. You can programmatically create your endpoint, binding, and channel inside your service. This would require that the address be hard-coded and require a recompile to change the address or binding. As long as your host’s app.config or web.config has a client endpoint specifying the contract, you don’t have to go through all that work. Your service is simply a client of another service, so your code looks just like that of a client of your service. Furthermore, changing the address or binding is as simple as changing config file values.


Service code:




    1 using System;


    2 using System.ServiceModel;


    3 using DataContracts;


    4 namespace ServiceImplementation


    5 {


    6     [ServiceContract]


    7     public interface IEmailService


    8     {


    9         [OperationContract]


   10         void Send(DataContracts.MailMessage msg);


   11     }


   12 


   13     public class EmailService : IEmailService


   14     {


   15         [OperationBehavior]


   16         public void Send(DataContracts.MailMessage msg)


   17         {


   18             // Open client proxy for legacy web service


   19             using (LegacyEmailServiceClient proxy =


   20                 new LegacyEmailServiceClient())


   21             {


   22                 proxy.SendEmail(msg.To,


   23                     msg.CC,


   24                     msg.Bcc,


   25                     msg.Body,


   26                     msg.Attachments);


   27             }


   28         }


   29     }


   30 }


   31 



Host’s app.config:




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


    2 <configuration>


    3   <system.serviceModel>


    4     <bindings>


    5       <basicHttpBinding>


    6         <binding name=BasicHttpBinding_Common>


    7           <security mode=None/>


    8         </binding>


    9       </basicHttpBinding>


   10       <netTcpBinding>


   11         <binding name=NetTcpBinding_Common>


   12           <security mode=None/>


   13         </binding>


   14       </netTcpBinding>


   15     </bindings>


   16     <client>


   17       <endpoint address=http://www.gotjeep.net/legacy/email.asmx


   18           binding=basicHttpBinding


   19                 bindingConfiguration=BasicHttpBinding_Common


   20           contract=LegacyEmailServiceClient


   21                 name=LegacyEmailServiceClient />


   22     </client>


   23     <services>


   24       <service name=ServiceImplementation.EmailService


   25               behaviorConfiguration=returnFaults>


   26         <host>


   27           <baseAddresses>


   28             <add baseAddress=http://localhost:8080/EmailService />


   29             <add baseAddress=net.tcp://localhost:8088/EmailService />


   30           </baseAddresses>


   31         </host>


   32         <endpoint name=NetTcpBinding_EmailService


   33                   binding=netTcpBinding


   34                   bindingConfiguration=NetTcpBinding_Common


   35                   contract=ServiceImplementation.IEmailService/>


   36         <endpoint name=BasicHttpBinding_EmailService


   37                   binding=basicHttpBinding


   38                   bindingConfiguration=BasicHttpBinding_Common


   39                   contract=ServiceImplementation.IEmailService/>


   40       </service>


   41     </services>


   42     <behaviors>


   43       <serviceBehaviors>


   44         <behavior name=returnFaults >


   45           <serviceMetadata httpGetEnabled=true />


   46         </behavior>


   47       </serviceBehaviors>


   48     </behaviors>


   49   </system.serviceModel>


   50 </configuration>


 Posted by at 2:08 am
Mar 252008
 

Before you try running a Console Application or Windows Service application that hosts a WCF service with basicHttpBinding or wsHttpBinding, see this MSDN article about “Configuring HTTP and HTTPS”. If you are WAS-hosted or Web App-hosted, the urlacl entries are made on your behalf.


You can view the current entries with “netsh http show urlacl“. To make the changes, you’ll need to “Run as Administrator” when going into your Command Prompt.


I decided to use the following command:



netsh http add urlacl url=http://+:8080/ user=Everyone


You should adjust the ports and/or path as necessary for your situation:



netsh http add urlacl url=http://+:8080/MyConsoleAppHostedService user=DOMAINuser


netsh http add urlacl url=http://+:8091/MyWindowsServiceHostedService user=SYSTEM

 Posted by at 1:03 am