« Back
in C# Fsharp events read.

F# Events From C# Application.

If we had some kind of long running task in an F# library, we want to be able to fork that off and execute that task in parallel from our main program and deal with it later when some interesting event is fired.

This article is about listening to that event in a C# application but if you're interested in architecting how to organize your events, have a look at this previous article on the domain events pattern.

F# is a great language for data manipulation and data science so it is entirely possible that the library is doing some heavier computation but we don't want that computation to block the main thread.

In our quest to learning F# and making it more mainstream, I've started doing my data manipulation and re-usable libraries in F#. Our talent still remains in C# so we'll assume our main program is something like a console app, or an MVC app.

Problem Context

Suppose our console application (C#) needed to perform some kind of File IO with a huge set of data. That is the job for the F# library which the main console application will make a call to. We need to execute that in another thread and continue work so that the user doesn't get stuck waiting there for that task to complete.

When the task does complete, we need to know what the final dynamic output file name is so that we can do something else with it - like push it to an FTP server, or to a SQL server.

The F# Library

namespace EventFiringLib

open System  
open System.Threading

module FileManager =

    //This is the custom EventArgs that the calling C# program wants to use and
    //acquire additional information about the file task. In this case we want the output filename after
    //task completion
    type FileTaskEventArgs(filename : string) =
        inherit System.EventArgs()
        member this.Filename = filename


    type FileTask() = 

        let event = new DelegateEvent<EventHandler<FileTaskEventArgs>>()

        [<CLIEvent>]
        member this.TaskCompletedEvent = event.Publish

        member this.ExecuteTask = 
            //do some long running task
            Thread.Sleep(5000);

            //trigger the event
            event.Trigger([| this;new FileTaskEventArgs(DateTime.Now.ToString("yyyy-MM-dd")+".csv") |])
            ignore

Our F# library has two classes. One that is the custom event arguments which will get passed to the main console application and the other is the actual class that does the file IO.

Notice that the FileTaskEventArgs inherits from EventArgs. This should make it compatible with other .NET languages. In this custom argument, we will return the filename of the final output file to the main program.

The FileTask class has a delegate there called event which has the signature of an EventHandler that takes our FileTaskEventArgs.

The way F# exposes the event is by making a public member. Here we have TaskCompletedEvent being exposed by calling event.Publish. The <CLIEvent> attribute is necessary to compile the event into a .NET event. That event has the underlying add and remove methods for handlers which we will attach to it.

Finally we trigger the event after the long running task has completed; Simulated here by a 5 second delay.

event.Trigger looks a little funny but it's an array of arguments -- The object and the new FileTaskEventArgs with a dynamic filename.

The Main Program Listening To The F# Event

Make sure that you add the F# project to the Console application project. Additionally you'll need to go into the Assemblies and add the FSharp.Core dll to the console app:

Adding FSharp.Core to C# Project

The Program.cs code:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using EventFiringLib;

namespace FSharpEventExample  
{
    class Program
    {
        static void Main(string[] args)
        {
            var fileManager = new FileManager.FileTask();
            fileManager.TaskCompletedEvent += new EventHandler<FileManager.FileTaskEventArgs>(OnFinishedFileTask);

            Task doFileTask = new Task(() =>
                {
                    Console.WriteLine("Executing File Task...");
                    var result = fileManager.ExecuteTask;
                }
            );
            doFileTask.Start();

            Console.WriteLine("Continuing Execution...");

            Console.ReadLine();

        }

        private static void OnFinishedFileTask(object sender, FileManager.FileTaskEventArgs e)
        {
            Console.WriteLine("Completed File Task with output file: " + e.Filename);
        }
    }
}

We attach our handler to the TaskCompletedEvent we exposed earlier and run the file task in a separate thread with the Task library.

Our final output is as you would expect:
C# and F# event output

If you're interested in the source code, I have made it available on this bitbucket repository:
FSharpEventExample

Thanks for reading!

comments powered by Disqus