Подтвердить что ты не робот

Лучший способ выполнения строго типизированных сеансов ASP.NET MVC

Я разрабатываю проект ASP.NET MVC и хочу использовать строго типизированные объекты сеанса. Я применил следующий класс, созданный контроллером, чтобы открыть этот объект:

public class StrongController<_T> : Controller
    where _T : new()
{
    public _T SessionObject
    {
        get
        {
            if (Session[typeof(_T).FullName] == null)
            {
                _T newsession = new _T();
                Session[typeof(_T).FullName] = newsession;
                return newsession;
            }
            else
                return (_T)Session[typeof(_T).FullName];
        }
    }

}

Это позволяет мне определить объект сеанса для каждого контроллера, что соответствует концепции изоляции контроллера. Есть ли лучший/более "правильный" способ, возможно, тот, который официально поддерживается Microsoft?

4b9b3361

Ответ 1

Таким образом, другие объекты не будут иметь доступа к этому объекту (например, ActionFilter). Я делаю это так:

public interface IUserDataStorage<T>
{
   T Access { get; set; }
}

public class HttpUserDataStorage<T>: IUserDataStorage<T>
  where T : class
{
  public T Access
  {
     get { return HttpContext.Current.Session[typeof(T).FullName] as T; }
     set { HttpContext.Current.Session[typeof(T).FullName] = value; }
  }
}

Затем я могу либо ввести IUserDataStorage в конструктор контроллера, либо использовать ServiceLocator.Current.GetInstance(typeof (IUserDataStorage <T> )) внутри ActionFilter.

public class MyController: Controller
{
   // automatically passed by IoC container
   public MyController(IUserDataStorage<MyObject> objectData)
   {
   }
}

Конечно, для случаев, когда это необходимо для всех контроллеров (например, ICurrentUser), вы можете вместо этого использовать инъекцию свойств.

Ответ 2

Это может быть лучше для вас. Я бы просто создал метод расширения, который может получить доступ к вашей сессии. Дополнительным преимуществом метода расширения является то, что вам больше не нужно наследовать от контроллера или вам нужно вводить зависимость, которая действительно не нужна для начала.

public static class SessionExtensions {
  public static T Get<T>(this HttpSessionBase session, string key)  {
     var result;
     if (session.TryGetValue(key, out result))
     {
        return (T)result;
     }
     // or throw an exception, whatever you want.
     return default(T);
   }
 }


public class HomeController : Controller {
    public ActionResult Index() {
       //....

       var candy = Session.Get<Candy>("chocolate");

       return View(); 
    }

}

Ответ 3

http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (извинения за цвета в моем блоге были связаны с темами и только еще не зафиксировали его)

public interface ISessionCache
{
  T Get<T>(string key);
  void Set<T>(string key, T item);
  bool contains(string key);
  void clearKey(string key);
  T singleTon<T>(String key, getStuffAction<T> actionToPerform);
}


public class InMemorySessionCache : BaseSessionCache
{
    Dictionary<String, Object> _col;
    public InMemorySessionCache()
    {
        _col = new Dictionary<string, object>();
    }

    public T Get<T>(string key)
    {
        return (T)_col[key];
    }

    public void Set<T>(string key, T item)
    {
        _col.Add(key, item);
    }

    public bool contains(string key)
    {
        if (_col.ContainsKey(key))
        {
            return true;
        }
        return false;
    }

    public void clearKey(string key)
    {
        if (contains(key))
        {
            _col.Remove(key);
        }
    }
}



public class HttpContextSessionCache : BaseSessionCache
{
   private readonly HttpContext _context;

  public HttpContextSessionCache()
   {
      _context = HttpContext.Current;
   }

  public T Get<T>(string key)
   {
      object value = _context.Session[key];
      return value == null ? default(T) : (T)value;
   }

  public void Set<T>(string key, T item)
   {
      _context.Session[key] = item;
   }

   public bool contains(string key)
   {
       if (_context.Session[key] != null)
       {
          return true;
       }
       return false;
   }
   public void clearKey(string key)
   {
       _context.Session[key] = null;
   }
}

Я придумал это несколько лет назад, и все работает отлично. такая же основная идея, как и все остальные, я думаю, почему microsoft не просто реализует это, поскольку стандарт ускользает от меня.

Ответ 4

Обычно я использую это для ключа сеанса, а затем явно добавляю объекты по мере необходимости. Причина этого в том, что это чистый способ сделать это, и я считаю, что вы хотите, чтобы количество объектов в сеансе было минимальным.

Этот конкретный подход объединяет аутентификацию и сеанс пользователя в одном месте, чтобы вы могли добавлять объекты и забывать об этом. Аргумент может быть сделан, что он большой многословный, но он предотвращает любое удвоение, и вы не должны иметь слишком много объектов в сеансе.

В основной библиотеке или в любом месте, где захотите, может существовать следующее.

    /// <summary>
    /// Provides a default pattern to access the current user in the session, identified
    /// by forms authentication.
    /// </summary>
    public abstract class MySession<T> where T : class
    {
        public const string USERSESSIONKEY = "CurrentUser";

        /// <summary>
        /// Gets the object associated with the CurrentUser from the session.
        /// </summary>
        public T CurrentUser
        {
            get
            {
                if (HttpContext.Current.Request.IsAuthenticated)
                {
                    if (HttpContext.Current.Session[USERSESSIONKEY] == null)
                    {
                        HttpContext.Current.Session[USERSESSIONKEY] = LoadCurrentUser(HttpContext.Current.User.Identity.Name);
                    }
                    return HttpContext.Current.Session[USERSESSIONKEY] as T;
                }
                else
                {
                    return null;
                }
            }
        }

        public void LogOutCurrentUser()
        {
            HttpContext.Current.Session[USERSESSIONKEY] = null;
            FormsAuthentication.SignOut();
        }

        /// <summary>
        /// Implement this method to load the user object identified by username.
        /// </summary>
        /// <param name="username">The username of the object to retrieve.</param>
        /// <returns>The user object associated with the username 'username'.</returns>
        protected abstract T LoadCurrentUser(string username);
    }

}

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

public class CurrentSession : MySession<PublicUser>
{
    public static CurrentSession Instance = new CurrentSession();

    protected override PublicUser LoadCurrentUser(string username)
    {
        // This would be a data logic call to load a user detail from the database
        return new PublicUser(username);
    }

    // Put additional session objects here
    public const string SESSIONOBJECT1 = "CurrentObject1";
    public const string SESSIONOBJECT2 = "CurrentObject2";

    public Object1 CurrentObject1
    {
        get
        {
            if (Session[SESSIONOBJECT1] == null)
                Session[SESSIONOBJECT1] = new Object1();

            return Session[SESSIONOBJECT1] as Object1;
        }
        set
        {
            Session[SESSIONOBJECT1] = value;
        }
    }

    public Object2 CurrentObject2
    {
        get
        {
            if (Session[SESSIONOBJECT2] == null)
                Session[SESSIONOBJECT2] = new Object2();

            return Session[SESSIONOBJECT2] as Object2;
        }
        set
        {
            Session[SESSIONOBJECT2] = value;
        }
    }
}

НАКОНЕЦ Большим преимуществом явного объявления того, что вы хотите в сеансе, является то, что вы можете ссылаться на это абсолютно в любом месте вашего приложения mvc, включая представления. Просто укажите его с помощью:

CurrentSession.Instance.Object1
CurrentSession.Instance.CurrentUser

Опять же, немного менее общий, чем другие подходы, но на самом деле действительно ясно, что происходит, нет другой инсталляции для фальсификации или зависимостей и на 100% безопаснее для контекста запроса.

В другом примечании, подход dicionary - это круто, но вы все равно в конечном итоге со строками повсюду, чтобы ссылаться на вещи. Вы могли бы установить его с помощью перечислений или чего-то еще, но я предпочитаю сильную типизацию и задаю и забываю о вышеупомянутом подходе.

Ответ 5

Да, через несколько лет после того, как этот вопрос был задан, и есть другие способы сделать это... но в случае, если кто-то еще найдет что-то, что сочетает в себе вышеупомянутые подходы, в привлекательный магазин (по крайней мере один, который обжаловал к моей команде и к мне...) Вот что мы используем.

 public enum SessionKey { CurrentUser, CurrentMember, CurrentChart, CurrentAPIToken, MemberBanner }

 public static class SessionCache {

    public static T Get<T>(this HttpSessionStateBase session, SessionKey key)
    {
        var value = session[key.ToString()];
        return value == null ? default(T) : (T) value;
    }

    public static void Set<T>(this HttpSessionStateBase session, SessionKey key, T item)
    {
        session[key.ToString()] = item;
    }

    public static bool contains(this HttpSessionStateBase session, SessionKey key)
    {
        if (session[key.ToString()] != null)
            return true;
        return false;
    }

    public static void clearKey(this HttpSessionStateBase session, SessionKey key)
    {
        session[key.ToString()] = null;
    }
}

Затем в ваших контроллерах вы можете сделать свою вещь с вашими переменными сеанса более строго типизированным способом.

// get member
var currentMember = Session.Get<Member>(SessionKey.CurrentMember);
// set member
Session.Set<Member>(SessionKey.CurrentMember, currentMember);
// clear member
Session.ClearKey(SessionKey.CurrentMember);
// get member if in session
if (Session.Contains(SessionKey.CurrentMember))
{
     var current = Session.Get<Member>(SessionKey.CurrentMember);
}

Надеюсь, это поможет кому-то!