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

浪尽此生 提交于 2019-11-30 14:42:29

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

Assuming that everything is running on the same machine, how about this:

  1. Define a common c# structure that defines the settings. All projects include this .cs file. Define this class as a struct with a StructLayout of Sequential or Explicit so it can be mapped directly into unmanaged shared memory. For example:

    [StructLayout(LayoutKind.Sequential)] unsafe struct MySharedSettings { public int setting1; public int setting2; public string setting3; // add more fields here. }

  2. Use named shared memory (aka: memory-mapped files). This allows multiple processes on the same computer to share data, without the overhead of Remoting or WCF. Shared memory is extremely fast and, unlike pipes, offers random access to the shared memory data. The service would create the named shared memory, and the UI applications would open the shared memory. You would have to use pinvoke to use the underlying Windows APIs, but this is not a big deal.

  3. The UI applications write the MySharedSettings to the shared memory, while the service reads the shared memory.

  4. Use a named Semaphore and/or named Mutex to protect access to the shared memory and to signal the availability of new settings. The service has a dedicated background thread that simply perform a WaitOne() on the semaphore, and the UI thread will signal when new data is written.

Usually services that perform a 'polling operation' (i.e. sync files) have enough lag time in their poll interval that you can just as easily re-read all settings each loop, or even as needed.

If your service is more along the lines of a SOA back-end, then the changes may affect settings that are generally only used once during a service's lifetime. If this is your type of application, then the option #2 you describe above is the most reliable. I can't say I care much for Paul's implementation as polling a file like that will yield unreliable results. I would recommend using a globally named wait handle to signal your process for changes. I'm sure you can find an example here on SO. If you don't want to do that then you could poll for the configuration file's last-modified time changing.

Overall my preference is for the first approach using the registry for storage. Write all your settings in discrete values in a registry hive and read them on-demand in your service. It's faster than you might think and easy to implement on both front and back-end.

I have to agree with your initial lean toward #2 and #3. I particularly like the #3 since I'm not a fan of polling but ultimately I think the decision between #2 or #3 will be driven by the requirements of your service.

As for storage of user settings I would recommend exploring Isolated Storage ( http://msdn.microsoft.com/en-us/library/3ak841sy.aspx ). It provides an excellent mechanism for secure, consistent, and reliable access to user files. You won't have to worry about user's having permission unless the Administrator has completely turned off Isolated Storage. Plus if you enable roaming, users can even take their settings with them if they utilize different systems on the same domain, pretty slick eh?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!