I have an app that is written using c# and ASP.NET MVC 5. I'm also using Unity.Mvc for dependency injection.
Along with many other classes, the class MessageManager
is registered in the IoC
container. However, the MessageManager
class depends on an instance of TempDataDictionary
to perform its work. This class is used to write temporary data for the views.
In order to resolve an instance of MessageManager
I need to also register an instance of TempDataDictionary
. I would need to be able to add values to TempDataDictionary
from the MessageManager
class and then I would need to access the temp data from the view. Therefore, I need to be able to access the same instance of TempDataDictionary
in the views so I can write out the messages to the user.
Also, if the controller redirects the user elsewhere, I don't want to lose the message, I still want to be able to show the message on the next view.
I tried the following to register both TempDataDictionary
and MessageManager
:
Container.RegisterType<TempDataDictionary>(new PerThreadLifetimeManager())
.RegisterType<IMessageManager, MessageManager>();
Then in my view, I have the following to resolve to an instance of IMessageManager
var manager = DependencyResolver.Current.GetService<IMessageManager>();
However, the message gets lost for some reason. That is, when I resolve manager
, TempDataDictionary
doesn't contain any messages that were added by MessageManager
from the controller.
How can I correctly register an instance of the TempDataDictionary
so the data persists until it is viewed?
UPDATED
Here is my IMessageManager
interface
public interface IMessageManager
{
void AddSuccess(string message, int? dismissAfter = null);
void AddError(string message, int? dismissAfter = null);
void AddInfo(string message, int? dismissAfter = null);
void AddWarning(string message, int? dismissAfter = null);
Dictionary<string, IEnumerable<FlashMessage>> GetAlerts();
}
The TempDataDictionary
is an intrinsic part of your MessageManager
implementation, therefore, you should implement it inside that class directly instead of registering it in the container.
Something like:
public class MessageManager : IMessageManager
{
private TempDataDictionary _tempDataDictionary;
[...]
}
However, IMHO, I don't think is a good practice to use TempDataDictionary
out of a controller context so instead of implementing it in your class, you could pass it every time you add or retrieve a message:
void AddSuccess(IDictionary<string, object> tempData, string message);
You could also create a MessageManager
instance for each request using PerThreadLifetimeManager
and then you don't need to use the TempDataDictionary
at all, you can just implement this yourself with regular lists or dictionaries:
public class MessageManager : IMessageManager
{
private List<string> _successMessages = new List<string>();
private List<string> _errorMessages = new List<string>();
private List<string> _warningMessage = new List<string>();
private List<string> _infoMessage = new List<string>();
public void AddSuccess(string message)
{
_successMessages.Add(message);
}
public void AddError(string message)
{
_errorMessages.Add(message);
}
public void AddWarning(string message)
{
_warningMessages.Add(message);
}
public void AddInfo(string message)
{
_infoMessages.Add(message);
}
public List<string> SuccessMessages
{
get { return _successMessages; }
}
public List<string> ErrorMessages
{
get { return _errorMessages; }
}
public List<string> WarningMessages
{
get { return _warningMessages; }
}
public List<string> InfoMessages
{
get { return _infoMessages; }
}
}
Then, register it per thread so everything is cleared out on each request:
Container.RegisterType.RegisterType<IMessageManager, MessageManager>
(new PerThreadLifetimeManager());
A better aproach?
If you want to make sure that the list is kept until it has been read, even if it happens in another request, or if you are using async actions or ajax requests, you can create your own LifetimeManager
implementation that resolves the instance of the above class per session, for example:
public class SessionLifetimeManager : LifetimeManager
{
private string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new PerSessionLifetimeManager();
}
}
Then replace PerThreadLifetimeManager
by SessionLifetimeManager
above and simply clear the list each time you access it, for example:
public List<string> InfoMessages
{
get
{
// Some view has accessed the data, clear the list before returning
var tempInfoMessages = new List<string>(_infoMessages);
_infoMessages.Clear();
return tempInfoMessages;
}
}
Reference:
The SessionLifetimeManager was borrowed from here: https://gist.github.com/CrestApps/a246530e386b95d0a05d36bb13805259
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With