I need to fake HttpContext.Current.Application table in order to access it from my unit tests.
I need to store my data somewhere. I thought that I can just pass instance of NameValueCollectionBase but as I discovered this base type has no indexer so it seems too complicated to use.
So what about faking this part of HttpContext? Is it possible? How can I make it? Will be NUnit.Mocks helpful?
Thank you in advance...
Please go through below links it will help you.
http://www.java2s.com/Open-Source/CSharp/Web-Frameworks/MvcContrib/MvcContrib/TestHelper/Fakes/FakeHttpContext.cs.htm
Mocking and HttpContextBase.get_User()
Thanks
Venkat
If you need indexes for namevaluecollection base please use below code
public static IEnumerable<KeyValuePair<string, string>> ToPairs(this NameValueCollection collection)
{
if(collection == null)
{
throw new ArgumentNullException("collection");
}
return collection.Cast<string>().Select(key => new KeyValuePair<string, string>(key, collection[key]));
}
For just to store data and passing around test methods please use above code.
In this scenario I generate some stubs derived from the base classes in System.Web.Abstractions. I often use this technique for MVC applications as MVC / WebApi controllers contain an abstraction to HttpContext (HttpContextBase)
This way I can stub out HttpContext requirements in my unit / integration tests, here's a sample...
public class MockHttpApplicationState : HttpApplicationStateBase
{
private IDictionary<string, object> _appState = new Dictionary<string, object>();
public override void Add(string name, object value)
{
_appState.Add(name, value);
}
public override object Get(string name)
{
return _appState[name];
}
public override object this[string name]
{
get
{
return _appState[name];
}
set
{
_appState[name] = value;
}
}
}
public class MockHttpContext : HttpContextBase
{
private IDictionary<string, object> _appKeys;
public MockHttpContext()
{
}
/// <summary>
/// Accepts a dictionary of app keys to supply to the HttpApplicationState instance
/// </summary>
/// <param name="applicationState"></param>
public MockHttpContext(IDictionary<string,object> applicationState)
{
_appKeys = applicationState;
}
public override Cache Cache
{
get
{
return HttpRuntime.Cache;
}
}
public override HttpApplicationStateBase Application
{
get
{
var mockAppState = new MockHttpApplicationState();
foreach (string key in _appKeys.Keys)
{
mockAppState.Add(key, _appKeys[key]);
}
return mockAppState;
}
}
public override HttpRequestBase Request
{
get
{
return new HttpRequestWrapper(new HttpRequest(null,"http://localhost",null));
}
}
}
Then my test can establish Controller and Http Context:
private readonly OnlineShop.MVC.Controllers.HomeController _controller =
new MVC.Controllers.HomeController(null,new UnitOfWork());
[OneTimeSetUp]
public void Init()
{
var appKeys = new Dictionary<string, object>();
appKeys.Add("localhost", 1);
var httpContext = new MockHttpContext(appKeys);
_controller.ControllerContext = new ControllerContext()
{
Controller = _controller,
RequestContext = new RequestContext(httpContext, new RouteData())
};
}
[Test]
public void Index_Returns_HomeView()
{
var view = _controller.Index() as ViewResult;
var viewModel = view.Model as MVC.ViewModels.Home;
Assert.IsInstanceOf<OnlineShop.MVC.ViewModels.Home>(viewModel);
Assert.IsTrue(viewModel.FeaturedProducts.Count > 0);
}
And my controller is aware of it's ambient HttpContextBase instance supplying Cache and Application state:
public ActionResult Index()
{
string cacheKey = string.Format("FeaturedProducts-{0}",WebsiteId);
IList<Product> productList = this.HttpContext.Cache[cacheKey] as IList<Product>;
//My app keeps a list of website contexts in the Application. This test returns 1 based on the unit / int tests or a real world db value when hosted on IIS etc..
int websiteId = (int)HttpContext.Application[this.Request.Url.Host];
Related
How do I abstract the connection to my database away?
I am currently working on a nuget package, that sort of unify the way data is retrieved from the database,
thus ensuring that data is collected in a similar manner, regardless which service inherits it.
I though seem to be a bit confused of whether my inherited class, and the properties will be assessible for MyService?
and instantiated correctly?
Here is an example:
public class DataService : IDataservice
{
private readonly CosmosClient client;
public TestClass(CosmosClient client)
{
var kvUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URL");
var secretclient = new SecretClient(new Uri(kvUrl), new DefaultAzureCredential());
var accountEndpoint = secretclient.GetSecret("AccountEndpoint-CosmosDb");
var accountKey = secretclient.GetSecret("AccountKey-CosmosDb");
this.client = new CosmosClientBuilder(accountEndpoint.Value.Value, accountKey.Value.Value).Build();
}
public IDictionary<string, object> GetForm(Guid id)
{
//Contains custom code for fetching form
//Processing fetched form'
//Return form
}
public object GetFormField(string propertyName)
{
//Contains custom code for fetching field
//Processing fetched field
//Return field
}
public string TestString()
{
return "Hello web";
}
}
public class MyService : DataService
{
public MyService()
{
}
public IDictionary<string, object> GetForm(Guid id) => return parent.GetForm(Guid id)
public object GetFormField(string propertyName) => return parent.GetFormField(string propertyName)
public override object GetFormField(string propertyName)
{
return "Hello new world";
}
}
does Myservice have an instantiated version of the CosmosClient?
Is GetForm and GetFormField accessible to MyService - and is it possible to explictly state when an inherited method is overridden as above?
and is this even a good idea? - I feel like I am creating an unessary layer, to ensure that data is fetched uniformly by making a class that everyone can inherit.
I think you could do something like this:
public class TestClass
{
private CosmosClient _client;
private SecretClient _secretClient;
public string kvUrl { get => Environment.GetEnvironmentVariable("KEY_VAULT_URL"); }
public SecretClient secretClient {
get
{
return _secretClient ?? (_secretClient = new SecretClient(new Uri(kvUrl), new DefaultAzureCredential()));
}
}
public KeyVaultSecret accountEndpoint {
get
{
return _secretClient.GetSecret("AccountEndpoint-CosmosDb");
}
}
public KeyVaultSecret accountKey {
get
{
return _secretClient.GetSecret("AccountKey-CosmosDb");
}
}
public CosmosClient client {
get
{
return _client ?? (_client = new CosmosClientBuilder(accountEndpoint.Value, accountKey.Value).Build());
}
}
}
Why dont you make an abstract class for DataService, and set the secrets/connections from the derived classes.
as
public abstract class DataService
{
public DataService(CosmosClient client)
{
var kvUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URL");
var secretclient = new SecretClient(new Uri(kvUrl), new DefaultAzureCredential());
var accountEndpoint = secretclient.GetSecret("AccountEndpoint-CosmosDb");
var accountKey = secretclient.GetSecret("AccountKey-CosmosDb");
this.client = new CosmosClientBuilder(accountEndpoint.Value.Value, accountKey.Value.Value).Build();
}
// your abstract methods here to be implementd by derived classes
}
class some_derived_class:DataService{
some_derived_class:DataService(CosmosClient client):base(client){}
}
How can I mock the Azure Redis Cache?
I would like to write unit test for one of my application which uses Azure Redis Cache. Since I am completely new to mocking and stubbing in writing unit test code, I am looking for help in how I can start with the basic script for mocking/stubbing cache component.
Testing with external resources like databases, files, and caches is integration testing, non unit. What you can test in unit-tests is the fact, that your code is calling caching methods.
So, first, you need an interface of cache service. This interface not only let you test your code, but also let you use different caching servers.
public interface ICache
{
void Add<T>(string key, TimeSpan lifetime, T value);
bool TryGet<T>(string key, out T value);
void Remove(string key);
. . .
}
Second, you need domain code to test:
public class SleepingMembersService
{
private readonly TimeStamp _lifetime = TimeStamp.FromMinutes(5);
private readonly ICache _cache;
private readonly INotifier _notifier;
public SleepingMembersService(ICache cache, INotifier notifier)
{
_cache = cache;
_notifier = notifier;
}
private string MakeKey(User user) => $"unsleepingUser{user.Id}";
public void WakeUpIfSleep(IUser user)
{
var key = MakeKey(user);
bool isWaking;
if (_cache.TryGet(key, out isWaking) && isWaking)
return;
notifier.Notify(user.Id, "Wake up!");
}
public void ConfirmImNotSleeping(IUser user)
{
var key = MakeKey(user);
_cache.Add(key, _lifeTime, true);
}
}
Third, let's make stub cache:
public class StubCache : ICache
{
public bool TryGetResult { get; set; }
public bool TryGetValue { get; set; }
public bool AddValue { get; set; }
public TimeStamp LifeTimeValue { get; set; }
void Add<T>(string key, TimeSpan lifetime, T value)
{
LifeTimeValue = lifetime;
AddValue = (bool)(object)value;
}
bool TryGet<T>(string key, out T value)
{
value = (T)(object)TryGetValue;
return TryGetResult;
}
. . .
}
And finally you can write unit-test:
pubic void ConfirmImNotSleeping_WhenCalled_CallsAdd()
{
var cache = new StubCache<bool>();
var notifier = new StubNotifier();
var service = new SleepingMembersService(cache, notifier);
var user = new StubUser(1, "John Doe");
service.ConfirmNotSleeping(user);
Assert.IsTrue(cache.AddValue);
}
Well, you've checked that the method ConfirmNotSleeping calls the method Add.
Now, you should implement ICache for Redis:
public RedisCache : ICache
{
private IConnectionMultiplexer connection;
public bool TryGet<T>(string key, out T value)
{
var cache = Connection.GetDatabase();
var rValue = cache.StringGet(key);
if (!rValue.HasValue)
{
value = default(T);
return false;
}
value = JsonConvert.DeserializeObject<T>(rValue);
return true;
}
. . .
}
To simplify implement stubs and mocks you can use libraries like Moq. These libraries let you automatically generate stubs and mocks for your purpose. So you test code will look like this:
pubic void ConfirmImNotSleeping_WhenCalled_CallsAdd()
{
var cacheStub = new Mock<ICache>();
var notifierStub = new Mock<INotifier>();
var service = new SleepingMembersService(cache.Object, notifier.Object);
var userStub = new Mock<IUser>();
service.ConfirmNotSleeping(user.Object);
cacheStub.Vertify(x => x.Add(It.IsAny<string>(), It.IsAny<TimeStamp>(), true));
}
I'm using Prism with IoC. The problem is to pass an object (like collections) through navigation. I was watching this post: How to Pass an object when navigating to a new view in PRISM 4
And this is the solution
I extract the hash code of the object and save it in a Dictionary, with the hash code as the key and the object as the value of the pair.
Then, I attach the hash code to the UriQuery.
After, I only have to get the hash code that comes from the Uri on the target view and use it to request the original object from the Dictionary.
Some example code:
Parameter repository class:
public class Parameters
{
private static Dictionary<int, object> paramList =
new Dictionary<int, object>();
public static void save(int hash, object value)
{
if (!paramList.ContainsKey(hash))
paramList.Add(hash, value);
}
public static object request(int hash)
{
return ((KeyValuePair<int, object>)paramList.
Where(x => x.Key == hash).FirstOrDefault()).Value;
}
}
The caller code:
UriQuery q = null;
Customer customer = new Customer();
q = new UriQuery();
Parameters.save(customer.GetHashCode(), customer);
q.Add("hash", customer.GetHashCode().ToString());
Uri viewUri = new Uri("MyView" + q.ToString(), UriKind.Relative);
regionManager.RequestNavigate(region, viewUri);
The target view code:
public partial class MyView : UserControl, INavigationAware
{
// some hidden code
public void OnNavigatedTo(NavigationContext navigationContext)
{
int hash = int.Parse(navigationContext.Parameters["hash"]);
Customer cust = (Customer)Parameters.request(hash);
}
}
That's it.
I'm not sure if this solution is the best to pass objects. I guess this maybe would be a service. Is a good way to do this or is there a better way to do it?
I posted an easier way. Mentioning it here for reference -
I would use the OnNavigatedTo and OnNavigatedFrom methods to pass on the objects using the NavigationContext.
First derive the viewmodel from INavigationAware interface -
public class MyViewModel : INavigationAware
{ ...
You can then implement OnNavigatedFrom and set the object you want to pass as navigation context as follows -
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
SharedData data = new SharedData();
...
navigationContext.NavigationService.Region.Context = data;
}
and when you want to receive the data, add the following piece of code in the second view model -
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.NavigationService.Region.Context != null)
{
if (navigationContext.NavigationService.Region.Context is SharedData)
{
SharedData data = (SharedData)navigationContext.NavigationService.Region.Context;
...
}
}
}
I just started using Prism and this is one of the first limitations I ran into. I solved it a different way. I first created a class that inherits from Uri and implements IDictionary (plus some generic methods for easier access)
public class NavigationUri : Uri, IDictionary<Type, object>
{
private IDictionary<Type, object> _internalDictionary = new Dictionary<Type, object>();
public NavigationUri(string uriString) : base(uriString, UriKind.Relative)
{
}
public NavigationUri(string uriString, UriKind uriKind) : base(uriString, uriKind)
{
}
public void Add<T>(T value)
{
Add(typeof(T), value);
}
public void Add(Type key, object value)
{
_internalDictionary.Add(key, value);
}
public bool ContainsKey<T>()
{
return ContainsKey(typeof (T));
}
public bool ContainsKey(Type key)
{
return _internalDictionary.ContainsKey(key);
}
public ICollection<Type> Keys
{
get { return _internalDictionary.Keys; }
}
public bool Remove<T>()
{
return Remove(typeof (T));
}
public bool Remove(Type key)
{
return _internalDictionary.Remove(key);
}
public bool TryGetValue<T>(out object value)
{
return TryGetValue(typeof (T), out value);
}
public bool TryGetValue(Type key, out object value)
{
return _internalDictionary.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return _internalDictionary.Values; }
}
public object this[Type key]
{
get { return _internalDictionary[key]; }
set { _internalDictionary[key] = value; }
}
public void Add(KeyValuePair<Type, object> item)
{
_internalDictionary.Add(item);
}
public void Clear()
{
_internalDictionary.Clear();
}
public bool Contains(KeyValuePair<Type, object> item)
{
return _internalDictionary.Contains(item);
}
public void CopyTo(KeyValuePair<Type, object>[] array, int arrayIndex)
{
_internalDictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _internalDictionary.Count; }
}
public bool IsReadOnly
{
get { return _internalDictionary.IsReadOnly; }
}
public bool Remove(KeyValuePair<Type, object> item)
{
return _internalDictionary.Remove(item);
}
public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
{
return _internalDictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _internalDictionary.GetEnumerator();
}
}
Then I created a class that inherits from RegionNavigationContentLoader. On GetContractFromNavigationContext I store the passed in Uri so I can access it in the CreateNewRegionItem method. In that method I check to see if the Uri is the NavigationUri and if so I loop though adding all the dependency injection overrides. I'm using Unity but I assume the code could easy be converted to another IOC container.
public class BaseRegionNavigationContentLoader : RegionNavigationContentLoader
{
private Uri _uri;
private IServiceLocator _serviceLocator;
private IUnityContainer _unityContainer;
public BaseRegionNavigationContentLoader(IServiceLocator serviceLocator, IUnityContainer unityContainer) : base(serviceLocator)
{
_serviceLocator = serviceLocator;
_unityContainer = unityContainer;
}
protected override string GetContractFromNavigationContext(NavigationContext navigationContext)
{
_uri = navigationContext.Uri;
return base.GetContractFromNavigationContext(navigationContext);
}
protected override object CreateNewRegionItem(string candidateTargetContract)
{
object instance;
try
{
var uri = _uri as NavigationUri;
if (uri == null)
{
instance = _serviceLocator.GetInstance<object>(candidateTargetContract);
}
else
{
// Create injection overrides for all the types in the uri
var depoverride = new DependencyOverrides();
foreach (var supplant in uri)
{
depoverride.Add(supplant.Key, supplant.Value);
}
instance = _unityContainer.Resolve<object>(candidateTargetContract, depoverride);
}
}
catch (ActivationException exception)
{
throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "CannotCreateNavigationTarget", new object[] { candidateTargetContract }), exception);
}
return instance;
}
}
Now in the prism Bootstrapper you need to register the BaseRegionNavigationContentLoader as IRegionNavigationContentLoader in the ConfigureServiceLocator method. Make sure you mark it as TransientLifetimeManager so it gets newed up each time. The default registration for IRegionNavigationContentLoader is container controlled which makes it act like a singleton but we need a new one each time since we need to pass the uri from one method to the next in a property.
Now I can wright code like the following and still use constructor injection.
var uri = new NavigationUri("MessageBoxView");
uri.Add(messageBoxEventArgs);
regionManager.RequestNavigate(RegionNames.MainRegion, uri);
I'm currently trying to implement StructureMap's AutoMocking functionality and I need help with getting the mocked .
I have a Test method as follows:
[Test]
public void DirctoryResult_Returns_Groups()
{
var autoMocker = new RhinoAutoMocker<GroupController>(MockMode.AAA);
GroupController controller = autoMocker.ClassUnderTest;
var directoryResult = controller.DirectoryResult("b");
var fundDirectoryViewModel = (FundDirectoryViewModel)directoryResult.ViewData.Model;
Assert.IsNotNull(fundDirectoryViewModel.Groups);
}
Currently the test is failing because fundDirectoryViewModel.Groups is null.
The real implementation of DirectoryResult is as follows:
private readonly IGroupService _groupService;
public PartialViewResult DirectoryResult(string query)
{
return PartialView(new FundDirectoryViewModel
{
Groups =_groupService.GetGroupsByQuery(query)
});
}
where _groupService.GetGroupsByQuery(query) uses an interface to IGroupRepository to read data from the database. Of course, I don't want my test to read data from the actual database, but can somebody tell me how to get mock data for it?
What do I need to do to get the AutoMocker to mock the fake data for me?
update:
for reference, this is the definition of GroupService & GroupRepository
public class GroupService : IGroupService
{
private readonly IGroupRepository _groupRepository;
public GroupService(IGroupRepository groupRepository)
{
_groupRepository = groupRepository;
}
public IList<CompanyGroupInfo> GetGroupsByQuery(string query)
{
return _groupRepository.GetGroupsByQuery(query);
}
}
public class GroupRepository : DataUniverseRepository, IGroupRepository
{
public GroupRepository(ISession session)
{
_session = session;
}
public IList<CompanyGroupInfo> GetGroupsByQuery(string query)
{
// dig into the database and return stuff with _session..
}
}
I've been informed that the question was wrong. Automocker doesn't mock data like that. It's up to me to specify the fake data with Rhino Mocks.
This works:
[Test]
public void DirctoryResult_Returns_Groups()
{
var service = autoMocker.Get<IGroupService>();
service.Expect(srv => srv.GetGroupsByQuery(Arg<string>.Is.Anything))
.Return(new List<CompanyGroupInfo>
{
new CompanyGroupInfo(),
new CompanyGroupInfo(),
new CompanyGroupInfo()
});
service.Replay();
var directoryResult = _controller.DirectoryResult("b");
var fundDirectoryViewModel = (FundDirectoryViewModel)directoryResult.ViewData.Model;
Assert.That(fundDirectoryViewModel.Groups.Count, Is.EqualTo(3));
service.AssertWasCalled(srv => srv.GetGroupsByQuery(Arg<string>.Is.Equal("b")));
}
I am developing an ASP.NET MVC project and want to use strongly-typed session objects. I have implemented the following Controller-derived class to expose this object:
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];
}
}
}
This allows me to define a session object for each controller, which is in line with the concept of controller isolation. Is there a better/more "correct" way, perhaps something that is officially supported by Microsoft?
This way other objects won't have access to this object (e.g. ActionFilter). I do it like this:
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; }
}
}
Then, I can either inject IUserDataStorage into controller's constructor, or use ServiceLocator.Current.GetInstance(typeof(IUserDataStorage<T>)) inside ActionFilter.
public class MyController: Controller
{
// automatically passed by IoC container
public MyController(IUserDataStorage<MyObject> objectData)
{
}
}
Of course for cases when all controllers need this (e.g. ICurrentUser) you may want to use property injection instead.
This might be better for what you want. I would just create an extension method that can access your session. The added benefit to the extension method is that you no longer have to inherit from a controller, or have to inject a dependency that really isn't necessary to begin with.
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();
}
}
http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (apologies for the colours on my blog was tooling around with themes and just havent fixed it yet)
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;
}
}
i came up with that a few years ago and it works fine. same basic idea as everyone else i guess, why microsoft dont just implement this as standard eludes me.
I generally use this for a session key and then explicitly add objects as needed. The reason for this is it's a clean way to do it and I find that you want to keep the number of objects in session to a minimum.
This particular approach brings together forms authentication and user session into one place so you can add objects and forget about it. The argument could be made that it is a big verbose, but it does prevent any double up and you shouldn't have too many objects in session.
The following can exist in a core library or wherever you want.
/// <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);
}
}
Then implement this in the following class namespaced to the root of your project (I usually put it in a code folder on mvc projects):
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's 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;
}
}
}
FINALLY
The big advantage of explicitly declaring what you want in session is that you can reference this absolutely anywhere in your mvc application including the views. Just reference it with:
CurrentSession.Instance.Object1
CurrentSession.Instance.CurrentUser
Again a little less generic than other approaches, but really really clear what's going on, no other rigging or dependancy injection and 100% safe to the request context.
On another note, the dicionary approaches are cool, but you still end up with strings all over the place to reference stuff. You could rig it with enums or something, but I prefer the strong typing and set and forget of the above approach.
Yes, it's years after this question was asked and there are other ways to do this... but in case anyone else shows up looking for something that combines the approaches above into an appealing one stop shop (at least one that appealed to my team and I...) Here's what we use.
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;
}
}
Then in your controllers you can do your thing with your session variables in a more strongly typed way.
// 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);
}
Hope this helps someone!