Как я могу правильно внедрить `TempDataDictionary` в мои классы? - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть приложение, написанное с использованием c # и ASP.NET MVC 5. Я также использую Unity.Mvc для внедрения зависимостей.

Наряду со многими другими классами, класс MessageManager зарегистрирован в контейнере IoC. Однако класс MessageManager зависит от экземпляра TempDataDictionary для выполнения своей работы. Этот класс используется для записи временных данных для представлений.

Чтобы разрешить экземпляр MessageManager, мне также необходимо зарегистрировать экземпляр TempDataDictionary. Мне нужно было бы иметь возможность добавлять значения в TempDataDictionary из класса MessageManager, а затем мне нужно было бы получить доступ к временным данным из представления. Поэтому мне нужно иметь возможность доступа к одному и тому же экземпляру TempDataDictionary в представлениях, чтобы я мог записывать сообщения пользователю.

Кроме того, если контроллер перенаправляет пользователя в другое место, я не хочу потерять сообщение, я все еще хочу иметь возможность показать сообщение в следующем представлении.

Я попробовал следующее, чтобы зарегистрировать TempDataDictionary и MessageManager:

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

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Это не просто ответ, а скорее альтернативное предложение:

Кроме того, если контроллер перенаправляет пользователя в другое место, я не хочу потерять сообщение, я все еще хочу иметь возможность показать сообщение в следующем представлении.

Просто взглянув на вышесказанное, можно предположить, что хранение ваших сообщений в базе данных может быть лучшим вариантом. Таким образом, вы даже можете вернуться в прошлое и просмотреть старые сообщения, если хотите.

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

Затем вы можете сохранить это сообщение в запросах, указать, когда пометить сообщение как увиденное, и даже позволить пользователю увидеть его старые сообщения (если вы хотите это сделать).

0 голосов
/ 03 мая 2018

TempDataDictionary является неотъемлемой частью вашей MessageManager реализации, поэтому вы должны реализовать его внутри этого класса напрямую, а не регистрировать в контейнере.

Что-то вроде:

public class MessageManager : IMessageManager
{
    private TempDataDictionary _tempDataDictionary;

    [...]
}

Однако, IMHO, я не считаю хорошей практикой использовать TempDataDictionary вне контекста контроллера, поэтому вместо того, чтобы реализовывать его в своем классе, вы можете передавать его каждый раз, когда добавляете или извлекаете сообщение:

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

Вы также можете создать MessageManager экземпляр для каждого запроса, используя PerThreadLifetimeManager, и тогда вам вообще не нужно будет использовать 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, которая разрешает экземпляр вышеупомянутого класса за сеанс, например:

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

...