I have a method that does some stuff that takes a while and posts progress/status messages back. The method used to be the Main() static method in my console Program class. Later, I decided to move the functionality into a shared library assembly, so that the same functionality could be accessed by a new web api project, so a different user interface with different mechanisms for stdio.
What is a good design pattern to replace all my Console.Writeline() calls in the moved method?
I'm thinking along the lines of adding an observable collection (of strings) to store the messages and then getting any calling assemblies to subscribe to changes on the collection and implement their own ui mechanisms for displaying messages back to the user.
Is this reasonable or is this reinventing the wheel? Is there already a purpose built method in the framework to handle a scenario like this?
Edit:
Thanks to #astef, I implemented it like so:
public interface IMessageObserver
{
void Notify(string message);
void Notify(string format, params object[] args);
}
public class ConsoleMessageObserver : IMessageObserver
{
public void Notify(string message)
{
Console.WriteLine(message);
}
public void Notify(string format, params object[] args)
{
Console.WriteLine(format, args);
}
}
class Program
{
static void Main()
{
Library.LongRunningMethod(new ConsoleMessageObserver());
}
}
static class Library
{
public static void LongRunningMethod(IMessageObserver observer)
{
observer.Notify("Some progress happened...");
}
}
If you need someone to handle your progress messages, just define it:
public interface IProgressObserver
{
void NotifyProgress(double done);
}
And use:
public void YourLongRunningMethod(IProgressObserver progressObserver)
{
// ...
progressObserver.NotifyProgress(1d);
}
Now you can be more specific on who will actually handle this messages. For example:
public class ConsoleProgressObserver : IProgressObserver
{
public void NotifyProgress(double done)
{
Console.WriteLine("Progress: {0:0.00}%", done * 100);
}
}
Here's one that was used in Windows 98 system ;)
public class StuckingProgressObserver : IProgressObserver
{
private const double stucksAfter = 0.95;
private readonly IProgressObserver wrapee;
public StuckingProgressObserver(IProgressObserver wrapee)
{
this.wrapee = wrapee;
}
public void NotifyProgress(double done)
{
if (done < stucksAfter)
{
wrapee.NotifyProgress(done);
}
}
}
Using an event, like #astef suggests is a good idea, but their code isn't idiomatic C#. .NET supports delegates directly, so there is no need for a one method interface.
In C# we'd define a class ProgressChangedEventArgs : EventArgs and then add an EventHandler<ProgressChangedEventArgs> ProgressChanged to the class that generates the event.
In fact the framework already includes those in the System.ComponentModel namespace.
Related
Is there a way to call a method to be executed before another method, like a trigger?
Something like an attribute that indicates the method to be executed, like this:
[OnBefore(MethodToBeExecutedBefore)]
public void MethodExecutedNormally()
{
//method code
}
I have a situation that I need to call a check method very often, and most of the time, they are before methods that take too long to execute.
There is no built in way to achieve this result, if you are using a dependency injection mechanism you can use the interception facilities if the DI framework supports this. (Ex: Unity, NInject)
If you want to go low level you can also use Reflection.Emit to create a derived class at runtime, that overrides methods with a particular attribute that invokes any extra functionality you want, but that is more difficult.
What you are talking about is called AOP or Aspect Oriented Programming.
There are no built-in options in C#. While Attributes exists, there is no mechanism to take any actions with them. You always need a piece of code that reads those attributes and then does something. Attributes themselves are only metadata and markers.
As far as external tools go, Postsharp is the de-facto standard AOP postcompiler for .NET, but it's not free (at least not for real use, there is a free version you may want to try, maybe it's enough for your use-case).
I think you should consider an event driven approach.
You could create an interface and some base classes to handle the event, then have your long running classes inherit from it. Subscribe to the event and handle accordingly:
public delegate void BeforeMethodExecutionHandler<TArgs>(ILongRunningWithEvents<TArgs> sender, TArgs args, string caller);
public interface ILongRunningWithEvents<TArgs>
{
event BeforeMethodExecutionHandler<TArgs> OnBeforeMethodExecution;
}
public class LongRunningClass<TArgs> : ILongRunningWithEvents<TArgs>
{
private BeforeMethodExecutionHandler<TArgs> _onBeforeMethodExecution;
public event BeforeMethodExecutionHandler<TArgs> OnBeforeMethodExecution
{
add { _onBeforeMethodExecution += value; }
remove { _onBeforeMethodExecution -= value; }
}
protected void RaiseOnBeforeMethodExecution(TArgs e, [CallerMemberName] string caller = null)
{
_onBeforeMethodExecution?.Invoke(this, e, caller);
}
}
public class ConcreteRunningClass : LongRunningClass<SampleArgs>
{
public void SomeLongRunningMethod()
{
RaiseOnBeforeMethodExecution(new SampleArgs("Starting!"));
//Code for the method here
}
}
public class SampleArgs
{
public SampleArgs(string message)
{
Message = message;
}
public string Message { get; private set; }
}
Sample usage:
public static void TestLongRunning()
{
ConcreteRunningClass concrete = new ConcreteRunningClass();
concrete.OnBeforeMethodExecution += Concrete_OnBeforeMethodExecution;
concrete.SomeLongRunningMethod();
}
private static void Concrete_OnBeforeMethodExecution(ILongRunningWithEvents<SampleArgs> sender, SampleArgs args, string caller)
{
Console.WriteLine("{0}: {1}", caller ?? "unknown", args.Message);
}
The message SomeLongRunningMethod: Starting! will be output before the long-running method executes.
You could add the caller name to the args. I whipped this out real quick to illustrate.
UPDATE: I see you added tags for ASP.NET MVC. The concept still applies to controllers as controllers are just classes.
As a disclaimer, I am completely new to C#. That been said, I was looking for a lightweight pub/sub library in C# I can use which would be similar to something like this in Javascript that I am used to. However all I could find was for .NET version 4 or higher. I have to use .NET 3.5. So I decided to write my own and I call this an EventBus. The goal is to have other classes to freely subscribe / publish events that are pre-defined in the EventBus. However I found out that the "event" in C# is not first class citizen so I couldn't pass that as a parameter to a function. So I decided to pass enum instead to indicate which event is of interest at the moment. My EventBus works fine as intended but as you can see in my code, I run into the problem of keep writing switch cases in all 3 functions whenever I add a new event. Here is my code.
public class EventBus {
public delegate void EventListener(object source, EventArgs args);
private static event EventListener MapLocationMarkerClicked;
public static void Subscribe(Event eventToSub, EventListener listener)
{
switch(eventToSub)
{
case Event.MapLocationMarkerClicked:
MapLocationMarkerClicked += listener;
break;
}
}
public static void Unsubscribe(Event eventToUnsub, EventListener listener)
{
switch (eventToUnsub)
{
case Event.MapLocationMarkerClicked:
MapLocationMarkerClicked -= listener;
break;
}
}
public static void Publish(Event eventToPub, object source, EventArgs args)
{
switch (eventToPub)
{
case Event.MapLocationMarkerClicked:
MapLocationMarkerClicked(source, args);
break;
}
}
}
public enum Event
{
MapLocationMarkerClicked
}
Is there a way to achieve all of the subscribe / unsubscribe / publish actions without switch statements like this? I was wondering how C#'s reflection could help in this situation. Maybe once the enum (or a literal string) is passed to indicate an event, it is used to find the event using reflection and perform the action?
You don't need reflection, you can use an EventHandlerList. However, the EventHandlerList does not take an enum, but an object as a key. But you can easily overcome this restriction with an intermediate dictionary:
static Dictionary<Event, object> eventMap = new Dictionary<Event, object>() { { Event.MapLocationMarkerClicked, new object() } };
static EventHandlerList events = new EventHandlerList();
public static void Subscribe(Event eventToSub, EventListener listener)
{
events.AddHandler(eventMap[eventToSub], listener);
}
public static void Unsubscribe(Event eventToUnsub, EventListener listener)
{
events.RemoveHandler(eventMap[eventToSub], listener);
}
public static void Publish(Event eventToPub, object source, EventArgs args)
{
EventListener listener = (EventListener)events[eventMap[eventToSub]];
listener?.Invoke(source, args);
}
You will probably want to take a look at the IObservable<T>/IObserver<T> interfaces, which are the .NET publish/subscribe interfaces.
Thank for all the interest in this question. Some of you have asked for more clarity on code involved, so in order to provide a bit more info, im going to edit it to provide a bit more detail.
Relating to my previous question, I am attempting to emulate a basic console in a WPF window (text output only). It's meant to work with a program that has a lot of code running in the background running on separate threads. This code relies heavily on a while loop as well, so my plan is to keep the WPF console window on the main thread (along with any additional GUI windows that might be needed) and execute all the code on separate threads.
The window has a WriteLine method used like so:
mainConsole.WriteLine("This is a message for the user.", SomeSender);
The rest of the code is going to need to call this method regularly.
Additional info:
The window itself is comprised of a Textblock wrapped in a Scroller. The WriteLine method of the window adds the message and formatting (font, font size, and colour - dependent on who the sender of the message is) to a List of objects which contain this information, and then displays the list of these messages including their formatting) as the content of the Textblock. The method works exactly as intended so doesn't need re-writing, it just needs to be accessible.
I've tried to keep this description as concise as possible. For more information, please see my previous question.
So my question now is: Is there an efficient way to make the window's WriteLine method usable to all threads from any class, thus enabling me to use it just like Console.WriteLine()?
While you have a number of options, it sounds like, in your case, it really does make sense for anyone, anywhere, to be able to write to your console. Given that, I'd create something like this:
public class MyConsole
{
public static event Action<string> TextWritten;
public static void Write(object obj)
{
string text = (obj ?? "").ToString();
if (TextWritten != null)
TextWritten(text);
}
public static void WriteLine(object obj)
{
Write(obj + "\n");
}
}
Then have your console form subscribe to the TextWritten event and, when text is written, write that text to the console. (Make sure to marshal to the UI thread first.)
The main advantage of using and event here, as opposed to having this class directly deal with your form, is that you can trivially add additional event handlers allowing you to interact with standard input/output, to add additional logging to files, to have multiple console forms open at once, etc. This flexibility can be useful for both debugging (i.e. additional writeouts to a flat file) and in production (allowing much easier redirection through standard in/out).
It seems you are trying to write a logging service that would allow you to access the log from anywhere within your code. You mention threads, so you will have to be mindful and handle that synchronization accordingly.
I would first create an ILogger interface such as this:
public interface ILogger
{
void Log(string line);
void Log(string format, params object[] args);
}
Then a proper Logger base class:
public abstract class Logger : ILogger
{
public abstract void Log(string line);
public virtual void Log(string format, params object[] args)
{
Log(string.Format(format, args));
}
}
Of course, you will need an actual implementation:
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class ConcurrentLogger : Logger, ILogger, IDisposable
{
bool isDisposed;
BlockingCollection<string> loggedLines;
Action<string> callback;
public ConcurrentLogger(Action<string> callback)
{
if (callback == null)
throw new ArgumentNullException("callback");
var queue = new ConcurrentQueue<string>();
this.loggedLines = new BlockingCollection<string>(queue);
this.callback = callback;
StartMonitoring();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool isDisposing)
{
if (isDisposed) return;
if (isDisposing)
{
if (loggedLines != null)
loggedLines.CompleteAdding();
}
isDisposed = true;
}
public override void Log(string line)
{
if (!loggedLines.IsAddingCompleted)
loggedLines.Add(line);
}
protected virtual void StartMonitoring()
{
Task.Factory.StartNew(() =>
{
foreach (var line in loggedLines.GetConsumingEnumerable())
{
if (callback != null)
callback(line);
}
loggedLines.Dispose();
}, TaskCreationOptions.LongRunning);
}
}
For global access, you will need a Singleton class, so I would make this LogManagerclass:
public sealed class LogManager : ILogger
{
#region Singleton
static readonly LogManager instance = new LogManager();
public static LogManager Current { get { return instance; } }
private LogManager() { } // Disallow creating instances.
#endregion
ILogger logger;
public ILogger Logger { get { return logger; } }
public void StartLogging(ILogger logger)
{
if (logger == null)
throw new ArgumentNullException("logger");
this.logger = logger;
}
public void StopLogging(bool dispose = true)
{
var previousLogger = this.logger as IDisposable;
this.logger =null;
if (previousLogger != null && dispose)
previousLogger.Dispose();
}
public void Log(string line)
{
if (logger != null) logger.Log(line);
}
public void Log(string format, params object[] args)
{
if (logger != null) logger.Log(format, args);
}
}
With some quick initialization:
void InitializeLog()
{
var log = new ConcurrentLogger(LogToTextBox);
LogManager.Current.StartLogging(log);
}
void LogToTextBox(string line)
{
if (!CheckAccess())
{
this.Dispatcher.BeginInvoke((Action<string>)LogToTextBox,
DispatcherPriority.Background,
line);
return;
}
logTextBox.AppendText(line + Environment.NewLine);
}
Then anywhere in your code you can call: LogManager.Current.Log(...);
Create a static class that holds the WriteLine method and a Property referencing the window, control or whatever you need inside the writeline method.
Then add some code to your MainWindow constructor or loaded event to set the Reference-Property to the needed item.
Afterwards you can use Writeline from wherever you want.
BTW: This may be much cleaner using a static MainViewModel with an Instance getter, bind the DataContext of the MainWindow to this ViewModel and use the MVVM pattern. Youy would then only set some ConsoleOutput property or call an AddLine method or even Command from wherever you want and don't have to know how it is displayed by the View. You can test your app using unit tests, you can change the visual representation, ... all withou touching the logic of your application.
namespace {yourrootnamespace}
{
namespace GlobalMethods
{
static class ConsoleMethods
{
mainConsole mc;
public static WriteLine(string msg, object sender)
{
lock (this)
{
mc.WriteLine(msg, sender)
}
}
static ConsoleMethods()
{
mc = new mainConsole();
}
//more methods
}
}
And then: using {yourrootnamespace}.GlobalMethods;
Or alternatively let the methods take in a mainConsole argument which it then uses to call.
I started a small app (C#, .Net4, console app) and it was a basic idea for moving files around at home based on rules.
This app has grown and become extremely useful. So my task is to break it into more reusable classes and smaller projects (class libraries).
I have a generic 'Show' function that accepts a string, and a error_level id. Based on that, I would output text to my console window in a certain colour. All is fine when it's all in one big class, but I want to move a method to it's own class libabry - however, I want it to report updates while it's processing, to my UI (Console window, for now). When I move it to the class, obviously, class to my 'Show' method', break.
Is there a way I can get messages sent from my class method, back to my UI? It's messages like, 'Opened Config file', 'Processing 12 new files', 'Success'.
And as it happens, the UI gets the messages and displays them, while the method finishes it's job.
At the moment, it's a Console App project. My plan is to rip out the working code, keeping the console app for testing, and later, change the 'UI' into a nice WPF desktop application. (I'm trying to learn WPF, and decided to use a small project I started ages ago, and 'skin it').
I would suggest that you add an interface, implement that interface in your UI, and pass a reference to the class that implements the interface to your new classes.
This approach should work if you are performing the work in a single thread or multiple threads.
For example, the interface:
public interface INotify
{
void Notify(string Msg);
}
the UI:
public class Form1 : INotify
{
// This is the method where you instantiate the new worker process
public void DoSomeWork() {
NewClass Worker = New NewClass(this);
}
public delegate void NotifyDelegate(string Msg);
public void Notify(string Msg)
{
txtLog.Text += Msg + Environment.NewLine;
}
void INotify.Notify(string Msg)
{
this.INotify_Notify(Msg);
}
private void INotify_Notify(string Msg)
{
if (this.InvokeRequired)
{
this.Invoke(new NotifyDelegate(Notify), Msg);
}
else
{
this.Notify(Msg);
}
}
}
and the new class (just call notify in this class to send the message):
public class NewClass
{
private INotify m_Notifier;
private void Notify(string Msg)
{
m_Notifier.Notify(Msg);
}
public NewClass(INotify oNotifier)
{
m_Notifier = oNotifier;
}
}
Update with alternate implementation
An alternate implementation, which will work with static classes, is to implement a delegate.
For example, here is the delegate:
public delegate void NotifyDelegate(string Msg);
Here is the sample static class for the console app:
static class Program
{
private static NotifyDelegate m_Notifier;
static void Main(string[] args)
{
m_Notifier = new NotifyDelegate(Notify);
NewClass oNewClass = new NewClass(m_Notifier);
// Your work code here
}
static void Notify(string Msg)
{
Console.WriteLine(Msg);
}
}
and a revised version of the work class:
public class NewClass
{
private NotifyDelegate m_Notifier;
public void Notify(string Msg)
{
m_Notifier.Invoke(Msg);
}
public NewClass(NotifyDelegate oNotifier)
{
m_Notifier = oNotifier;
}
}
If i understand your question correctly i would implement event handling so that your UI can subscribe to some sort of status event.
An alternative would be to use some kind of Logging Framework like NLog and log to a static method in your UI via the methodCall target.
Since you are using WPF it would make sense to use MVVM. This would probably be the best way to create powerful and maintainable UIs.
Well this is usually done with Binding, you bind your viewModel with the view, and any changes to the viewModel, will be directly displayed in your UI.
Could the class/es that do work raise an event that the class on the UI thread is listening to? The 'worker' class would raise an event with some parameters, the listener class would then write that information to the UI.
I am trying to use a method inside class, from another class.
namespace Crystal.Utilities
{
public class Logging
{
public static void Log()
{
//dostuff
Crystal.MainForm.general_log_add_item("Hello World");
}
}
}
namespace Crystal
{
public partial class MainForm : Form
{
public void general_log_add_item(string msg)
{
listBox1.Items.Add(msg);
}
}
}
I want to be able to call Crystal.Utilities.Logging.Log() from anywhere, and that to be able to call Crystal.MainForm.general_log_add_item() . But It doesn't let me, because if I put it as public, then I can't see it, if it's static then It can't interact with my listbox.
This is a wrong approach. Your class should not call into the UI, as the UI could change. The class should not know nor care about the UI. Instead, the class could expose an event that the form could subscribe to, and update based upon the information contained within the event's arguments.
Here's a hastily thrown together example.
class Program
{
static void Main()
{
Logger.OnLogging += Logger_OnLogging;
Logger.Log();
Logger.OnLogging -= Logger_OnLogging;
}
static void Logger_OnLogging(LoggingEventArgs e)
{
Trace.WriteLine(e.Message);
}
}
public class Logger
{
public delegate void LoggingEventHandler(LoggingEventArgs e);
public static event LoggingEventHandler OnLogging;
public static void Log()
{
// do stuff
RaiseLoggingEvent("Data logged");
}
protected static void RaiseLoggingEvent(string message)
{
if (OnLogging != null)
OnLogging(new LoggingEventArgs(message));
}
}
public class LoggingEventArgs : EventArgs
{
public LoggingEventArgs(string message)
{
this.Message = message;
}
public string Message { get; private set; }
}
Instead of implementing it as a static method, try implementing as a singleton. It's a common trick to make an instance global in scope, and restrict to one instance, without making everything static (and thus unable to be used as an instance).
You have to understand that the window is not static, there is one instance of him, thats why the method cant be static,
you can use
Application.Windows to reach this instance and call the add method.
or you can register the window in his constructor on another class that will mediate the Logging and the window.
If you don't understand tell me and I'll try to be more clear
When you declare a method as "static" you're saying that it's not dependent upon a specific instance of the class it's in.
For example if you have a class named "chair" and you want to count how many chairs there are, you'll do that with a static field, and a static method to return that field's value.
The count of all chairs is not related to a specific chair.
In your case you want to add a static method to add an item to a specific instance of a Form. That's impossible and doesn't make sense.
If you want to add an item to a listBox, it must be through a public method.
So basically what I'm saying is - rethink what you're trying to do, there's a good explanation as to why you're not succeeding in doing that.