Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep settings in sync between forms application and windows service (or any n-tier, really)

I have a Windows Service that performs a number of periodic activities, and I want to change the settings of this service from a Windows Forms app. I'm not sure, though, about the best way to make sure the service has the most updated user preferences in it (how often to run, what folders to use for things, whatever else the user can specify). The user can change settings any time, at will, and I'd like the service know about it almost immediately. Here are the options I'm weighing:

  1. The form and service share use the same "Settings" object from a third, shared project, and the form uses a WCF "UpdateSettings(newSettings)" call to let the service know that there have been changes (or, optionally, a call to update each individual setting, though this seems like a lot over different calls). I currently use WCF for basic messages, but the settings object can be huge, since there's a lot of other stuff in there
  2. Form and Service use a common config file (XML, or the same settings object from #1, but serialized to disk). The Form just writes a new copy of the object after it's been changed, and the service checks every so often and picks it up if it's new, updating its copy of the settings
  3. Same as #2, but with a basic WCF call that that tells the service to go get the settings. Essentially, an "on-demand" instead of "polling" version of #2.

I know best is subjective, but I'm interested in any obvious pro or con reasons for these choices. Since I'll have to save my settings between runnings of the application (reboots, etc), I'll have to serialize the settings to disk anyway, so I'm already leaning towards #2 or #3. I'll need a place on disk where I can save the settings, but maybe the AppData folder will work okay, though that will only allow Administrators to change the settings, since they're the only ones that have permission to write to this location (where every user, including the service account, can read it).

Thanks for your insight!

like image 507
SqlRyan Avatar asked Jul 02 '10 16:07

SqlRyan


1 Answers

I kinda use your number 2.

But I'm only working in .NET 2 with my application, but it should still apply.

I have a settings class that I use across my 2 programs. Inside this settings class I setup a FileSystemWatcher object that looks at the Settings file.

If the settings file is updated by the other application, my current gets an event trigger to indicate that the settings need to reload.

You can also apply the same principle in your settings screen so that if the (service) other application updates anything during the settings edit, that is reflected in your screen.

I use the AppData (my company/application name directory) to store the file.

The other thing to bear in mind, is that there can be locking on the file while it is being written so you can either use a temp name save, delete old, rename temp method or put some protective locking on the file when reading after the filewatcher event fires that changes have been made.

I use this approach in my FileSystemWatcher before proceeding

IPSDependency.FileSystem.WaitForLockOnFile(Me.mFilePath)

the code for that is like this. (upon reading this now, there may be a better method my using some sleep in here to reduce CPU thrashing)

Public Shared Function IsLockAvailable(ByVal filename As String, ByVal fnfIsOK As Boolean) As Boolean
    Dim fi As FileInfo
    fi = New FileInfo(filename)
    Return IsLockAvailable(New FileInfo(filename), fnfIsOK)
End Function

Public Shared Function IsLockAvailable(ByVal theFile As FileInfo, ByVal fnfIsOK As Boolean) As Boolean
    Dim fs As FileStream
    Try
        If theFile.Exists Then
            fs = New FileStream(theFile.FullName, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
            fs.Close()
            Return True
        Else
            Return fnfIsOK
        End If
    Catch ex As IOException
        'we just let the exception go, because we are only testing the file rather than trying to use it.
        Return False
    End Try
End Function

Public Shared Sub WaitForLockOnFile(ByVal theFilename As String)
    WaitForLockOnFile(New FileInfo(theFilename))
End Sub

Public Shared Sub WaitForLockOnFile(ByVal theFile As FileInfo)
    Dim lockAvailable As Boolean
    If theFile.Exists Then
        While Not lockAvailable
            lockAvailable = IsLockAvailable(theFile, False)
        End While
    End If
End Sub
like image 160
Paul Farry Avatar answered Sep 18 '22 17:09

Paul Farry