ASP.NET WebApp Performance issues: How to resolve? - c#

I've been trying to identify some performance issues with our ASP.NET Web application. It's an online application, used by multiple users(10+). Just a quick overview: The applcation uses a combination of web forms, MVC Pages, web services etc... When multiple users connects to the app, it seems to become really slow. After investigating the memory, it seems as if the application is using A LOT of memory and that is slowing down the machine, which indicates unmanaged resources not being disposed of. I installed ANTS, and then captured a few applications on the system. It turns out a lot of the memory is used by unmanaged resources: http://tinypic.com/r/154ujra/7
This is the first time I've been profiling memory. ANTS profiler indicates that one of my classes (RULE) has a high number of live instances: http://tinypic.com/r/1264ltu/7 (Which doesn't seem to be freed up by the GC)
After drilling down into class level, http://tinypic.com/r/2r3v6nq/7, it displays a warning that the class isn't released from memory, and this could be because of an event handler not being unregistered. Now that class does contain an event handler instance, so could it be that?
public class Rule
{
public event EventHandler deleted;
public void Delete()
{
if (baseQuestionnaire.basePortfolio.mode != Mode.Admin)
{
throw new Exception("Rules can only be deleted in Admin mode");
}
else
{
// Delete the rule from the database
if (id != -1)
{
string delete = "DELETE FROM tb" + DataManager.dbPrefix + "_QuestionRule WHERE QuestionRuleId = " + id.ToString();
DataManager.execute(delete);
}
// Raise a deleted event
if (deleted != null)
deleted(this, new EventArgs());
}
}
}
Event is then assigned in another class like this, but never unregistered
public class Option : IComparable
{
public void AddRule(Rule newRule)
{
newRule.deleted += new EventHandler(newRule_deleted);
allRules.Add(newRule);
}
............................
}

Hmm.. I see that the eventhandler 'deleted' is a public one. So my guess is that the Rule object is created first and then the Rule.deleted is assigned an eventhandler. If this is the case then as you suspected, the eventhandler is probably the cause of Rule objects not being garbage collected.
EDIT:
Maybe you could try something like this:
public class Option : IComparable, IDisposable
{
private Rule newRule;
private EventHandler newRuleDeletedEventHandler;
public void AddRule(Rule newRule)
{
this.newRule = newRule;
newRuleDeletedEventHandler = new EventHandler(newRule_deleted);
newRule.deleted += newRuleDeletedEventHandler;
allRules.Add(newRule);
}
public override void dispose()
{
newRule.deleted -= newRuleDeletedEventHandler;
}
}

Related

Why is memory utilization continuiously increasing when using dependency injection in a C# console application?

I may know the answer to my posted question: I'm using constructor dependency injection throughout the entire application which is a looped C# console application that does not exit after each request.
I suspect the life time of all of the included objects is essentially infinite due to this. When attempting to adjust the life time while registering, it warns that a transient object cannot be implemented on a singleton object due to dependencies (which inspired looking at memory utilization and this question).
This is my first ground up console application, a bot, that logs into a service provider and waits for messages. I come from .NET Core Web API which again has dependencies all over, but I think the key difference here is below all of my code is the platform itself which handles each request individually then kills the thread that ran.
How close am I? Would I have to separate the bot itself from the base console application listening to the service provider and attempt to replicate the platform that IIS/kestrel/MVC routing provides to separate the individual requests?
Edit: Originally I intended this question as more of a design principal, best practice, or asking for direction direction. Folks requested reproducible code so here we go:
namespace BotLesson
{
internal class Program
{
private static readonly Container Container;
static Program()
{
Container = new Container();
}
private static void Main(string[] args)
{
var config = new Configuration(args);
Container.AddConfiguration(args);
Container.AddLogging(config);
Container.Register<ITelegramBotClient>(() => new TelegramBotClient(config["TelegramToken"])
{
Timeout = TimeSpan.FromSeconds(30)
});
Container.Register<IBot, Bot>();
Container.Register<ISignalHandler, SignalHandler>();
Container.Register<IEventHandler, EventHandler>();
Container.Register<IEvent, MessageEvent>();
Container.Verify();
Container.GetInstance<IBot>().Process();
Container?.Dispose();
}
}
}
Bot.cs
namespace BotLesson
{
internal class Bot : IBot
{
private readonly ITelegramBotClient _client;
private readonly ISignalHandler _signalHandler;
private bool _disposed;
public Bot(ITelegramBotClient client, IEventHandler handler, ISignalHandler signalHandler)
{
_signalHandler = signalHandler;
_client = client;
_client.OnCallbackQuery += handler.OnCallbackQuery;
_client.OnInlineQuery += handler.OnInlineQuery;
_client.OnInlineResultChosen += handler.OnInlineResultChosen;
_client.OnMessage += handler.OnMessage;
_client.OnMessageEdited += handler.OnMessageEdited;
_client.OnReceiveError += (sender, args) => Log.Error(args.ApiRequestException.Message, args.ApiRequestException);
_client.OnReceiveGeneralError += (sender, args) => Log.Error(args.Exception.Message, args.Exception);
_client.OnUpdate += handler.OnUpdate;
}
public void Process()
{
_signalHandler.Set();
_client.StartReceiving();
Log.Information("Application running");
_signalHandler.Wait();
Log.Information("Application shutting down");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) _client.StopReceiving();
_disposed = true;
}
}
}
EventHandler.cs
namespace BotLesson
{
internal class EventHandler : IEventHandler
{
public void OnCallbackQuery(object? sender, CallbackQueryEventArgs e)
{
Log.Debug("CallbackQueryEventArgs: {e}", e);
}
public void OnInlineQuery(object? sender, InlineQueryEventArgs e)
{
Log.Debug("InlineQueryEventArgs: {e}", e);
}
public void OnInlineResultChosen(object? sender, ChosenInlineResultEventArgs e)
{
Log.Debug("ChosenInlineResultEventArgs: {e}", e);
}
public void OnMessage(object? sender, MessageEventArgs e)
{
Log.Debug("MessageEventArgs: {e}", e);
}
public void OnMessageEdited(object? sender, MessageEventArgs e)
{
Log.Debug("MessageEventArgs: {e}", e);
}
public void OnReceiveError(object? sender, ReceiveErrorEventArgs e)
{
Log.Error(e.ApiRequestException, e.ApiRequestException.Message);
}
public void OnReceiveGeneralError(object? sender, ReceiveGeneralErrorEventArgs e)
{
Log.Error(e.Exception, e.Exception.Message);
}
public void OnUpdate(object? sender, UpdateEventArgs e)
{
Log.Debug("UpdateEventArgs: {e}", e);
}
}
}
SignalHandler.cs
This isn't directly related to my problem, but it is holding the application in a waiting pattern while the third party library listens for messages.
namespace BotLesson
{
internal class SignalHandler : ISignalHandler
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
private readonly SetConsoleCtrlHandler? _setConsoleCtrlHandler;
public SignalHandler()
{
if (!NativeLibrary.TryLoad("Kernel32", typeof(Library).Assembly, null, out var kernel)) return;
if (NativeLibrary.TryGetExport(kernel, "SetConsoleCtrlHandler", out var intPtr))
_setConsoleCtrlHandler = (SetConsoleCtrlHandler) Marshal.GetDelegateForFunctionPointer(intPtr,
typeof(SetConsoleCtrlHandler));
}
public void Set()
{
if (_setConsoleCtrlHandler == null) Task.Factory.StartNew(UnixSignalHandler);
else _setConsoleCtrlHandler(WindowsSignalHandler, true);
}
public void Wait()
{
_resetEvent.WaitOne();
}
public void Exit()
{
_resetEvent.Set();
}
private void UnixSignalHandler()
{
UnixSignal[] signals =
{
new UnixSignal(Signum.SIGHUP),
new UnixSignal(Signum.SIGINT),
new UnixSignal(Signum.SIGQUIT),
new UnixSignal(Signum.SIGABRT),
new UnixSignal(Signum.SIGTERM)
};
UnixSignal.WaitAny(signals);
Exit();
}
private bool WindowsSignalHandler(WindowsCtrlType signal)
{
switch (signal)
{
case WindowsCtrlType.CtrlCEvent:
case WindowsCtrlType.CtrlBreakEvent:
case WindowsCtrlType.CtrlCloseEvent:
case WindowsCtrlType.CtrlLogoffEvent:
case WindowsCtrlType.CtrlShutdownEvent:
Exit();
break;
default:
throw new ArgumentOutOfRangeException(nameof(signal), signal, null);
}
return true;
}
private delegate bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handlerRoutine, bool add);
private delegate bool SetConsoleCtrlEventHandler(WindowsCtrlType sig);
private enum WindowsCtrlType
{
CtrlCEvent = 0,
CtrlBreakEvent = 1,
CtrlCloseEvent = 2,
CtrlLogoffEvent = 5,
CtrlShutdownEvent = 6
}
}
}
My original point is based off of some assumptions I am making on SimpleInject--or more specifically the way I am using SimpleInject.
The application stays running, waiting on SignalHandler._resetEvent. Meanwhile messages come in via any of the handlers on Bot.cs constructor.
So my thought/theory is Main launches Bot.Process which has a direct dependency on ITelegramClient and IEventHandler. In my code there isn't a mechanism to let these resources go and I suspect I was assuming the IoC was going to perform magic and release resources.
However, sending messages to the bot continuously increases the number of objects, according to Visual Studio memory usage. This is reflected in actual process memory as well.
Though, while editing this post for approval, I think I may have ultimately been misinterpreting Visual Studio's diagnostic tools. The application's memory utilization seems to hang out at around 36 MB after 15 minutes of run time. Or it's simply increasing so little at a time that it's difficult to see.
Comparing Memory Usage snapshots I took at 1 minute versus 17 minutes, there appears to have been 1 of each of the objects above created. If I am reading this properly, I imagine that proves the IoC is not creating new objects (or they are being disposed before I have a chance to create a snapshot.
The key to your answer is in the resume of your observation when profiling your application's memory: "there appears to have been 1 of each of the objects above created". Since all those objects live inside an infinite application loop, you don't have to worry about their lifetime.
From the code you've posted, the only expensive objects that are created dynamically but won't accumulate during the lifetime of Bot are the exception objects (and their associated call stacks), especially when exceptions are caught by a try-catch.
Assuming that the "Simple Injector" library you are using works properly, there is no reason to doubt the lifetime management being correctly implemented like you did. This means it only depends the way your container is configured.
Right now all your instances have a Transient lifetime, which is the default. It's important to notice this, as it appears you are expecting a Singleton lifetime.
Transient means a new instance for every request opposed to Singleton where the same shared instance is returned for each request. To achieve this behavior you must explicitly register the export with a Singleton lifetime defined:
// Container.GetInstance<IBot>() will now always return the same instance
Container.Register<IBot, Bot>(Lifestyle.Singleton);
Never use a Service Locator, especially when using Dependency Injection, just to manage an object's lifetime. As you can see, the IoC conatiner is designed to handle that. It's a key feature that is implemented by every IoC library. Service Locator can be and should be replaced by proper DI e.g., instead of passing around the IoC container you should inject abstract factories as a dependency. A direct dependency on the Service Locator introduces an unwanted tight coupling. It's very difficult to mock a dependency on a Service Locator when writing test cases.
The current implementation of Bot is also quite dangerous whe thinking about memory leaks, especially in case of the exported TelegramBotClient instance being Singleton and the EventHandler having a transient lifetime.
You hook the EventHandler to the TelegramBotClient. When the lifetime of Bot ends, you still have the TelegramBotClient keeping the EventHandler alive, which creates a memory leak. Also every new instance of Bot would attach new event handlers to the TelegramBotClient, resulting in multiple duplicate handler invocations.
To always be on the safe side you should either unsubscribe from the events immediately when they are handled or when the scopes lifetime ends e.g. in a Closed event handler or in the Dispose method. In this case make sure the object is disposed properly by client code. Since you can't always guarantee that a type like Bot is disposed properly, you should consider to create configured shared instances of the TelegramBotClient and EventHandler using an abstract factory. This factory returns a shared TelegramBotClient where all its events are observed by the shared EventHandler.
This ensures that events are subscribe to only once.
But the most preferable solution is to use the Weak-Event pattern.
You should notice this as you seem to have some trouble to determine object lifetimes and potential memory leaks.
Using your code it is very easy to create a memory leak accidentally.
If you want to write robust applications, it is essential to know the main pitfalls to create memory leaks: Fighting Common WPF Memory Leaks with dotMemory, 8 Ways You can Cause Memory Leaks in .NET, 5 Techniques to avoid Memory Leaks by Events in C# .NET you should know

MassTransit message consumers never see subclass type

This question deals with events (base class events and subclass events) and event handlers. I'm working on existing code, that doesn't seem to work the way the author expected it. I have difficulty understanding why it doesn't work though, so I want to understand what's going on before I try to fix the existing code.
I've found the following question, which may or may not suggest I need to make an additional event handler for the subtype events:
C#: Raising an inherited event
If making an additional event handler is indeed the solution, I would still like to learn why this is the case. This is my first question here, and I did really try to search for the answer/explanation to my question, but sincere apologies if it's still something I should've easily found. A stern "RTFM!" with a educational link would be fine with me at this point :)
We have 2 event classes, a base type and a subtype. The subtype event exists to deal with deletion events.
public class BaseTypeEvent
{
public Guid Id { get; set; }
public string Name { get; set; }
public BaseTypeEvent()
{ }
public BaseTypeEvent(SomeRandomThing item)
{
Id = item.Id;
Name = item.Name;
}
}
public class SubTypeEvent : BaseTypeEvent
{
public DateTimeOffset Deleted { get; set; }
public SubTypeEvent()
{
Deleted = DateTimeOffset.UtcNow;
}
}
A usage of these events that seems to be failing:
public class UsageClass
{
public UsageClass(IEventBusService eventBusService)
{
eventBusService.MyBaseTypeEvents += HandleMethod;
}
private void HandleMethod(BaseTypeEvent e)
{
if(e is SubTypeEvent)
{
//code that deals with deletion events
//execution never actually gets here
}
//code that deals with events that are not deletion events
}
}
The declaration of the events are in the IEventBusService and EventBusService:
public delegate void MyEventHandler(BaseTypeEvent e);
public interface IEventBusService
{
public event MyEventHandler MyBaseTypeEvents;
void PublishStuff(BaseTypeEvent e);
}
public class EventBusService : IEventBusService, IDisposable
{
public void Initialize()
{
//Bus is MassTransit
Bus.Initialize(sbc =>
{
sbc.Subscribe(subs => subs.Handler<BaseTypeEvent>(OnBaseTypeEvent));
}
}
private void OnBaseTypeEvent(BaseTypeEvent e)
{
if (MyBaseTypeEvents == null) return;
try
{
MyBaseTypeEvents(e);
}
catch (Exception e)
{
//some logging
}
}
public event MyEventHandler MyBaseTypeEvents;
public void PublishStuff(BaseTypeEvent e)
{
//some logging
//publish e to the event bus of our choice (MassTransit)
Bus.Instance.Publish(e);
}
}
And then finally the place where we send the deletion event (to try to delete an item of what I have cleverly named SomeRandomThing above):
eventBusService.PublishStuff(new SubTypeEvent
{
Id = id,
Deleted = DateTimeOffset.UtcNow
});
So the problem: after sending the deletion event with the last line of code above, the if-statement in the UsageClass that checks whether an incoming event is of type SubTypeEvent is never actually true. The type of e in HandleMethod of UsageClass is BaseTypeEvent.
Edit:
I've decided to get rid of the subtyping in this case. We now no longer have BaseTypeEvent and SubTypeEvent, but simply EventTypeA and EventTypeB. One deals with creates and updates, the other deals with deletes (for which we need significantly less information that the creates and updates anyway).
public delegate void MyEventAHandler(EventTypeA e);
public delegate void MyEventBHandler(EventTypeB e);
and
void PublishStuffForA(EventTypeA e);
void PublishStuffForB(EventTypeB e);
and so on.
I've made an extra subscription to MassTransit in the Initialize method of our EventbusService, and made extra handlers in the various UsageClasses that needed them:
sbc.Subscribe(subs => subs.Handler<EventTypeA>(OnEventTypeA));
sbc.Subscribe(subs => subs.Handler<EventTypeB>(OnEventTypeB));
and
public UsageClass(IEventBusService eventBusService)
{
eventBusService.MyEventTypeAEvents += HandleMethodForA;
eventBusService.MyEventTypeBEvents += HandleMethodForB;
}
and so on.
I now no longer have to check if an incoming event is of a certain type, I just handle to two types separately. Perhaps a cop out, but it works.
I'm hesitant to qualify this as the answer to my own question, as #Glubus' comments as well as #Travis' comments were what answered my question. Still thought this small edit write-up might be nice to let everyone know what I did as a solution :)
Edit 2:
Sources of information that were helpful:
Derived types are not published to consumers in MassTransit
MassTransit message mis-typing
MassTransit: Message contracts, polymorphism and dynamic proxy objects
https://groups.google.com/forum/#!searchin/masstransit-discuss/polymorphism/masstransit-discuss/q_M4erHQ7OI/FxaotfIzI7YJ
So I can tell you the short answer:
Using polymorphism in messaging contracts introduces coupling.
We believe, as MassTransit developers, that this is a bad idea. It's still possible, but not out of the box. You have to use binary serialization or a customer serializer. The default the serialization pipeline only populates a proxy of the type in the consumer.

How to disable subscription to an event from many instances of one type and allow only one?

I have Windows Forms application with one main form (derived from base Form). Other modal forms that could be opened there are derived from my class ManagedForm, which is also derived from Form.
Also I have a static notifier service which fires some events like this:
public static class NotifierService
{
public delegate void NotifierServiceEventHandler(object sender, NotifierServiceEventArgs e);
private static readonly object Locker = new object();
private static NotifierServiceEventHandler _notifierServiceEventHandler;
#region Events
public static event NotifierServiceEventHandler OnOk
{
add
{
lock (Locker)
{
_notifierServiceEventHandler += value;
if (
_notifierServiceEventHandler.GetInvocationList()
.Count(
_ =>
_.Method.DeclaringType != null &&
value.Method.DeclaringType != null &&
_.Method.DeclaringType == value.Method.DeclaringType) <= 1)
return;
_notifierServiceEventHandler -= value;
}
}
remove
{
lock (Locker)
{
_notifierServiceEventHandler -= value;
}
}
}
// and many more events similar to previous...
#endregion
#region Event firing methods
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
NotifierServiceEventHandler handler;
lock (Locker)
{
handler = _notifierServiceEventHandler;
}
if (handler == null) return;
handler(typeof (NotifierService),
new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
#endregion
}
So in some places of code these events could be fired like:
NotifierService.NotifyExclamation("Fail!");
In the main form there is StatusStrip control used for notification purposes, and due to main form has subscribtion to these events -- their messages will be shown in the status strip.
BUT!, as I've said earlier, user may open other forms, and these forms could produce others and so on... (they are derived from one class ManagedForm which will be subscribed to NotifierService as soon as it has been created).
In these forms there is another logic how to notify user -- they need to show MessageBoxes with messages. As you can see, I've added some magic in event accessors to allow only one subscriber of any type, because w/o this all opened forms will generate their own MessageBoxes. But when one child ManagedForm has produced another and the second has been closed -- no MessageBoxes will be shown.
What magic should I implement to allow subscription from only first ManagedForm? Many thanks for any ideas.
EDIT: Suggested ideas doesn't solve this issue. I've tried to change event to this:
private static readonly object Locker = new object();
private static EventHandler<NotifierServiceEventArgs> _myEvent;
public static event EventHandler<NotifierServiceEventArgs> OnOk
{
add
{
if (_myEvent == null || _myEvent.GetInvocationList().All(_ => _.Method.DeclaringType != value.Method.DeclaringType))
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
Then I've open one modal child form and create a situation in which event has been fired by NotifierService. One MessageBox has been generated and shown (that's OK). Afterwards I've opened another modal form from first and create another situation in which another event has been fired. One MessageBox has been generated and shown (that's also OK). Now I'm closing second form and making a situation needed to fire event. No MessageBoxes has been shown (but in the status strip of the main form message of event has been shown correctly, so nothing has been changed from my first implementation).
Should I change something in remove clause? I do not need that only one subscriber should be, I need that each of the subscribers should be of distinct types. Sorry If bad English.
The way you are trying to solve the problem is fundamentally wrong by design. Your service class defines an event that will be fired under some circumstances. Some clients subscribe to that event, this way requesting to be notified when it happened. This is simply the .NET way of implementing the Observer pattern, so your service (being the subject or observable), should not apply any logic neither at subscribe nor the notify part, thus defeating the whole purpose of the pattern. Hans Passant already pointed to some flaws in your design, but even his solution is not perfect because looking at the event signature, it's totally unclear that only form instance methods are supposed to be registered - one can try using static method, anonymous lambda/method, some class method etc.
So, IMO the following are some of the viable choices you have.
(A) Keep your NotificationService events, but remove any "magic" from both subscribe and notify parts (shortly, use the regular way of defining and firing an event) and put the logic needed in your subscribers:
public static class NotifierService
{
public delegate void NotifierServiceEventHandler(object sender, NotifierServiceEventArgs e);
public static event NotifierServiceEventHandler OnOk;
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
var handler = OnOk;
if (handler != null)
handler(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
}
Assuming that only the active form is supposed to handle the notifications, the existing handlers in both your MainForm and ManagedForm would use something like this inside their method body
if (this != ActiveForm) return;
// do the processing
You can even create a base form like this
class NotifiedForm : Form
{
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += OnNotifyOK;
// similar for other events
}
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
NotifierService.OnOk -= OnNotifyOK;
// similar for other events
}
protected virtual void OnNotifyOK(object sender, NotifierServiceEventArgs e) { }
// similar for other events
}
and let your MainForm, ManagedForm (and any other is needed) inherit from that and just override the OnNotifyXXX methods and apply their logic.
To conclude, this approach would keep your service abstract and will leave the decisions to the clients of the service.
(B) If the sole purpose of your service is to act like a notification coordinator specifically for your forms, then you can remove events along with subscribe/unsubscribe parts (since Application.OpenForms and Form.ActiveForm already provide enough information needed) and handle the logic in your service. In order to do that, you'll need some sort of a base interface(s) or forms, and the easiest would be to use a similar approach to what was optional in the option (A) by creating a base form class like this
class NotifiedForm : Form
{
public virtual void OnNotifyOK(object sender, NotifierServiceEventArgs e) { }
// similar for other notifications
}
and let your MainForm, ManagedForm and other needed inherit from it. Note that there is no logic here (checking ActiveForm etc.) because now that's the responsibility of the caller. Then the service could be something like this:
public static class NotifierService
{
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
var target = Form.ActiveForm as NotifiedForm;
if (target != null)
target.OnNotifyOK(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
// similar for other notifications
}
if the logic is to notify only the active form.
Or
public static class NotifierService
{
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
// Could also be a forward for, forach etc.
for (int i = Application.OpenForms.Count - 1; i >= 0; i--)
{
var target = Application.OpenForms[i] as NotifiedForm;
if (target != null /* && someOtherCritaria(target) */)
{
target.OnNotifyOK(typeof(NotifierService), new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
// Could also continue
break;
}
}
}
// similar for other notifications
}
if some other logic is needed (which I doubt).
Hope that helps. In any case, option (A) is more flexible and allows much more usage scenarios, but if the usage scenarios are fixed by design, then the option (B) is better because it requires less from the clients (thus being less error prone) and provides a centralized application logic in one place.
I would like you proceed as follows:
Remove the magic from event accessor method and let all the subscribers subscribe to the event. So now you will have your main form and all other forms subscribed to the event.
Now place the magic in your event invocation method. For example in your NotifyOK method, first get the invocation list of deligate, now invoke each deligate one by one using DynamicInvoke or Invoke method of each deligate in the invocation list only if you have not already invoked for the particular DeclaringType. See the algo below:
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
NotifierServiceEventHandler handler;
lock (Locker)
{
handler = _notifierServiceEventHandler;
}
if (handler == null) return;
// Get invocation list of handler as you have done in event accessor
//initialise a new List<T> to hold the declaring types
// loop through each member (delegate) of invocation list
// if the current member declaration type is not in List<t>
// Invoke or DynamicInvoke current delegate
// add the declaration type of current delegate to List<t>
}
Try this:?)
private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;
public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
i have reduced NotifierService to this:
public static class NotifierService
{
public static event EventHandler<NotifierServiceEventArgs> OnOk = delegate { };
public static void NotifyOk(string fullMessage = "Ok.", string shortMessage = null)
{
OnOk(typeof(NotifierService),
new NotifierServiceEventArgs(StatusType.Ok, fullMessage, shortMessage ?? fullMessage));
}
}
and then in ManagedForm used this handler
NotifierService.OnOk += Notify;
private void Notify(object sender, NotifierServiceEventArgs e)
{
// handle event in first open ManagedForm
if (Application.OpenForms.OfType<ManagedForm>().FirstOrDefault() == this)
{
// notification logic
}
}
if forms are opened as Modal (using ShowDialog()), it is possible to use another variant (according to this question):
private void Notify(object sender, NotifierServiceEventArgs e)
{
// handle event in active (last shown) ManagedForm
if (this.CanFocus)
{
// notification logic
}
}
so the idea is that all ManagedForms receive event data and then decide should they do something or not
P.S.: unsubscribe handlers on Dispose
protected override void Dispose(bool disposing)
{
if (disposing)
{
NotifierService.OnOk -= Notify;
}
// default
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
I have made a setup similar to yours & I see the problem.
I'll give 2 working suggestion to fix the issue (you may choose as per the changes required) -
Quickest fix with minimal changes to your original code -
So this is what I understand from the problem situation - You hooked event NotifierService.OnOk to an event handler in class ManagedForm & also wrote code to unhook the event handler from event NotifierService.OnOk when the form closes.
I'm assuming that you wrote the code to unhook the event handler from event NotifierService.OnOk when the form closes
But what I'm not sure is that when do you hook event NotifierService.OnOk to its event handler in managed form. Thats critical & I guess thats the only problem in your setup.
I assume you have set it up at a place which happens only once in the lifetime of form - like constructor or Load Event handler. And thats how I could reproduce the problem.
As fix, Just move hooking the event NotifierService.OnOk to its event handler at a place which which is called everytime the form becomes active
like
something like this -
public partial class ManagedFrom : Form
{
// this is the fix. Everytime the form comes up. It tries to register itself.
//The existing magic will consider its request to register only when the other form is closed or if its the 1st of its type.
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += NotifierService_OnOk;
}
No more change needed, your existing logic in the event will take care of rest.
I have written the reason as comment in code above.
A little Better way but needs more changes
I would like to relieve the event OnOk form all the additional (& magical) responsibilities, I change the event
public static event NotifierServiceEventHandler OnOk
{
add
{
lock (Locker) // I'm not removing the locks. May be the publisher works in a multithreaded business layer.
{
_notifierServiceEventHandler += value;
}
}
remove
{
lock (Locker)
{
_notifierServiceEventHandler -= value;
}
}
}
Instead the subscriber should know when to Start and when to stop the subscription.
Therefore I change ManagedFrom
public partial class ManagedFrom : Form
{
//start the subscription
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
NotifierService.OnOk += NotifierService_OnOk;
}
//stop the subscription
protected override void OnDeactivate(EventArgs e)
{
base.OnDeactivate(e);
NotifierService.OnOk -= NotifierService_OnOk;
}
In both the suggestions, my intend is to just fix the issue without introducing any new pattern. But do let me know if thats needed.
Also do let me know if it was helpful or if you think I took any wrong assumption .
To sum up:
there are multiple sources of events;
there are multiple targets;
there are different types of events which have to be processed differently.
Idea to use static manager is ok (unless you have performance issues, then splitting into multiple different message queues is the option), but cheating with subscribing/unsubscribing feels so wrong.
Make a simple event
public enum MessageType { StatusText, MessageBox }
public NotifyEventArgs: EventArgs
{
public MessageType Type { get; }
public string Message { get; }
public NotifyEventArgs(MessageType type, string message)
{
Type = type;
Message = message;
}
}
public static NotifyManager
{
public event EventHandler<NotifyMessageArgs> Notify;
public static OnEventHandler(MessageType type, string message) =>
Notify?.Invoke(null, new NotifyEventArgs(type, message));
}
Each form has to subscribe to this event when shown and unsubscribe when hidden. Not sure which events are the best here (got used to much to WPF Loaded, Unloaded, but there is no such in winforms, try to use Shown or VisibilityChanged perhaps).
Each form will receive event, but only one has to process MessageBox type (it is safe for all of them to display StatusMessage). For this you need some mechanizm to decide whenever form is the one (used to display message boxes). E.g. it can be active form:
void NotifyManager_Event(object sender, NotifyEventArgs e)
{
if(e.Type == MessageType.MessageBox && this == Form.ActiveForm)
MessageBox.Show(this, e.Message);
else
statusBar.Text = e.Message;
}
Are you sure that it is the task of the NotifierService to make sure that only one Form will show the notification?
If you would describe the tasks of a NotifierService, you would describe what it does and "whenever the NotifierService has something to notify, it will notify everyone who said that it wanted to be notified about the notifications"
This would make your notifierservice less dependant of the current application where it is used. If you want a completely different application with for instance only two Forms, where you want both Forms to react on the notifications you could not use this notifierservice.
But in my Forms application only one form may react on the notifications
That is right: it is your Forms application that has this constraint, not the notifierservice. You make a Forms aplication that may use any kind of notifierservice, but whatever notifierservice is used, only one of the Forms in my application may show the notification.
This means that you should have some rule to know whether a form should show the notifications or not
For instance:
Only the current form may show the notifications
Only the top left form may show the notifications
Only the main form may show the notifications, except when the settings form is visible
So let's assume you have something to determine which Form or Forms may react on notifications. This changes upon something happening: a form becomes active, or a form closes, a form becomes invisible, whatever.
Make a Boolean property for a ManagedForm that holds whether it should show notifications:
class ManagedForm
{
public bool ShowNotifications {get; set;}
public void OnEventNotification(object sender, ...)
{
if (this.ShowNotifications)
{
// show the notification
}
}
Now someone has to know which form should show the notification. This someone should set property ShowNotification.
For instance if only the active ManagedForm should show the notifications then the ManagedForm can decide for itsels:
public OnFormActiveChanged(object sender, ...)
{
this.ShowNotifications = this.Form.IsActive;
}
If all red Forms should show the notifications:
public OnFormBackColorChanged(object sender, ...)
{
this.ShowNotifications = this.Form.BackColor == Color.Red;
}
If you have a lot of Forms, with only a few that show notifications, then a lot events OnShowNotification will be called for nothing, but since this is just a function call it won't be a problem unless you show 1000 forms or so, and I guess in that you have more serious problems.
Summerized
Decide the criterium on which a ManagedForm should show the notifications
Decide when a different form should show the notifications
Create an event handler for when the form changes, let the event handler set property ShowNotification
When the event to show the notification occurs, check the property.
Subscriptions are useful if you actually want these events to propagate to each form, but that doesn't seem like what you want to do. Given any action, your code is needing to show only one dialog box and update the status text of the main form.
Maybe you should consider using a singleton pattern, instead. By using a static event handler, this is essentially what you are already doing.
public class MainAppForm : Form
{
static MainAppForm mainAppForm;
public MainAppForm()
{
mainAppForm = this;
}
public static void NotifyOk(Form sender, string fullMessage = "Ok.", string shortMessage = null)
{
mainAppForm.NotifyOk(sender, fullMessage, shortMessage);
}
public void NotifyOk(Form sender, string fullMessage, string shortMessage)
{
this.statusStrip.Invoke(delegate {
this.statusStrip.Text = shortMessage;
});
}
}

Performance considerations for event subscription

Consider this code:
class GameEventsManager
{
public void StartGameEvent(GameEvent TheGameEvent)
{
SubscribeToGameEvent(TheGameEvent);
TheGameEvent.Begin();
UnsubscribeToGameEvent(TheGameEvent);
}
private void SubscribeToGameEvent(GameEvent TheGameEvent)
{
TheGameEvent.OnPlaySound += OnPlaySound;
TheGameEvent.OnShowWrittenItem += OnShowWrittenItem;
...
}
private void UnsubscribeToGameEvent(GameEvent TheGameEvent)
{
TheGameEvent.OnPlaySound -= OnPlaySound;
TheGameEvent.OnShowWrittenItem -= OnShowWrittenItem;
...
}
}
A GameEvent is a class that basically does this: when Begin() gets called, it raises events that get passed to the GameEventManager, so that it may "make" the appropriate changes to the game environment (this is by further propagating the events to the objects that are responsible for executing each particular instruction, like in the Observer pattern).
Now take into consideration that all of my InventoryItems (can trigger events, such as OnConsume, OnUse) are static fields in their particular classes. Although this may seem a bit rough around the edges, I feel that being able to do:
AddItem(WrittenItems.NoteFromKing) //NoteFromKing is a static field in WrittenItems
makes things a lot simpler, and it's a welcome sight considering I'm working on a quite complex game.
This, however, makes it very hard for me to list ALL of the game's items somewhere, in case this would be needed. Which brings us to my question:
A LevelManager, that manages things such as when the player interacts with a particular item in the level, tells the GameEventsManager to run a particular GameEvent, if required. The GameEventsManager then subscribes to the GameEvent, starts it, and then unsubscribes. Should I expect to see noticeable performance issues while following this subscribe/run/unsubscribe pattern? In the end, the manager might subscribe/unsubscribe to about 20 events inside GameEvent.
In case the subscribe/unsubscribe mechanism is slow, I could make a single subscribe process that runs at game initialization, but that would force me to build an extra structure, to list all of the items.
So, in short, I'd like to know if I should be expecting considerable slowdowns from this kind of implementation. Or more exactly, if subscribing to about 20 events, and then unsubscribing from them is considerably slow.
Language is C#, using .NET 2.0 subset under Unity 4.
This, however, makes it very hard for me to list ALL of the game's items somewhere
Why so? You could create an ItemManager (which is a singleton):
public class ItemManager
{
private static volatile ItemManager _instance;
private static object syncRoot = new Object();
private ObservableCollection<ItemBase> _registeredItems = new ObservableCollection<ItemBase>();
private ItemManager()
{
}
public ItemManager Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new ItemManager();
}
}
return instance;
}
}
public void RegisterItem(ItemBase item)
{
_registeredItems.Add(item);
// Do some stuff here, subscribe events, etc.
}
public void UnregisterItem(item)
{
// Do some stuff here, unregister events, etc.
_registeredItems.Remove(item)
}
}
Afterwards you make all item classes derive from a class called "ItemBase". And in ItemBases Constructor you call this:
ItemManager.Instance.RegisterItem(this);
So you don't have to add every single item manually. For more information about the singleton pattern, take a look here: http://msdn.microsoft.com/en-us/library/ff650316.aspx.
A little benefit of this is also, that you can implement a general communication between the GameManager and the ItemManager.

ViewModel updates every second?

Iam writing an app that shows a list of the remaining time a user has on a course.
I want the list to dynamically update every second so the user has the full overview.
public class ReservationCustomerList : INotifyPropertyChanged
{
public int UnitsLeft { get; set; }
public DateTime? OnCircuitSince { get; set; }
public TimeSpan? TimeLeftDate
{
get
{
if (OnCircuitSince.HasValue)
return TimeSpan.FromSeconds((OnCircuitSince.Value - DateTime.Now).TotalSeconds - UnitsLeft);
return TimeSpan.FromSeconds(UnitsLeft);
}
}
private void FireEverySecond()
{
PropertyChanged.Fire(this, x => x.TimeLeftDate);
}
}
As you can see above the idea is that the model knows when the customer entered the circuit and the time the have left.
As you can see iam thinking of using the INotifyPropertyChanged interface and then actually having a timer on every viewmodel.
However this is my concern. Adding a Timer on every viewmodel seems very bloated, is this really the best way to achieve this ?
Second concern is that if the timer is never stopped wouldn't this result in a memory leak since the timer would never stop and keep the viewmodel alive ?
If this is the case my Viewmodel would also need to implement IDisposable and i would need to remember to run through all viewmodels and dispose them to make sure that these are garbage collected. Are my concerns correct ?
Thanks.
Yes i was thinking of having a timer service to prevent having multiple timers, however having to manually unregister would surely at some point introduce memoery leaks.
So the idea with Weak Events is great.
Iam thinking of doing it something like this:
public class TimerService
{
static Timer Timer;
static FastSmartWeakEvent<EventHandler> _secondEvent = new FastSmartWeakEvent<EventHandler>();
static FastSmartWeakEvent<EventHandler> _minuteEvent = new FastSmartWeakEvent<EventHandler>();
static DateTime LastTime;
public static event EventHandler SecondEvent
{
add { _secondEvent.Add(value); }
remove { _secondEvent.Remove(value); }
}
public static event EventHandler MinuteEvent
{
add { _minuteEvent.Add(value); }
remove { _minuteEvent.Remove(value); }
}
static TimerService()
{
Timer = new Timer(TimerFire, null, 1000, 1000);
}
static void TimerFire(object state)
{
_secondEvent.Raise(null, EventArgs.Empty);
if (LastTime.Minute != DateTime.Now.Minute)
_minuteEvent.Raise(null, EventArgs.Empty);
LastTime = DateTime.Now;
}
}
Do you have any comments ?
I Know i could use a singleton GetInstance (or IoC) however this would just make it more inconvinient to use.
Iam using the WeakEvent implementation that Daniel Grunwald wrote on codeproject. (it gives a very clean class and not much overhead).
http://www.codeproject.com/KB/cs/WeakEvents.aspx
You could have a timer service with a private timer and a public event, that notifies all the viewmodels every second.
Regarding the memory issues, you could register your viewmodel with the timer service when your page is navigated (OnNavigatedTo) and unregister it when the view is closed (OnNavigatedFrom). This way the viewmodels wouldn't have any reference with the timer service when they go out of scope, and they would be garbage collected properly.

Categories

Resources