My team is developing a Plugin project, in which Host application coordinates Plugins work (each Plugin has a specific function and will be executed on a seperate Thread). I'm writing Host application and define IPlugin interface; other people will develop Plugins.
The problem is: how to know when plugin completed or thrown exception. I have a solution is using event and delegate to let Plugins callback Host application, but I think this approach is not really good. Because if develop Plugin person implemented my IPlugin interface but forget writing raising event code.
In this case, the Plugin can be plug in my host application but my host application can not know when this Plugin completed or thrown exception or other communication, that's very bad.
My code likes this:
IPlugin.cs:
public delegate void OnFinishDelegate(IPlugin p, AMessageEventArgs e);
public delegate void OnExceptionDelegate(IPlugin p, AMessageEventArgs e);
public interface IPlugin
{
event OnFinishDelegate OnFinish;
event OnExceptionDelegate OnException;
void DoWork();
}
EventArgs:
public class AMessageEventArgs:EventArgs
{
private string message;
public string Message
{
get { return message; }
set { message = value; }
}
}
Host Application:
static void Main(string[] args)
{
// ...ignore load plugin code
//
IPlugin p = new Plugin();
// register event with IPlugin
p.OnFinish += new OnFinishDelegate(OnFinishHandler);
p.OnException += new OnExceptionDelegate(OnExceptionHandler);
// let plugin do its work on a subthread
Thread t = new Thread(p.DoWork);
t.Start();
// and then main continue do other work...
}
// if plugin throw exception, in the host application will
// handle this exception...
private static void OnExceptionHandler(IPlugin p, AMessageEventArgs e)
{
// in here will process exception of plugin...
}
// if plugin completed its work, it will use OnFinish event to
// notify to host application
private static void OnFinishHandler(IPlugin p,AMessageEventArgs e)
{
// in here will process completed work event
}
And I expect Plugin code will like below:
public class Plugin:IPlugin
{
// use event to callback host application
public event OnFinishDelegate OnFinish;
public event OnExceptionDelegate OnException;
// Create an EventArgs
AMessageEventArgs e = new AMessageEventArgs();
public void DoWork()
{
try
{
// execute update data
UpdateData();
}
catch (Exception ex)
{
e.Message = ex.Message;
// if have any exception, Plugin will raise an event in host application
// but if developer forget write below three lines of code, my host application will
// out of control.
if (OnException!=null)
{
OnException(this,e);
}
}
// if have no exception, Plugin will use OnFinish event to talk to host application
// to know that this plugin completed its work
// but if developer forget write below three lines of code, my host application will
// out of control.
if (OnFinish!=null)
{
OnFinish(this,e);
}
}
How to resolve this problem?
Additional problem: Did my IPlugin interface define well? if not well, can you advise me to improve this interface.
Thanks you!
I don't think the plugins need to know they're running in separate threads. I'd just have:
interface IPlugin {
void DoWork();
}
And then the host program would deal with catching exceptions and all the threading, something like:
Thread t = new Thread(() => {
try {
plugin.DoWork();
}
catch (Exception ex) {
// handle exception or save it to handle in the main thread
}
});
t.Start();
You've correctly identified the problem with the event model in the interface - when a plugin implements the IPlugin interface, they have to define the OnException event, but there is nothing requiring them to use it.
Define your IPlugin interface with a single DoWork method, and allow the standard exception pattern to report successful completion or an error status. If the DoWork method returns normally, the plugin is finished (no need for an OnFinish event). If DoWork throws an exception, your main application can catch the exception and deal with it (no need for an OnException event).
I would recommend looking into the Task Parallel Library for executing the plugins. It provides more control than using threads directly, including exception handling.
I've just read HeadFist Design pattern and discovered a solution: Factory Method.
In my case, I should use abstract class instead interface (IPlugin). In this abstract class, I define a Operate() method that will report result(completed result or exception result) to my host application. The second method is DoWork() method that the third party programmer can writing his function and don't care about OnComplete or OnException event.
My code like below:
Abstract class:
public delegate void OnFinishDelegate(APlugin p, AMessageEventArgs e);
public delegate void OnExceptionDelegate(APlugin p, AMessageEventArgs e);
public abstract class APlugin
{
public event OnFinishDelegate OnFinish;
public event OnExceptionDelegate OnException;
AMessageEventArgs e = new AMessageEventArgs();
public void Operate()
{
try
{
// implement plugin work
// we don't care how does the third party programmer write his Plugin program
DoWork();
// if DoWork() completed , it will raise an OnFinish event
// in my host application
e.Message = "Completed";
if (OnFinish != null)
{
OnFinish(this, e);
}
}
catch(Exception ex)
{
// if DoWork() throw exception, it will raise an OnException event
// in my host application
e.Message = ex.Message;
if (OnException!=null)
{
OnException(this,e);
}
}
}
// In here, the third party programmer will override this DoWork() method
private abstract void DoWork();
}
Host applicaton code:
static void Main(string[] args)
{
// ...ignore load plugin code
//
APlugin p = new Plugin();
// register event with IPlugin
p.OnFinish += new OnFinishDelegate(OnFinishHandler);
p.OnException += new OnExceptionDelegate(OnExceptionHandler);
// let plugin do its work on a subthread
Thread t = new Thread(p.Operate);
t.Start();
// and then main continue do other work...
}
// if plugin throw exception, in the host application will
// handle this exception...
private static void OnExceptionHandler(APlugin p, AMessageEventArgs e)
{
// in here will process exception of plugin...
}
// if plugin completed its work, it will use OnFinish event to
// notify to host application
private static void OnFinishHandler(APlugin p, AMessageEventArgs e)
{
// in here will process completed work event
}
>
And Plugin code (must like below):
class Plugin:APlugin
{
public override void DoWork()
{
// in here, the third party programmer can write anything
// for example: update data in database
UpdateData();
}
private void UpdateData()
{
// do update data
}
}
Related
I've done some searching here and haven't been able to get a clear answer to my problem.
I have a several child classes all with 1 interface. I have a parent class that contains a variable and this variable is created as a new instances of one of those child classes depending on external params. Here's some code:
public interface I
{
public delegate void ExecutionCompletedHandler(bool status);
public event ExecutionCompletedHandler executionCompleted;
public void Execute();
}
public class C1 : I
{
public void Execute()
{
// Create background worker and execute DoStuff
}
public void BackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool status = (bool)e.Result;
this.executionCompleted(true);
}
}
public class C2 : I
{
// Same setup as C1
}
public class C3 : I
{
// Same setup as C1
}
public class MyManager
{
public void doStuff(int val)
{
var compObj = null;
// compObj is now instantiated as new instance of C1, C2 or C3 depending on val
// ex: compObj = new C1();
compObj.executionCompleted += new I.ExecutionCompletedHandler(executionCompletedHandler);
compObj.Execute();
}
private void executionCompletedHandler(bool status)
{
// Do stuff with status and exit gracefully
}
}
This is what I'd like to do but I know it's not right. I feel as if I'm 90% of the way there. It's saying that the executionCompleted variable in the C1 class is hiding the interface's variable. I've tried to follow various guides and examples but haven't been able to figure this out. Thanks!
Edit: I'm using .NET 4.0 in Visual Studio 2010.
EDIT 2:
I was able to figure it out with help from #NikProtsman...I converted the interface to an abstract class, and in that abstract class, implemented a CompleteExecution function. In this function, I would call the event handler. In the C1/C2/C3 classes, when background worker is finished executing, I would call this method. Works perfectly. We're in the process of upgrading to VS 2019 and after this, I'm going to push to make that happen quicker! Thanks!
Try this:
In your interface, change Execute to:
public Task Execute();
In your Class C1:
//Add this line to conform to Interface
public event I.ExecutionCompleteHandler executionCompleted;
public async Task Execute()
{
// Create background worker and execute DoStuff
await DoStuff();
// You'll need to supply appropriate args here
BackgroundWorkerCompleted(this, args);
}
public void BackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool status = (bool)e.Result;
//Changed this line, assumed you needed the status from the line above
executionCompleted?.invoke(status);
}
Next your MyManager should look like this:
public class MyManager
{
public async Task doStuff(int val)
{
var compObj = null
// compObj is now instantiated as new instance of C1, C2 or C3 depending on val
compObj = new C1();
// Subscribe to the 'executioncompleted' event in your new instance
compObj.executionCompleted += HandleExecutionComplete;
// Execute your code
await compObj.Execute();
// Unsubscribe from the event (cleaning up after yourself)
compObj.executionCompleted -= HandleExecutionComplete;
}
private void HandleExecutionComplete(bool status)
{
// Do stuff with status and exit gracefully
}
}
The key point here is assigning the Execution Handler properly in your Manager, and then using it to subscribe to the C1 class event. Inside the C1 class, use a Task for DoStuff, and await it in Execute which becomes an async Task. Once DoStuff is done, the WorkerCompleted task runs, executes your handler, and off you go.
This can all be simplified somewhat but that is outside the scope of this question. The idea is how the control flow will work and using async calls with await to make sure your program waits for what it needs, and then continues, and how you subscribe to that event externally.
Just be sure to await your MyManager.doStuff call on the outside as well, otherwise any results you are waiting for will not get picked up in time.
I have a WPF application and I need to listen to, and handle events for the lifetime of the application for a certain class.
Is it bad practice to create a wrapper class, create a static instance of it and call "StartListening()"? What if an unhanded exception happens on this static instance? Will it tear down the entire application as it would in an ASP.NET application?
Should I QueueUserWorkItem, create the class, attach events, and then put some kind of while(true){} statement to keep the thread alive?
What is the best practice?
To me this seems like a classic publisher/listener problem.
I would create an interface: IMyClassNameEventListener and make MyClass take an instance of it as a constructor parameter. Then in the constructor I would call the Attach(MyClass obj) method on the interface instance. Of course, the listener would have a singleton lifecycle, it doesn't need to be static.
A slightly better approach would be to use a factory to create instances of MyClass which would then do the attaching, so the Attach call and the dependency are out of the constructor.
Wether the app would fail would be dependent on how you start the listener. You can look into the TaskFactory class, it provides options to handle exception propagation. How would you want the app to behave if the listener fails?
Of course in the listener object itself, you only need to have code run when there is something to handle. So, when you receive an event, you startup a thread. You can use a queue of actions if you'd want to have only one thread running.
Inside the listener class, you might want to have something like the following:
private Queue<Action> ActionQueue = new Queue<Action>();
private object LockObj = new Object();
private volatile bool IsRunning;
public void Attach(Class1 obj)
{
obj.SomeEvent += this.HandleEvent;
}
private void HandleEvent(object sender, EventArgs e)
{
lock(this.LockObj)
{
this.ActionQueue.Enque(() => this.Handle(sender, e));
if (!this.IsRunning)
{
Task.Factory.StartNew(() => this.Loop() );
}
}
}
private void Loop()
{
this.IsRunning = true;
while ((Action action = this.DequeueAction()) != null)
action();
this.IsRunning = false;
}
private Action DequeueAction()
{
lock (this.LockObj)
{
return this.ActionQueue.Count > 0 ? this.ActionQueue.Dequeue() : null;
}
}
private void Handle(object sender, EventArgs e)
{
//handling code
}
I have a UserControl on a Form,
when I MouseMove on that UserControl I want to do something in the Form.
How can I make the Form 'listen' for this event?
I am using Visual C#, .Net framework 3.5, winforms
I suppose you're referring to a use control or something like that.
You can add a public event, and trigger it inside your class when detecting the inner class event.
Then you have to subscribe to the published event in the second class.
This is a sample so that you see the sintax:
public class WithEvent
{
// this is the new published event
public EventHandler<EventArgs> NewMouseEvent;
// This handles the original mouse event of the inner class
public void OriginalEventhandler(object sender, EventArgs e)
{
// this raises the published event (if susbcribedby any handler)
if (NewMouseEvent != null)
{
NewMouseEvent(this, e);
}
}
}
public class Subscriber
{
public void Handler(object sender, EventArgs e)
{
// this is the second class handler
}
public void Subscribe()
{
WithEvent we = new WithEvent();
// This is how you subscribe the handler of the second class
we.NewMouseEvent += Handler;
}
}
If you are talking about Windows Forms (it's not clear from the question) you need to define
a new event in the class who recieves the mouse-event. After reciving it raises a new custom-event. Another class is subcribed to that (custom-event) a recieves notification.
For moe information (it's not something that can be presenteed in a couple of lines)
can have alook here:
How to propagate an Event up to the MainForm?
If you are talking about WPF, there are different concept of events: event routing. If your class is UI element present in UI tree of the component that recieves actually mouse-event, it will be propagated to your class too. So no need of more coding.
To expand a little on the answer from JotaBe, there are two scenarios that I could see you having:
a) class A calls a method in class B, and an exception happens. In this case, you don't need to do anything: exception will walk the stack, until it finds a catch statement. So, really, all you need to do is NOT catch an exception, or if you do need to catch it (for logging purposes and such), then rethrow it.
b) if you need to have a code triggered in some unrelated class, as a result of exception, then the best way is to use events. In your class declare:
public class ClassA
{
public static event EventHandler<Exception> OnException;
public void Notify(Exception ex)
{
if (OnException != null)
{
OnException(this, ex);
}
}
}
and then, in order to be notified, all you need is to
ClassA.OnException += (sender, exeption) =>
{
... some GetHashCode ..
};
... I guess JotaBe already added all necessary example code as I was typing
I have written a plugin system that uses an interface and for any plugins that meet this contract are loaded at runtime into the main system.
The plugin effectively returns a TabPage that is slotted into the main app, and is controlled fromwithin the plugin dll.
If an error occurs within the plugin, the standard Windows error message shows. What I want to do it create an event that returns the error message so I can display it in the area I have reserved for text.
Do I need to keep a track of all attached plugin/interface instances to be able to set up an event to monitor each one?
At present, my system loops through the dll's within the app folder and those that meet the interface contract are loaded up, the actual instance of the interface is discarded each time as control is then handed over to the dll via button events that are loaded with the TabPage and handled within the plugin.
I hope this all makes sense.
You don't need to keep a reference to the plugin class, just add a delegate to the event when you start it up, after that you don't need the reference anymore.
You could add an event to your plugin contract:
public interface IPlugin
{
event EventHandler<ErrorEventArgs> Error;
void Initialise();
}
That way, any host can subscribe to that event when errors occur within the plugin:
public class MyPlugin : IPlugin
{
public event EventHandler<ErrorEventArgs> Error;
public void Initialise()
{
try
{
}
catch (Exception e)
{
OnError(new ErrorEventArgs(e));
}
}
protected void OnError(ErrorEventArgs e)
{
var ev = Error;
if (ev != null)
ev(this, e);
}
}
If I have followed you post correctly, this is how I would go about doing it.
In the plugin interface (Lets say IPlugin) you will need to declare an event.
public delegate void ShowErrorEventHandler(string errorMessage);
public interface IPlugin
{
event ShowErrorEventHandler ShowError;
}
Then when you load your plugins, for each one just subscribe to it's ShowError event, for example:
...
foreach(var plugin in plugins)
{
plugin.ShowError += MainForm_ShowError;
}
...
private void MainForm_ShowError(string errorMessage)
{
// Do something with the error... stick it in your reserved area
txtReservedArea.Text = errorMessage;
}
Hope this helps
I have a class that basically stores files in amazon s3.
Here is what it looks like (simplified)
public class S3FileStore
{
public void PutFile(string ID, Stream content)
{
//do stuff
}
}
In my client app, I want to be able to call:
var s3 = new() S3FileStore();
s3.PutFile ("myId", File.OpenRead(#"C:\myFile1"));
s3.PutFile ("myId", File.OpenRead(#"C:\myFile2"));
s3.PutFile ("myId", File.OpenRead(#"C:\myFile3"));
I want this to be an asynchronous operation - I want the S3FileStore to handle this (i don't want my caller to have to execute PutFile asynchronously so to speak) but, i want to be able to trap exceptions / tell if the operation completed for each file.
I've looked at event based async calls, especially this:
http://blogs.windowsclient.net/rendle/archive/2008/11/04/functional-shortcuts-2-event-based-asynchronous-pattern.aspx
However, I can't see how to call my PutFile (void) method?
Are there any better examples?
Look at the solution for this question: Adding cancel ability and exception handling to async code . Hope it helps.
The BackgroundWorker base class might be worth a look, and also the Thread Pool:
ThreadPool.QueueUserWorkItem(delegate
{
s3.PutFile ("myId", File.OpenRead(#"C:\myFile1"));
});
This is basically what you would do with the Action/BeginInvoke pattern. With BeginInvoke, you additionally receive an IAsyncResult on which you can call .WaitOne() to block the current thread until the operation finished, in case you need that. You would trigger a new BeginInvoke for every file you'd like to save.
If you need to do this frequently, a more sophisticated version could be to use a Queue in combination with the BackgroundWorker, e.g.:
public sealed class S3StoreLikePutFileWorker<TYourData> : BackgroundWorker
{
private AutoResetEvent WakeUpEvent = new AutoResetEvent(false);
private Queue<TYourData> DataQueue = new Queue<TYourData>();
private volatile bool StopWork = false;
public void PutFile(TYourData dataToWrite)
{
DataQueue.Enqueue(dataToWrite);
WakeUpEvent.Set();
}
public void Close()
{
StopWork = true;
WakeUpEvent.Set();
}
private override void OnDoWork(DoWorkEventArgs e)
{
do
{
// sleep until there is something to do
WakeUpEvent.WaitOne();
if(StopWork) break;
// Write data, if available
while(DataQueue.Count > 0)
{
TYourData yourDataToWrite = DataQueue.Dequeue();
// write data to file
}
}
while(!StopWork);
}
}
Depending on how much complexity you need.
The BackgroundWorker supports progress feedback (set WorkerReportsProgress = true; in the constructor), and you can also add a custom event to report errors, if that is necessary:
// create a custom EventArgs class that provides the information you need
public sealed class MyEventArgs : EventArgs {
// Add information about the file
}
// ... define the event in the worker class ...
public event EventHandler<MyEventArgs> ErrorOccured;
// ... call it in the worker class (if needed) ...
if(ErrorOccured != null) ErrorOccured(this, new MyEventArgs(/*...*/));