如何正确地将 `TempDataDictionary` 注入我的 类?

How can I correctly inject `TempDataDictionary` into my classes?

我有一个使用 c# 和 ASP.NET MVC 5 编写的应用程序。我还使用 Unity.Mvc 进行依赖注入。

与许多其他 classes 一起,class MessageManagerIoC 容器中注册。但是,MessageManager class 依赖于 TempDataDictionary 的实例来执行其工作。此 class 用于为视图写入临时数据。

为了解析 MessageManager 的实例,我还需要注册 TempDataDictionary 的实例。我需要能够从 MessageManager class 向 TempDataDictionary 添加值,然后我需要从视图访问临时数据。因此,我需要能够在视图中访问 TempDataDictionary 的相同实例,以便我可以将消息写给用户。

此外,如果控制器将用户重定向到别处,我不想丢失消息,我仍然希望能够在下一个视图中显示消息。

我尝试了以下方法来注册 TempDataDictionaryMessageManager

Container.RegisterType<TempDataDictionary>(new PerThreadLifetimeManager())
         .RegisterType<IMessageManager, MessageManager>();

然后在我看来,我有以下内容可以解析为 IMessageManager

的实例
var manager = DependencyResolver.Current.GetService<IMessageManager>();

但是,由于某种原因,消息丢失了。也就是说,当我解析 manager 时,TempDataDictionary 不包含 MessageManager 从控制器添加的任何消息。

如何正确注册 TempDataDictionary 的实例,以便数据在被查看之前一直存在?

已更新 这是我的 IMessageManager 界面

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();
}

TempDataDictionaryMessageManager 实现的固有部分,因此,您应该直接在 class 中实现它,而不是在容器中注册它。

类似于:

public class MessageManager : IMessageManager
{
    private TempDataDictionary _tempDataDictionary;

    [...]
}

但是,恕我直言,我认为在控制器上下文之外使用 TempDataDictionary 不是一个好习惯,因此与其在 class 中实现它,不如每次都传递它添加或检索消息:

void AddSuccess(IDictionary<string, object> tempData, string message);

您还可以使用 PerThreadLifetimeManager 为每个请求创建一个 MessageManager 实例,然后您根本不需要使用 TempDataDictionary,您可以自己实现它常规列表或字典:

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; }
    }
}

然后,按线程注册它,以便在每次请求时清除所有内容:

Container.RegisterType.RegisterType<IMessageManager, MessageManager>
        (new PerThreadLifetimeManager());

更好的方法?

如果你想确保列表在被读取之前一直保留,即使它发生在另一个请求中,或者如果你正在使用异步操作或 ajax 请求,你可以创建自己的LifetimeManager 每个会话解析上述 class 实例的实现,例如:

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();
    }
}

然后把上面的PerThreadLifetimeManager换成SessionLifetimeManager,每次访问的时候清空列表即可,例如:

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; 
    }
}

参考:

SessionLifetimeManager 是从这里借来的:https://gist.github.com/CrestApps/a246530e386b95d0a05d36bb13805259

这不是严格意义上的答案,而是一个备选建议:

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.

仅看上面的内容,我就觉得将消息存储在数据库中可能是更好的选择。这样,您甚至可以回到过去并根据需要查看旧消息。

// persist in EF db context
class Message {
    DateTime CreatedUtc { get; set; }
    DateTime SeenUtc { get; set; }
    string Text { get; set; }
    AspNetUser User { get; set; }
    // etc
}

然后您可以跨请求保留此消息,管理何时将消息标记为已读,甚至让用户查看他的旧消息(如果您愿意的话)