Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Sunday, June 13, 2010

The Standard WCF Service Application Template for F#

In the interest of building up a larger set of installed templates for F#, I've ported the standard C# WCF Service Application to F# and packaged it into a .vsi file.

Here are a few snippets from the solution that the template creates:

IService1.fs
namespace FSharpWcfServiceApplicationTemplate.Contracts

open System.Runtime.Serialization
open System.ServiceModel

[<ServiceContract>]
type IService1 =
    [<OperationContract>]
    abstract GetData: value:int -> string
    [<OperationContract>]
    abstract GetDataUsingDataContract: composite:CompositeType -> CompositeType
Service1.fs
namespace FSharpWcfServiceApplicationTemplate

open System
open FSharpWcfServiceApplicationTemplate.Contracts 

type Service1() =
    interface IService1 with
        member x.GetData value =
            sprintf "%A" value
        member x.GetDataUsingDataContract composite =
            match composite.BoolValue with
            | true -> composite.StringValue <- 
                          sprintf "%A%A" composite.StringValue "Suffix"
            | _ -> "do nothing" |> ignore
            composite
You can download the installer from here and get the full source from http://github.com/dmohl/FSharpWcfServiceApplicationExample.

Sunday, April 4, 2010

A Distributed Hash Table (DHT) in F#: Moving from .NET Remoting to WCF

In my last post I provided a start to a distributed hash table (DHT) using F#, the Chord protocol, and .NET remoting.  In this post, I'll show how that code has changed due to a migration from .NET remoting to WCF.

As a side note, I've codenamed this little project Polyphony.

Here's a summary of the primary changes:

- Chord.fs has gone away.
- ChordServer now contains contract specific information.
- ChordClient now calls the service operations.

The primary code changes are provided below.  The complete project is available at http://github.com/dmohl/polyphony.

ChordServer.fs
module ChordServer

open System
open System.ServiceModel
open System.Collections
open System.Configuration
open System.Net

[<ServiceContract>]  
type IChordServer = interface   
    [<OperationContract>]  
    abstract PutValueByKey : key:obj -> value:obj -> unit  
    [<OperationContract>]  
    abstract GetValueByKey : value:obj -> obj  
end

[<ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)>]
type ChordServer = class
    val hashTable : Hashtable
    new () = {hashTable = new Hashtable()}
    interface IChordServer with
        member x.PutValueByKey key value =
            x.hashTable.Add(key, value)
        member x.GetValueByKey key =
            x.hashTable.Item(key)
end

let logException (ex:Exception) =
    Console.WriteLine("Error: {0}", ex.Message)
    Console.WriteLine(ex.Source)
    
let Initialize () =
    try
        let localServer = ConfigurationManager.AppSettings.Item("LocalServer")
        Console.WriteLine("Starting Server: {0}", localServer)
        let host = new ServiceHost(typeof<ChordServer>)
        host.AddServiceEndpoint(typeof<IChordServer>,
                    new NetTcpBinding(), localServer) |> ignore       
        host.Open()
        Some(host)
    with
    | ex -> logException ex
            None
           
let Stop (host: ServiceHost option)  =
    try
        match host with
        | None -> ()
        | Some(host) ->
            if host.State <> CommunicationState.Closed then
                host.Close()
                Console.WriteLine("Stopping Server")               
    with
    | ex -> logException ex           
ChordClient.fs
module ChordClient

open System
open System.Configuration
open System.ServiceModel

let remoteServer = ConfigurationManager.AppSettings.Item("RemoteServer")
let localServer = ConfigurationManager.AppSettings.Item("LocalServer")

let CallServer (server:string) (operationContract:string) (inputArguments:string[]) =
    let service = new ChannelFactory<ChordServer.IChordServer>(
                        new NetTcpBinding(), server)  
    try                    
        try
            let proxy = service.CreateChannel()        
            let result = match operationContract with
                         | "put" ->
                             proxy.PutValueByKey inputArguments.[1] inputArguments.[2] 
                             "Put Complete" :> obj
                         | "get" -> 
                             proxy.GetValueByKey inputArguments.[1]     
                         | _ -> "Unknown" :> obj 
            result             
        with
        | ex -> 
            Console.WriteLine ex.Message
            "Unknown" :> obj
    finally                 
        service.Close |> ignore

let RunCommand(input:string) : unit =
    let inputArguments = input.Split(' ')
    let result = 
        match inputArguments.[0] with
        | "put" ->
            CallServer localServer inputArguments.[0] inputArguments |> ignore
            sprintf "PUT Key:%A Value:%A" inputArguments.[1] inputArguments.[2] :> obj   
        | "get" -> 
            let rec getValue server =
                let value = CallServer server inputArguments.[0] inputArguments
                match value with
                | null -> getValue remoteServer    
                | _ -> value
            getValue localServer
        | _ -> "unknown command" :> obj   
    Console.WriteLine(result) |> ignore

Sunday, January 18, 2009

Building a RESTful Service with WCF and F#

There has been a lot of discussion recently, centered on the idea of making services RESTful.  REST (Representational State Transfer) "is a key design idiom that embraces a stateless client-server architecture in which the web services are viewed as resources and can be identified by their URLs." (1)  This post will provide a simple example of creating a RESTful service with WCF and F#. 

Creating the project:

The first step in building our example is to create a new F# Library project.  I chose to name this project "FSharpWCF".  The next step is to add references to System.Runtime.Serialization, System.ServiceModel, and System.ServiceModel.Web.

Building the contracts:

The code looks only slightly different than an equivalent in a language such as C#, though there are a couple of key things that should be pointed out.  First, System.ServiceModel.Web has been opened.  This is the namespace that allows us to use the WebGet attribute.  The WebGet attribute allows us to set a UriTemplate that will be used for navigation to our service.  Our UriTemplate is set to "temp/{value}", which indicates that navigating to the base URL/temp/{value} will return the result of the service.  {value} associates directly with the parameter named value in our GetData operation.  The last important thing to notice is the assignment of the name "value" to the request argument of the GetData operation (i.e. abstract GetData : value:string -> string).  For more information on this, see this post by Ted Neward.

#light
namespace FSharpWCF

open System
open System.Runtime.Serialization
open System.ServiceModel
open System.ServiceModel.Web

[<ServiceContract>]
type IGetDataService = interface 
    [<WebGet(UriTemplate="temp/{value}")>]
    [<OperationContract>]
    abstract GetData : value:string -> string
end

type GetDataService() = 
    interface IGetDataService with
        member this.GetData value =
            sprintf "You entered: %s" value


Adding an app.config file:

Since an F# library doesn't have App.config as an option when adding a new item, you will need to create the file in the project directory manually, then add it as an existing item.  The main thing to notice is the addition of the endpointBehaviors section and the association of this behaviorConfiguration to the endpoint.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="FSharpWCF.GetDataService" behaviorConfiguration="FSharpWCF.GetDataServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8080/FSharpWCF/GetDataService/" />
          </baseAddresses>
        </host>
        <endpoint binding="webHttpBinding" contract="FSharpWCF.IGetDataService" 
                  behaviorConfiguration="Web">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="FSharpWCF.GetDataServiceBehavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="Web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>


Testing the service:

To test your new service, do the following:

1. Right click on the FSharpWCF project and select properties.
2. Navigate to the Debug tab, change the start action to "Start external program:" and locate the WcfSvcHost.exe on your development machine (i.e. C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\WcfSvcHost.exe).
3. Add the following "command line arguments:" /service:FSharpWCF.dll /config:FSharpWCF.dll.config
4. Set "Working directory:" to the directory of these DLLs.

You should now be able to launch the service by running the project from Visual Studio.  This will host your service with WcfSvcHost and allow you to access your service from a browser.  (Note: WcfSvcHost should only be used for testing purposes.  You should never use this as a production host.)

To use the service, open a browser and navigate to http://localhost:8080/FSharpWCF/GetDataService/temp/2 and you should receive <string>You entered: 2</string> as a result. 

Conclusion:

That's all there is to it.  The service can now be viewed as a resource that can be identified by it's URL. 


Resources:

1. http://java.sun.com/developer/technicalArticles/WebServices/restful/