I have "Handlers" able to trigger "Brokers" (an object that does something - not important here).
Handlers are listening to different kind of events:
TimeEvent: Every 10 seconds, 10 minutes (...)
FileSystemEvent: Once a file copied/moved/deleted
DbEvent: When a record is added to a DB table
MailEvent: When I received an email in my Office 365 mailbox
Each handler must have:
Start and stop methods (start/stop catching events)
An instance of the associated broker
A way to "trigger" the broker (Process method + specific set of arguments).
Each handler should
Trigger the associated broker when a specific event is raised
I want to trigger brokers from the base Handler class so I centralize my logic (fire events, catch exception, manage threads etc.). However, the base handler doesn't know how to call the broker (when to call this function, what parameters to send to the Process method) >> Only specialized children handlers know how to do that.
The only way I found is to implement in the base handler an Execute method accepting an Action parameter... I don't like this approach as it's not really straight forward (child needs to call base class otherwise nothing happens). I was hoping to find a better design to handle this. In addition I can tell you my developers will tell me they don't understand how to use the system.
abstract class Handler : IHandler
{
public IBroker Broker { get; protected set; }
public event ProcessedEventHandler Processed;
protected void OnProcessed(ProcessExecutionResult result) => Processed?.Invoke(this, result);
public static IHandler Create(IBroker broker)
{
if (broker is ITimerTriggeredBroker)
return new TimeHandler((ITimerTriggeredBroker)broker);
return null;
}
protected Handler(IBroker broker)
{
if (broker == null) throw new ArgumentNullException(nameof(broker));
Broker = broker;
}
public abstract void Start();
public abstract void Stop();
protected void Execute(Action action)
{
var res = new ProcessExecutionResult();
try
{
action?.Invoke();
res.IsSuccess = true;
}
catch (Exception ex)
{
res.Exception = ex;
res.IsSuccess = false;
}
finally
{
OnProcessed(res);
}
}
}
TimeHandler (handling Time related events)
class TimeHandler : Handler
{
private readonly Timer _timer;
private readonly DateTime _start;
private readonly TimeSpan _frequency;
public TimeHandler(ITimerTriggeredBroker broker)
: base(broker)
{
_start = broker.Trigger.StartTime;
_frequency = broker.Trigger.Frequency;
_timer = new Timer(_ => Execute(broker.Process));
}
(...)
}
FileHandler (handling FileSystem related events)
class FileHandler : Handler
{
private readonly FileSystemWatcher _watcher = new FileSystemWatcher();
public FileHandler(IFileTriggeredBroker broker)
: base(broker)
{
if (!Directory.Exists(broker.Trigger.DirectoryPath))
throw new DirectoryNotFoundException("Unable to locate the supplied directory");
_watcher.Filter = broker.Trigger.Filter;
_watcher.Path = broker.Trigger.DirectoryPath;
_watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName |
NotifyFilters.FileName;
_watcher.Created += (s, a) =>
{
if (IsCopied(a.FullPath)) Execute(() => broker.Process(a.FullPath));
};
}
There are several aspects to what you are trying to achieve:
The architecture should be easy to understand and follow by your programmers. It should guide them while they program and protect them from making errors.
It should be robust. For example, you should guarantee that every file created in the watched folder is processed.
In my answer I will ignore the robustness aspect. Please look at this seriously. FileSystemWatcher is not guaranteed to deliver all files created. Also, it is advisable to you separate the processing of FileSystemWatcher events in a separate thread or use .NET tasks for this.
Furthermore, I think you should consider using a queue, like Microsoft MQ, Azure Queue, RabbitMQ. You can do this directly or use a system like MassTransit.
Below I propose an architecture that will make it easier for your programmers to build upon.
General description
Divide the application in folders or different assemblies to make a clear separation between framework and specific handlers/brokers.
For each type of processing we create a specific message class and let the handler and broker implement a generic interface specific for the message type.
We will make use of the C# advanced type system to make sure that it is difficult to make mistakes and that the compiler will help the programmers to use the right things. For this we use generic interfaces and classes based on the message type class.
Main program
Here we will setup a manager that will register all handlers and brokers with their respective messages. This is a stand alone example, I suggest you use a system for dependency injection like for example AutoFac to further optimize this.
static void Main(string[] args)
{
var manager = new Manager();
manager.Register<FileHandlerMessage>(new FileHandler(), new FileBroker());
manager.Register<TimeHandlerMessage>(new TimeHandler(), new TimeBroker());
manager.Start();
Console.ReadLine();
manager.Stop();
}
Manager
The role of the Manager class is to organize for proper usage of handlers and brokers.
class Manager
{
private List<IGenericHandler> handlers = new List<IGenericHandler>();
public void Register<M>(IHandler<M> handler, IBroker<M> broker) where M : Message
{
handlers.Add(handler);
}
public void Start()
{
foreach ( var handler in handlers )
{
handler.Start();
}
}
public void Stop()
{
foreach (var handler in handlers)
{
handler.Stop();
}
}
}
Messages
For each type of broker, we will define a specific message class, derived from a common base class:
abstract class Message
{
}
class FileHandlerMessage : Message
{
public string FileName { get; set; }
}
Handlers
interface IGenericHandler
{
void Start();
void Stop();
}
interface IHandler<M> : IGenericHandler where M : Message
{
void SetBroker(IBroker<M> broker);
}
class FileHandler : IHandler<FileHandlerMessage>
{
private IBroker<FileHandlerMessage> broker;
public FileHandler()
{
}
public void SetBroker(IBroker<FileHandlerMessage> fileBroker)
{
this.broker = fileBroker;
}
public void Start()
{
// do something
var message = new FileHandlerMessage();
broker.Process(message);
}
public void Stop()
{
// do something
}
}
class TimeHandler : IHandler<TimeHandlerMessage>
{
private IBroker<TimeHandlerMessage> broker;
public void SetBroker(IBroker<TimeHandlerMessage> broker)
{
this.broker = broker;
}
public void Start()
{
// do something
var message = new TimeHandlerMessage();
broker.Process(message);
}
public void Stop()
{
// do something
throw new NotImplementedException();
}
}
Brokers
class FileBroker : IBroker<FileHandlerMessage>
{
public void Process(FileHandlerMessage message)
{
throw new NotImplementedException();
}
}
class TimeBroker : IBroker<TimeHandlerMessage>
{
public void Process(TimeHandlerMessage message)
{
throw new NotImplementedException();
}
}
Related
I am using the observer pattern to add and remove subscribers from a newsletter using events.
I'm not sure how to approach what I'm trying to do. Is it possible to add a subscriber using an event and how?
The console is operated by a form with buttons so when the button is clicked it will add subscribers and when the remove button is clicked it will remove a subscriber, on the form, there is also a button to post a newsletter which will post a newsletter for each subscriber.
This is what I have managed to get so far:
class Program
{
//[STAThread]
static void Main(string[] args)
{
SoftwareRevolution softrev = new Ass_3._1.SoftwareRevolution();
ConsoleOutput First = new ConsoleOutput(softrev);
softrev.Datetime = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
Application.Run(new InputForm());
}
}
public delegate void NotifyMe(string Value);
class ConsoleOutput : IEventsObserver
{
IEventsSubject IEventsSubject;
public ConsoleOutput(IEventsSubject IEventsSubject)
{
this.IEventsSubject = IEventsSubject;
this.IEventsSubject.Notifier += new Ass_3._1.NotifyMe(UpdateNews);
}
public void UpdateNews(string info)
{
Console.WriteLine(info);
}
}
interface IEventsObserver
{
void UpdateNews(string info);
}
interface IEventsSubject
{
event NotifyMe Notifier;
}
class SoftwareRevolution : IEventsSubject
{
List<IEventsObserver> Readers = new List<IEventsObserver>();
private string datetime;
public string Datetime
{
get
{
return datetime;
}
set
{
datetime = value;
Notifier(datetime);
}
}
public event NotifyMe Notifier;
}
The object which issues a newsletter, should have an event which fires when a newsletter issues. Then all subscribes which has subscribed for that event will receive it.
Distribution of event will be done without having such list in publisher class. In fact the delegate implementation in C# takes care of that and there is an invokation list behind the event delegate.
Usually the Publisher doesn't need to keep list of Subscriber object itself and if you need such list, it's better to keep it in a SubscriptionManager.
The subscription manager, subscribes and unsubscribes the subscribers to the event of publisher. It also can keep a list of subscribers.
Example
The Newsletter class contains some fields which represents a newsletter:
public class Newsletter
{
public string Content;
}
The NewsLetterPublishedEventArgs contains information about published newsletter:
public class NewsletterPublishedEventArgs : EventArgs
{
public Newsletter Newsletter { get; set; }
}
The Publisher class published the newsletter and raises an event after publishing:
public class Publisher
{
public event EventHandler<NewsletterPublishedEventArgs> NewsLetterPublished;
public void PublishNewsLetter()
{
var newsLetter = new Newsletter() { Content = $"New publish at {DateTime.Now}" };
NewsLetterPublished?.Invoke(this,
new NewsletterPublishedEventArgs() { Newsletter = newsLetter });
}
}
The Subscriber class has a method which allows it to receive NewsLetterPublished notification:
public class Subscriber
{
public string Name { get; set; }
public void ReceiveNewsLetter(object sender, NewsletterPublishedEventArgs e)
{
MessageBox.Show(e.Newsletter.Content);
}
}
At the end, SubscriptionManager class subscribes and unsubscribes subscribers to the publisher:
public class SubscriptionManager
{
public Publisher Publisher => new Publisher();
private List<Subscriber> subscribers;
public void Subscribe(Subscriber s)
{
if (!subscribers.Contains(s))
{
subscribers.Add(s);
Publisher.NewsLetterPublished += s.ReceiveNewsLetter;
}
}
public void Unsubscribe(Subscriber s)
{
if (subscribers.Contains(s))
{
subscribers.Remove(s);
Publisher.NewsLetterPublished -= s.ReceiveNewsLetter;
}
}
}
Note
To keep thing simple I created a simple example to just demonstrate the logic for learning purpose. In a real world example, you need to use interfaces, generic classes and dependency injection.
How can ConnectionManager get expiredRegistrationId that was captured in AndroidPush class?
Am I doing this the wrong way?
Any suggestions on how I may improve my solution?
Is there any pattern I could follow?
Solution : Managers
public class ConnectionManger
{
private readonly IPushManager pushManager = new PushManager();
public void NotifyAppUser(List<PushNotificationSubscription> regIds, Alert alert)
{
pushManager.PushNotification(regIds, alert);
var expiredRegistrationId = ??
}
}
Solution : PushNotification
public class PushManager : IPushManager
{
public void PushNotification(List<PushNotificationSubscription> registeredPhone, Alert alert)
{
AndroidPush androidPush = new AndroidPush();
androidPush.Push(alert, registeredPhone);
}
}
public class AndroidPush : IPushNotificationStrategy
{
public void Push(Alert alert, List<string> registrationIds)
{
// Wait for GCM server
#region GCM Events
gcmBroker.OnNotificationFailed += (notification, aggregateEx) =>
{
var expiredRegistrationId = aggregateEx.OldId;
Q: How do i pass expiredRegistrationId to ConnectionManager class?
};
}
}
You have .NET as common playground so there are several options
Assuming:
you by solutions mean separate dll´s
you need to return a list of strings ( from the original question )
your code is keeping gcmBroker alive so the OnNotificationFailed event can fire
then this should work:
Change your signature in the IPushNotificationStrategy interface to
List<string> Push(Alert alert, List<string> registrationIds)
Add this event to your IPushManager interface:
event Action<List<string>> ExpiredRegs;
Implement and invoke this event from PushManager:
public event Action<List<string>> ExpiredRegs;
// call this when Push returns some expiredRegs :
void OnExpiredRegs(List<string> expiredRegs) => ExpiredRegs?.Invoke(expiredRegs);
Subscribe to the event in ConnectionManger:
pushManager.ExpiredRegs += OnExpiredRegs;
void OnExpiredRegs(List<string> expiredRegs)
{
// whatever you need to do
}
public class RSensor : IRSensor
{
public RSensorType SensorType { get; protected set; }
public event EventHandler ConnectionLost;
public void Configure(Stream input, Stream output, RSensorType type, ISensorReciever reciever)
{
throw new NotImplementedException();
}
public RSensor()
{
}
}
I have a class RSensor that has an EventHandler ConnectionLost which will inform me when the connection goes down.
I have another class ConnectionManager that somehow needs to handle when the connection goes down. I have created a dummy function void HandleConnectionLost()
in this class. How do I trigger this function when the EventHandler ConnectionLost from RSensor class is triggered.
public class ConnectionManager
{
ActiveList m_ActiveList;
Dictionary<IRionSensor, VSManager> m_ConnectionManager;
class VehicleStreamManager
{
public IVehicle Vehicle { get; set; }
public NetworkStream AStream { get; set; }
public NetworkStream BStream { get; set; }
}
ConnectionManager(ActiveExercise exercise)
{
m_ActiveList = exercise;
m_ConnectionManager = new Dictionary<IRSensor, VehicleStreamManager>();
}
void Connect()
{
List<VehicleStreamManager> vsmList;
VehicleStreamManager vsm;
vsm = InitiateAVehicleConnections();
m_ConnectionManager.Add(null, vsm);
vsmList = InitiateBVehicleConnections();
foreach(var item in vsmList)
{
RSensor fsensor = new RSensor();
fsensor.Configure(item.AStream, item.BStream, RSensorType.RSensor_Both, null);
m_ConnectionManager.Add(fsensor, item);
}
}
void HandleConnectionLost()
{
//Do some stuff
}
}
}
You need to redefine your HandleConnectionLost handler slightly, so that it conforms to an EventHandler signature:
void HandleConnectionLost(object sender, EventArgs args)
{
// Do stuff here
}
Then, in your ConnectionManager loop that instantiates sensors, you need to wire up the event handlers:
foreach(var item in vsmList)
{
RSensor fsensor = new RSensor();
fsensor.Configure(item.AStream, item.BStream,
RSensorType.RSensor_Both, null);
fsensor.ConnectionLost += HandleConnectionLost;
m_ConnectionManager.Add(fsensor, item);
}
This way, you handler will be triggered whenever one of the sensors loses the connection.
You are not showing the code that does this, but you said that ConnectionLost event will inform me when the connection goes down. I take this to mean that there is code present that raises the event already. If not, let me know and I'll extend the example.
However, the sensor must rely on some kind of external notification of the connection being lost - it will likely rely on a lower-level infrastructure.
I want to send an alert message to any subscribers when a trap occurred.
The code I created works fine using a delegate method myDelegate del.
My questions are:
I want to know whether it's better to use EventHandler instead of a delegate?
I'm not sure what the differences are between a delegate and an EventHandler in my case.
notify(trapinfo t), that's what I've done here to get trap information. But it seems not to be a good idea. I read some online tutorial lesson introducing passing delegate object; I'm wondering if it's appropriate in my case? And how should I do it? Any suggestions?
Thanks a lot :)
My code:
public class trapinfo
{
public string info;
public string ip;
public string cause;
}
public class trap
{
public delegate void myDelegate(trapinfo t);
public myDelegate del;
trapinfo info = new trapinfo();
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
foreach (myDelegate d in del.GetInvocationList())
{
info.cause = "Shut Down";
info.ip = "192.168.0.1";
info.info = "Test";
d.Invoke(info);
}
}
}
}
public class machine
{
private int _occuredtime=0;
public trapinfo info = new trapinfo();
public void notify(trapinfo t)
{
++_occuredtime;
info.cause = t.cause;
info.info = t.info;
info.ip = t.ip;
getInfo();
}
public void subscribe(trap t)
{
t.del += new trap.myDelegate(notify);
}
public void getInfo()
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
info.cause, info.info, info.ip,_occuredtime);
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
machineA.subscribe(t);
t.run();
}
}
Update 2013-08-12
How about the observer/observable design pattern, that looks great in my case (EventHandler).
In my case, a machine subscribes to a trap messenger. (Add a machine to an invocation list)
Once a trap occurred, I send a message to all machines which are subscribed. (Call HandleEvent to handle it)
Advantages:
don't care about GetInvocationList() anymore, just use (+=) and (-=) to decide whom to send the trap.
It's easier to understand the logic of my program.
I know there are several ways to do it, but I wish I could analyze its pros and cons.
And thanks for your comments and suggestions, that would be very helpful!
I read the MSDN EventArgs article which Matthew Watson suggested.
Here's my Event Version:
public class TrapInfoEventArgs : EventArgs
{
public int info { get; set; }
public string ip { get; set; }
public string cause { get; set; }
}
public class trap
{
public event EventHandler<TrapInfoEventArgs> TrapOccurred;
protected virtual void OnTrapOccurred(TrapInfoEventArgs e)
{
EventHandler<TrapInfoEventArgs> handler = TrapOccurred;
if (handler != null)
{
handler(this, e);
}
}
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
TrapInfoEventArgs args = new TrapInfoEventArgs();
args.cause = "Shut Down";
OnTrapOccurred(args);
}
}
}
public class machine
{
public void c_TrapOccurred(object sender, TrapInfoEventArgs e)
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
e.cause, e.info, e.ip, DateTime.Now.ToString());
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
t.TrapOccurred += machineA.c_TrapOccurred; //notify machine A
t.run();
}
}
The difference between event and delegate is that:
event declaration adds a layer of protection on the delegate instance.
This protection prevents clients of the delegate from resetting the
delegate and its invocation list, and only allows adding or removing
targets from the invocation list
See What are the differences between delegates and events?
2) As I see it, your subscriber should not change delegates freely. One subscriber can assign = to it instead of adding +=. This will assign a new delegate, therefore, the previous delegate with its invocation list will be lost and previous subscribers will not be called anymore. So you should use Event for sure. Or you can change your code to make your delegate private and write additional functions for manipulating it to define your own event behavior.
//preventing direct assignment
private myDelegate del ;
public void AddCallback(myDelegate m){
del += m;
}
public void RemoveCallback(myDelegate m){
del -= m;
}
//or
public static trap operator +(trap x,myDelegate m){
x.AddCallback(m);
return x;
}
public static trap operator -(trap x, myDelegate m)
{
x.RemoveCallback(m);
return x;
}
//usage
//t.AddCallback(new trap.myDelegate(notify));
t+=new trap.myDelegate(notify);
It is much better to use an event for your example.
An event is understood by the Visual Studio Form and WPF designers, so you can use the IDE to subscribe to events.
When raising events, there is no need for you to write your own foreach handling to iterate through them.
events are the way that most programmers will expect this functionality to be accessed.
If you use a delegate, the consuming code can mess around with it in ways that you will want to prevent (such as resetting its invocation list). events do not allow that to happen.
As for your second question: Using an event you would create a class derived from EventArgs to hold the data, and pass that to the event when you raise it. The consumer will then have access to it.
See here for details: http://msdn.microsoft.com/en-us/library/system.eventargs.aspx
I am using c# and events alot lately but I'm just starting to create my own events and use them. I'm a little confused on why to use the event keyword, I got the same result by only using delegates.
public partial class Form1 : Form
{
ServerConnection connection = new ServerConnection();
public Form1()
{
InitializeComponent();
connection.ref = new del(MethOne);
connection.ref += new del(MethTwo);
}
public void MethOne(object message)
{
MessageBox.Show((string)message);
}
public void MethTwo(object message)
{
MessageBox.Show((string)message);
}
}
public delegate void del(string message);
public class ServerConnection
{
private TcpListener tcpListener;
public del ref;
private List<NetworkStream> clientList = new List<NetworkStream>();
public ServerConnection()
{
this.tcpListener = new TcpListener(IPAddress.Any, 3000);
ThreadStart startListening = new ThreadStart(ListenForClients);
Thread startThread = new Thread(startListening);
startThread.Start();
}
public void ListenForClients()
{
tcpListener.Start();
ParameterizedThreadStart handleClient = new ParameterizedThreadStart(HandleClient);
while (true)
{
TcpClient newClient = tcpListener.AcceptTcpClient();
Thread handleClientThread = new Thread(handleClient);
handleClientThread.Start(newClient);
}
}
public void HandleClient(object newClient)
{
NetworkStream clientStream = ((TcpClient)newClient).GetStream();
clientList.Add(clientStream);
BinaryFormatter formatter = new BinaryFormatter();
string message;
while (true)
{
message = (string)formatter.Deserialize(clientStream);
ref((string)message);
}
}
The event keyword lets you specify add and remove operations inline with the declaration.
private Action _myEvent;
public event Action MyEvent
{
add
{
Console.WriteLine("Listener added!");
_myEvent += value;
}
remove
{
Console.WriteLine("Listener removed!");
_myEvent -= value;
}
}
Have a look at
C# events vs. delegates
the event keyword is a modifier for a delegate declaration that allows
it to be included in an interface, constraints it invocation from
within the class that declares it, provides it with a pair of
customizable accessors (add and remove) and forces the signature of
the delegate (when used within the .NET framework).
The purpose is to identify what is an event, and what is just a callback.
Both seems to be the same thing, but the meaning is different.
Also Visual Studio places different icons to indicate events.
If I remember well, it the early days of C#, delegates didn't support this:
this.mydelegatefield += somethingHere;
Only events... but may be it is only my imagination.
EDIT
Just not to be missleading... there is the difference of add/remove methods. I place this after the other answers (since I forgot about this). So, credit is not mine.
Event is just a sugarcoat. 3 things happen when you define an event.
Simple EG:
public event EventHandler alarm;
Sample compiler output
private EventHandler alarm
public void add_alarm(EventHandler value)
{
}
public void remove_alarm(EventHandler value)
{
}
Notice private in contrast to your public del me;
Public accessors may cause problems. Also, using get and set is a better pattern
events can be compared to properties of your class.
Properties are interfaces to your MemberField/Object states.
Similarly event is an interface to the underlying delegate.
you can still achieve the endresult without event.But you lose encapsulation without events.
A non protected delegate can be prone to abuse.