If you develop an application it often comes up "how to deliver patches?". In case of a client application it might be an easy way to check updates at start up like ClickOnce does or simply ask user to update and restart the client. But when it comes to a service it is a more difficult question. When can you stop the service? Can you stop it at all? When you stop it what is going to happen with the clients or with the arriving data? Of course there are some solutions like load balancing, hearth-beat systems, MAF, etc. The best would to replace the DLL, of course it is not possible, at least overwrite isn't. What you can do, put an other version of the file into the folder and let the application to discover it.
My tiny demo application writes "hello" on the console in every second. To make it modular and "replaceable" it is implemented in an other library, client accesses it's interface only. I use MEF to discover the actual implementations.
It does (at least now) nothing, but creates a MEF container from assemblies in the ./debugger folder and calls the Log method in every second.
We need a logger interface, which has to be an other assembly, lets name it "Debugger.SDK". It only contains an interface (what a waste).
Don't forget to decorate with InheritedExport attribute, to allow MEF to find it. IDebugger interface exposes one method and one property. Log logs, but what is Version? We need to distinguish the implementations to know a newer version is available. Reference this DLL into the main app.
Now, we got an IDebugger, let's import it into our demo client.
Like I said, we cannot overwrite or unload loaded DLL-s (unless they are loaded into a different app domain), so we need to prepare to might have more then one implementation of IDebugger. However we only need one at the time. Now we can implement the Log method. It checks whether any implementation of IDebugger is available and use one with the highest version number, otherwise use Console.WriteLine to show the message.
We are almost done. Somehow we need to detect new files into the debugger folder and recompose IDebugger import. To achieve, use FileSystemWatcher.
PluginCatalog was created once we composed the MEF container, all we have to do is refresh.
If you start the app, it's going to write "HeLLo" on the console until you press any key. To replace this logger style create a new project, include DebuggerSDK, implement IDebugger interface and copy the compiled file into the ./debugger folder. E.g:
You don't need to export it extra, since interface has an InheritedExport attribute, which exports every inherited class under the type of the interface.
Please keep it mind, that this kind of implementation cannot be an official patch deliver method, since it has to load new DLL(s) which consume(s) more and more memory. It can be also security critical, due an inner functionality is simple replaceable.
Source code can be downloaded from here.
My tiny demo application writes "hello" on the console in every second. To make it modular and "replaceable" it is implemented in an other library, client accesses it's interface only. I use MEF to discover the actual implementations.
private
DirectoryCatalog
PluginCatalog { get; set; } public Program() { this.Compose(@".\debugger"); } private void Compose(string path) { this.PluginCatalog = new
DirectoryCatalog
(path); var catalog = new
AggregateCatalog
( new AssemblyCatalog(System.Reflection.
Assembly
.GetEntryAssembly()), new AssemblyCatalog(System.Reflection.
Assembly
.GetExecutingAssembly()), this.PluginCatalog); var container = new CompositionContainer(catalog); container.ComposeParts(this); } public void Log(string msg) { } static void Main(string[] args) { var p = new Program(); while (
Console
.KeyAvailable == false) { p.Log("HeLLo");
Thread
.Sleep(1000); } }
It does (at least now) nothing, but creates a MEF container from assemblies in the ./debugger folder and calls the Log method in every second.
We need a logger interface, which has to be an other assembly, lets name it "Debugger.SDK". It only contains an interface (what a waste).
namespace MEF.DebuggerSDK { using System.ComponentModel.Composition; [
InheritedExport
(typeof(
IDebugger
))] public interface
IDebugger
{ void Log(string msg); int Version { get; } } }
Don't forget to decorate with InheritedExport attribute, to allow MEF to find it. IDebugger interface exposes one method and one property. Log logs, but what is Version? We need to distinguish the implementations to know a newer version is available. Reference this DLL into the main app.
Now, we got an IDebugger, let's import it into our demo client.
[ImportMany(typeof(IDebugger), AllowRecomposition=true)] private
IEnumerable
<IDebugger> Debugger { get; set; }
Like I said, we cannot overwrite or unload loaded DLL-s (unless they are loaded into a different app domain), so we need to prepare to might have more then one implementation of IDebugger. However we only need one at the time. Now we can implement the Log method. It checks whether any implementation of IDebugger is available and use one with the highest version number, otherwise use Console.WriteLine to show the message.
public void Log(string msg) { if (this.Debugger.Any()) { var debugger = this.Debugger.OrderByDescending(d => d.Version).First(); debugger.Log(msg); } else {
Console
.WriteLine(msg); } }
We are almost done. Somehow we need to detect new files into the debugger folder and recompose IDebugger import. To achieve, use FileSystemWatcher.
private void Watch() {
FileSystemWatcher
watcher = new FileSystemWatcher(@".\debugger", "*.dll"); watcher.Created += this.OnPluginFolderChanged; watcher.EnableRaisingEvents = true; } private void OnPluginFolderChanged(object sender, FileSystemEventArgs e) { this.PluginCatalog.Refresh();
Console
.WriteLine("Plugin folder changed, recompose..."); }
PluginCatalog was created once we composed the MEF container, all we have to do is refresh.
If you start the app, it's going to write "HeLLo" on the console until you press any key. To replace this logger style create a new project, include DebuggerSDK, implement IDebugger interface and copy the compiled file into the ./debugger folder. E.g:
namespace MEF.LowerDebugger { using MEF.DebuggerSDK; public class
LowerDebugger
:
IDebugger
{ public void Log(string msg) {
Console
.WriteLine("[lower] " + msg.ToLower()); } public int Version { get { return 1; } } } }
You don't need to export it extra, since interface has an InheritedExport attribute, which exports every inherited class under the type of the interface.
Replace implementation in runtime |
Please keep it mind, that this kind of implementation cannot be an official patch deliver method, since it has to load new DLL(s) which consume(s) more and more memory. It can be also security critical, due an inner functionality is simple replaceable.
Source code can be downloaded from here.
No comments:
Post a Comment