I'm trying to implement my own GenericIdentity implementation but keep receiving the following error when it attempts to load the views (I'm using asp.net MVC):
System.Runtime.Serialization.SerializationException was unhandled
by user code Message="Type is not resolved for member
'OpenIDExtendedIdentity,Training.Web, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'."
Source="WebDev.WebHost"
I've ended up with the following class:
[Serializable]
public class OpenIDExtendedIdentity : GenericIdentity {
private string _nickName;
private int _userId;
public OpenIDExtendedIdentity(String name, string nickName, int userId)
: base(name, "OpenID") {
_nickName = nickName;
_userId = userId;
}
public string NickName {
get { return _nickName; }
}
public int UserID {
get { return _userId; }
}
}
In my Global.asax I read a cookie's serialized value into a memory stream and then use that to create my OpenIDExtendedIdentity object. I ended up with this attempt at a solution after countless tries of various sorts. It works correctly up until the point where it attempts to render the views.
What I'm essentially attempting to achieve is the ability to do the following (While using the default Role manager from asp.net):
User.Identity.UserID
User.Identity.NickName
... etc.
I've listed some of the sources I've read in my attempt to get this resolved. Some people have reported a Cassini error, but it seems like others have had success implementing this type of custom functionality - thus a boggling of my mind.
http://forums.asp.net/p/32497/161775.aspx
http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html
http://social.msdn.microsoft.com/Forums/en-US/netfxremoting/thread/e6767ae2-dfbf-445b-9139-93735f1a0f72
I'm not sure if this is exactly the same issue, but I've run into the same issue when trying to create my own identity implementation.
This blog solved my problem.
It seems that the problem is there's an issue with identity serialization in Cassini, but you can get around it by deriving your class from MarshalByRefObject:
[Serializable]
public class MyUser : MarshalByRefObject, IIdentity
{
public int UserId ...
You can't inherit from GenericIdentity then, of course, but you can still implement the IIdentity interface that GenericIdentity implements, so you can use the thing in most places that expect an IIdentity at least.
It seems to be a limitation or a bug of the VisualStudio (Web Development Server), when I used the IIS Express in VS2012 or the full IIS config, the problem was fixed. Like suggested here: https://stackoverflow.com/a/1287129/926064
Solution from "baggadonuts" at This post solved my problem. Copied code below.
[Serializable]
public class StubIdentity : IIdentity, ISerializable
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (context.State == StreamingContextStates.CrossAppDomain)
{
GenericIdentity gIdent = new GenericIdentity(this.Name, this.AuthenticationType);
info.SetType(gIdent.GetType());
System.Reflection.MemberInfo[] serializableMembers;
object[] serializableValues;
serializableMembers = FormatterServices.GetSerializableMembers(gIdent.GetType());
serializableValues = FormatterServices.GetObjectData(gIdent, serializableMembers);
for (int i = 0; i < serializableMembers.Length; i++)
{
info.AddValue(serializableMembers[i].Name, serializableValues[i]);
}
}
else
{
throw new InvalidOperationException("Serialization not supported");
}
}
Related
I have spent over two weeks searching google, bing, stack overflow, and msdn docs trying to figure out how to do a proper dependency injection for a mobile app that I am developing. To be clear, I do DI every day in web apps. I do not need a crash course on what, who, and why DI is important. I know it is, and am always embracing it.
What I need to understand is how this works in a mobile app world, and in particular a UWP Template 10 Mobile app.
From my past, in a .net/Asp app I can "RegisterType(new XYZ).Singleton() blah" {please forgive syntax; just an example} in App_Start.ConfigureServices. This works almost identical in .netcore, granted some syntactic changes.
My problem is now I am trying to provide my api is going to an UWP app that needs to digest my IXYZ service. By no means do I think that they should "new" up an instance every time. There has to be a way to inject this into a container on the UWP side; and I feel I am missing something very simple in the process.
Here is the code I have:
App.xaml.cs
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// TODO: add your long-running task here
//if (args.Kind == ActivationKind.LockScreen)
//{
//}
RegisterServices();
await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));
}
public static IServiceProvider Container { get; private set; }
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IXYZ, XYZ>();
Container = services.BuildServiceProvider();
}
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
private readonly IXYZ _xyz;
public MainPageViewModel(IXYZ xyz)
{
//Stuff
_xyz= xyz;
}
}
I now get the error:
XAML MainPage...ViewModel type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface nested generic or a struct, and must have a public default constructor.
I am willing to use any brand of IoC Container, but what I need is an example of how to properly use DI for services in a UWP app. 99.9% of questions about DI is about Views (i.e. Prism?) not just a simple DI for a service (i.e. DataRepo; aka API/DataService).
Again, I feel I am missing something obvious and need a nudge in the right direction. Can somebody show me an example project, basic code, or a base flogging on how I should not be a programmer...please don't do that (I don't know if my ego could take it).
You can try to Microsoft.Hosting.Extensions just like ASP.NET, there's an implementation on Xamarin.Forms by James Montemagno, as well it can be used in UWP I have tried and it works perfectly. You have to change some parts in order to get it working.
In OnLaunched Method add Startup.Init();
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
// Tell the host configuration where to file the file (this is required for Xamarin apps)
c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });
//read in the configuration file!
c.AddJsonFile(configFile);
})
.ConfigureServices((c, x) =>
{
// Configure our local services and access the host configuration
ConfigureServices(c, x);
}).
ConfigureLogging(l => l.AddConsole(o =>
{
//setup a console logger and disable colors since they don't have any colors in VS
o.DisableColors = true;
}))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
//ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainPageViewModel>();
}
static string ExtractResource(string filename, string location)
{
var a = Assembly.GetExecutingAssembly();
using (var resFilestream = a.GetManifestResourceStream(filename))
{
if (resFilestream != null)
{
var full = Path.Combine(location, filename);
using (var stream = File.Create(full))
{
resFilestream.CopyTo(stream);
}
}
}
return Path.Combine(location, filename);
}
}
Injecting a ViewModel is possible as well which is pretty nice.
With help from #mvermef and the SO question Dependency Injection using Template 10 I found a solutions. This turned out to be a rabbit hole where at every turn I ran into an issue.
The first problem was just getting Dependency Injection to work. Once I was able to get that figured out from the sources above I was able to start injecting my services into ViewModels and setting them to the DataContext in the code behind.
Then I ran into an injection issue problem with injecting my IXYZ services into the ViewModels of UserControls.
Pages and their ViewModels worked great but I had issues with the DataContext of the UserControl not being injected with UserControl's ViewModel. They were instead getting injected by the Page's ViewModel that held it.
The final solution turned out to be making sure that the UserControl had the DataContext being set in XAML not the code behind, as we did with the Pages, and then creating a DependencyProperty in the code behind.
To show the basic solution read below.
To make it work I started with:
APP.XAML.CS
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// long-running startup tasks go here
RegisterServices();
await Task.CompletedTask;
}
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBinderService, BinderServices>();
**//ViewModels**
**////User Controls**
services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();
**//ViewModels**
**////Pages**
services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
etc....
Container = services.BuildServiceProvider();
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
**//INJECT THE VIEWMODEL FOR EACH PAGE**
**//ONLY THE PAGE NOT USERCONTROL**
if (page is CallListPage)
{
return Container.GetService<CallListPageViewModel>();
}
if (page is CallListResultPage)
{
return Container.GetService<CallListResultPageViewModel>();
}
etc...
return base.ResolveForPage(page, navigationService);
}
In the code behind for the Page
CALLLISTPAGE.XAML.CS
public CallListPage()
{
InitializeComponent();
}
CallListPageViewModel _viewModel;
public CallListPageViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
}
In your XAML add your UserControl
CALLLISTPAGE.XAML
<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>
In your UserControl make sure to add the DataContext to the XAML NOT the code behind like we did with the pages.
COMPANYCONTROL.XAML
<UserControl.DataContext>
<viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
In the UserControl Code Behind add a Dependency Property
COMPANYCONTROL.XAML.CS
public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
"Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));
public CompanyControl()
{
InitializeComponent();
}
public Company Company
{
get => (Company) GetValue(CompanyProperty);
set => SetValue(CompanyProperty, value);
}
private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CompanyControl;
var viewModel = control?.ViewModel;
if (viewModel != null)
viewModel.Company = (Company) e.NewValue;
}
In the end I am not sure if this is an elegant solution but it works.
I have created a web service with ServiceStack which returns List<SyncUserDTO>.
It has more properties, but I simplified it to one field, Timestamp.
[DataContract]
public class SyncUserDTO
{
public SyncUserDTO()
{
Timestamp = new TimestampDTO();
}
[DataMember(Order = 1)]
public TimestampDTO Timestamp { get; set; }
}
[DataContract]
public class TimestampDTO
{
[DataMember]
public bool DataValid { get; set; }
[DataMember]
public DateTime? Value { get; set; }
}
The service seems to work perfectly (with other tests), but when I create a client console application and Add Service Reference, the SyncUserDTO does not have the constructor, meaning this doesn't work:
static void SendUsersServiceReference()
{
var users = new List<SyncUserDTO>();
for (var i = 0; i < 5; i++)
{
var user = new SyncUserDTO();
user.Timestamp.Value = DateTime.Now; // NullReferenceException,
user.Timestamp.DataValid = true; // as Timestamp is null
}
}
When pressing F12 on SyncUserDTO, I can't seem to find any Constructor method in Reference.cs, explaining why the above doesn't work.
But why is the constructor not created in my proxy classes in the client application?
I need to do the "construction" myself in the client, and then it works:
var user = new SyncUserDTO() { Timestamp = new TimestampDTO() };
Of cause, I don't want the people who consumes my service to have to create this themselves. They should really note care about the underlying TimestampDTO. The constructor should do this.
Btw, I searched Google and SO for terms like "Constructor not created in proxy class with Add Service Reference" with and without "ServiceStack", no results to aid me in this quest...
Pps. Demis (ServiceStack), if you're reading this, yes SOAP is on the way out, REST is the new black - but I want to support both, which it seems like ServiceStack does, which is really great. I love ServiceStack :D
try to instanciate your property by the time you are going to access it, I know that´s a workaround but it could be convenient in your scenario.
private TimestampDTO _timestamp;
public TimestampDTO Timestamp
{
get
{
if(_timestamp==null) _timestamp=new TimestampDTO();
return _timestamp;
}
set
{
_Timestamp=value;
}
}
This is my solution (for now):
I created a new service method in my service, where the client gets a new UserDTO complete with all fields. This way, the constructor is run on the server. I bet I have quite a performance hit this way, but it doesn't matter that much (now...).
Service DTO's:
[DataContract]
public class ReturnNewEmptyUser : IReturn<ReturnNewEmptyUserResponse> {}
[DataContract]
public class ReturnNewEmptyUserResponse
{
[DataMember]
public SyncUserDTO User { get; set; }
}
The Service:
public class SyncService : Service
{
public ReturnNewEmptyUserResponse Any(ReturnNewEmptyUser request)
{
var user = new ReturnNewEmptyUserResponse { User = new SyncUserDTO() };
return user;
}
}
On the client:
static void SendUsersServiceReference()
{
var webservice = new ServiceReference1.SyncReplyClient();
var users = new List<User>();
for (var i = 0; i < 5; i++)
{
var userResponse = webservice.ReturnNewEmptyUser(new ReturnNewEmptyUser());
var user = userResponse.User;
user.Timestamp.Value = DateTime.Now;
user.Timestamp.DataValid = true;
// Continue with field population...
users.Add(user);
}
// Send users with webservice method
// ...
}
We're wondering if it is a bad way to expose the fields this way. It is nice, because the client can use autocomplete and know exactly the types used - but is it better to force the client to create an XML/JSON in a specific format.
This should be in another question - this question I guess has been answered: Add service reference/proxy classes does not contain methods (incl. constructors for types), only types. If you really need the constructor, have it run and then exposed on the server and then consume it from the client. Like a factory-thing, as Adam wrote here: Class constructor (from C# web service) won't auto-implement properties in C# MVC
Btw - is there any security issues with this design? User is logged in via url-credentials (should probably be header authentication), only a few systems has access to it.
A proxy class does not keep implementation details, like a constructor. It is just a DTO. This can only be done if you share the classes, through a shared project.
Think about that servicestack is just telling the client which properties it needs, and their type.. the implementation is up to the client.
When I run following code:
public ActionResult Complete()
{
try
{
VeriTabanDataContext db = new VeriTabanDataContext();
db.Persons.InsertOnSubmit(_person);
db.SubmitChanges();
return View(_person);
}
catch (Exception ex)
{
return RedirectToAction("Error", ex);
}
}
I'm getting following Exception, on SubmitChanges();
"An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."
Here "_person" object is taken from Session and is a good standing. Note: _person is a result of multistep wizard and this is the place where I add new Person object to the DB.
My Person table has 9 relations and it's not ok for me to add version column for each of them as is suggested by some geeks around
I've investigated this problem a lot and spend 2 days on it and still couldn't solve it. Some of the workarounds that other suggest don't solve my problem, and others seem to be just dirty workaround. Do you experts have a good solution for this problem, considering that Person class has many relations and also it isn't ok to add a column to the tables.
I also want to note that I've tried to use 'db.Persons.Attach(_person) ' and setting db.DeferredLoadingEnabled = false; THis time I'm not getting any Exception but data is NOT saved to DB
I create a class called applicationController which derives from Controller. Then i make all of my controller classes derive from this. The applicationController class has a constrcutor which creates a new instance of my repository (or datacontext in your instance) which is used throughout the application:
public class ApplicationController : Controller
{
private VeriTabanDataContext _datacontext;
public ApplicationController() : this(new VeriTabanDataContext())
{
}
public ApplicationController(VeriTabanDataContext datacontext)
{
_datacontext = datacontext;
}
Public VeriTabanDataContext DataContext
{
get { return _datacontext; }
}
}
Then you can use this in all of your controllers
public class MyController : ApplicationController
{
public ActionResult Complete()
{
DataContext.Persons.InsertOnSubmit(_person);
DataContext.SubmitChanges();
return View(_person);
}
}
Not on my PC with VS installed at the moment so not tested this code....
Hope this resolves the issue -Mark
Can you do the following:
var foundPerson = db.Person.FirstOrDefault( p => p.Id == _person.Id);
if(foundPerson == null)
{
db.InsertOnSubmit(_person);
}else{
Mapper.Map(_person,foundPerson);
}
db.SubmitChanges();
return View(_person);
Where I have used AutoMapper to map from one entity to another. To do this add the reference to AutoMapper to your project and in your start up code for the application you will need to configure your mappings, for the above to work you would need:
Mapper.CreateMap<Person, Person>().ForMember(src => src.Id, opt => opt.Ignore());
I have an ASP.NET MVC app which depends on a lot of settings (name-value pairs), I am planning to store this information in a database table called SiteSettings. Is there an easy way in which I can get these settings using NHibernate. And what are the best practices when saving settings for a web application. And by settings I mean the settings which control the flow of processes in the web application and which are governed by business rules. These are not the typical connection string kind of settings. I was unable to get much information on the web on this topic. Maybe I am not searching on the right keywords, Any help will be greatly appreciated.
I can't answer in the context of nhibernate (which I'm not using) or best practices (I came up with this on my own recently). However, it works well for me, and will probably work for you.
I have a table (Biz_Config) in the database to store business preferences. (I've created a web.config section for what I call IT preferences.)
I have a class that is in charge of managing the biz preferences. The constructor grabs the entire table (one row per setting) and copies these into a dictionary, and it has methods to access (such as bizconfig.get("key")) and update this dictionary, also updating the table at the same time. It also has a few shortcut properties for specific dictionary values, especially where the value has to be cast (I have a few important numbers). It works quite well.
In order to be more efficient and not instantiate it every time I need a setting, and also to access it easily from my controllers and views, I created a static class, Globals, that is in charge of getting things out of the session or application variables. For the biz config object, it checks the application variable and, if null, creates a new one. Otherwise it just returns it. Globals is part of my helpers namespace, which is included in my web.config to be available to my views. So I can easily call:
<% Globals.Biz_Config.Get("key") %>
I hope this helps. If you'd like code, I can dig that up for you.
James
If you have a set of key/value pairs, you probably want to use a <map>. See the official NHibernate documentation or Ayende's post about 'NHibernate Mapping - <map/>'.
I have come up with a solution which is quite similar to the one suggested by James. I have an SiteSettingsService class which manages the settings for the whole site, it has a simple dependency on an interface called ISiteServiceRepository. This might not be the most elegant solution, But it is working perfectly for me. I have also configured the SiteSettingsService class as a Singleton using StructureMap. So, it saves me unnecessary instantiantion every time I need any settings.
//ISiteServiceRepository, an implementation of this uses NHibernate to do just two things
//i)Get all the settings, ii)Persist all the settings
using System.Collections.Generic;
using Cosmicvent.Mcwa.Core.Domain.Model;
namespace Cosmicvent.Mcwa.Core.Domain {
public interface ISiteServiceRepository {
IList<Setting> GetSettings();
void PersistSettings(IDictionary<string, string> settings);
}
}
//The main SiteSettingsService class depends on the ISiteServiceRepository
using System;
using System.Collections.Generic;
using Cosmicvent.Mcwa.Core.Domain;
using Cosmicvent.Mcwa.Core.Domain.Model;
namespace Cosmicvent.Mcwa.Core.Services {
public class SiteSettingsService : ISiteSettingsService {
private readonly ISiteServiceRepository _siteServiceRepository;
private IDictionary<string, string> _settings;
public SiteSettingsService(ISiteServiceRepository siteServiceRepository) {
_siteServiceRepository = siteServiceRepository;
//Fill up the settings
HydrateSettings();
}
public int ActiveDegreeId {
get {
return int.Parse(GetValue("Active_Degree_Id"));
}
}
public string SiteTitle {
get { return GetValue("Site_Title"); }
}
public decimal CounsellingFee {
get { return decimal.Parse(GetValue("Counselling_Fee")); }
}
public decimal TuitionFee {
get { return decimal.Parse(GetValue("Tuition_Fee")); }
}
public decimal RegistrationFee {
get { return decimal.Parse(GetValue("Registration_Fee")); }
}
public void UpdateSetting(string setting, string value) {
if (!string.IsNullOrEmpty(setting) && !string.IsNullOrEmpty(value)) {
SetValue(setting, value);
PersistSettings();
}
}
//Helper methods
private void HydrateSettings() {
_settings = new Dictionary<string, string>();
IList<Setting> siteRepoSettings = _siteServiceRepository.GetSettings();
if (siteRepoSettings == null) {
throw new ArgumentException("Site Settings Repository returned a null dictionary");
}
foreach (Setting setting in siteRepoSettings) {
_settings.Add(setting.Name.ToUpper(), setting.Value);
}
}
private string GetValue(string key) {
key = key.ToUpper();
if (_settings == null) {
throw new NullReferenceException("The Site Settings object is Null");
}
if (!_settings.ContainsKey(key)) {
throw new KeyNotFoundException(string.Format("The site setting {0} was not found", key));
}
return _settings[key];
}
private void SetValue(string key, string value) {
key = key.ToUpper();
if (_settings == null) {
throw new NullReferenceException("The Site Settings object is Null");
}
if (!_settings.ContainsKey(key)) {
throw new KeyNotFoundException(string.Format("The site setting {0} was not found", key));
}
_settings[key] = value;
}
private void PersistSettings() {
_siteServiceRepository.PersistSettings(_settings);
}
}
}
Hope this helps future developers facing similar problems. Any suggestions for improving this are more than welcome.
I am writing a web application that will allow a user to browse to multiple web pages within the website making certain requests. All information that the user inputs will be stored in an object that I created. The problem is that I need this object to be accessed from any part of the website and I don't really know the best way to accomplish this. I know that one solution is to use session variables but I don't know how to use them in asp .net MVC. And where would I declare a session variable? Is there any other way?
I would think you'll want to think about if things really belong in a session state. This is something I find myself doing every now and then and it's a nice strongly typed approach to the whole thing but you should be careful when putting things in the session context. Not everything should be there just because it belongs to some user.
in global.asax hook the OnSessionStart event
void OnSessionStart(...)
{
HttpContext.Current.Session.Add("__MySessionObject", new MySessionObject());
}
From anywhere in code where the HttpContext.Current property != null you can retrive that object. I do this with an extension method.
public static MySessionObject GetMySessionObject(this HttpContext current)
{
return current != null ? (MySessionObject)current.Session["__MySessionObject"] : null;
}
This way you can in code
void OnLoad(...)
{
var sessionObj = HttpContext.Current.GetMySessionObject();
// do something with 'sessionObj'
}
The answer here is correct, I however struggled to implement it in an ASP.NET MVC 3 app. I wanted to access a Session object in a controller and couldn't figure out why I kept on getting a "Instance not set to an instance of an Object error". What I noticed is that in a controller when I tried to access the session by doing the following, I kept on getting that error. This is due to the fact that this.HttpContext is part of the Controller object.
this.Session["blah"]
// or
this.HttpContext.Session["blah"]
However, what I wanted was the HttpContext that's part of the System.Web namespace because this is the one the Answer above suggests to use in Global.asax.cs. So I had to explicitly do the following:
System.Web.HttpContext.Current.Session["blah"]
this helped me, not sure if I did anything that isn't M.O. around here, but I hope it helps someone!
Because I dislike seeing "HTTPContext.Current.Session" about the place, I use a singleton pattern to access session variables, it gives you an easy to access strongly typed bag of data.
[Serializable]
public sealed class SessionSingleton
{
#region Singleton
private const string SESSION_SINGLETON_NAME = "Singleton_502E69E5-668B-E011-951F-00155DF26207";
private SessionSingleton()
{
}
public static SessionSingleton Current
{
get
{
if ( HttpContext.Current.Session[SESSION_SINGLETON_NAME] == null )
{
HttpContext.Current.Session[SESSION_SINGLETON_NAME] = new SessionSingleton();
}
return HttpContext.Current.Session[SESSION_SINGLETON_NAME] as SessionSingleton;
}
}
#endregion
public string SessionVariable { get; set; }
public string SessionVariable2 { get; set; }
// ...
then you can access your data from anywhere:
SessionSingleton.Current.SessionVariable = "Hello, World!";
Well, IMHO..
never reference a Session inside your view/master page
minimize your useage of Session. MVC provides TempData obj for this, which is basically a Session that lives for a single trip to the server.
With regards to #1, I have a strongly typed Master View which has a property to access whatever the Session object represents....in my instance the stongly typed Master View is generic which gives me some flexibility with regards to strongly typed View Pages
ViewMasterPage<AdminViewModel>
AdminViewModel
{
SomeImportantObjectThatWasInSession ImportantObject
}
AdminViewModel<TModel> : AdminViewModel where TModel : class
{
TModel Content
}
and then...
ViewPage<AdminViewModel<U>>
If you are using asp.net mvc, here is a simple way to access the session.
From a Controller:
{Controller}.ControllerContext.HttpContext.Session["{name}"]
From a View:
<%=Session["{name}"] %>
This is definitely not the best way to access your session variables, but it is a direct route. So use it with caution (preferably during rapid prototyping), and use a Wrapper/Container and OnSessionStart when it becomes appropriate.
HTH
Although I don't know about asp.net mvc, but this is what we should do in a normal .net website. It should work for asp.net mvc also.
YourSessionClass obj=Session["key"] as YourSessionClass;
if(obj==null){
obj=new YourSessionClass();
Session["key"]=obj;
}
You would put this inside a method for easy access.
HTH
There are 3 ways to do it.
You can directly access HttpContext.Current.Session
You can Mock HttpContextBase
Create a extension method for HttpContextBase
I prefer 3rd way.This link is good reference.
Get/Set HttpContext Session Methods in BaseController vs Mocking HttpContextBase to create Get/Set methods
My way of accessing sessions is to write a helper class which encapsulates the various field names and their types. I hope this example helps:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.SessionState;
namespace dmkp
{
/// <summary>
/// Encapsulates the session state
/// </summary>
public sealed class LoginInfo
{
private HttpSessionState _session;
public LoginInfo(HttpSessionState session)
{
this._session = session;
}
public string Username
{
get { return (this._session["Username"] ?? string.Empty).ToString(); }
set { this._session["Username"] = value; }
}
public string FullName
{
get { return (this._session["FullName"] ?? string.Empty).ToString(); }
set { this._session["FullName"] = value; }
}
public int ID
{
get { return Convert.ToInt32((this._session["UID"] ?? -1)); }
set { this._session["UID"] = value; }
}
public UserAccess AccessLevel
{
get { return (UserAccess)(this._session["AccessLevel"]); }
set { this._session["AccessLevel"] = value; }
}
}
}
Great answers from the guys but I would caution you against always relying on the Session. It is quick and easy to do so, and of course would work but would not be great in all cicrumstances.
For example if you run into a scenario where your hosting doesn't allow session use, or if you are on a web farm, or in the example of a shared SharePoint application.
If you wanted a different solution you could look at using an IOC Container such as Castle Windsor, creating a provider class as a wrapper and then keeping one instance of your class using the per request or session lifestyle depending on your requirements.
The IOC would ensure that the same instance is returned each time.
More complicated yes, if you need a simple solution just use the session.
Here are some implementation examples below out of interest.
Using this method you could create a provider class along the lines of:
public class CustomClassProvider : ICustomClassProvider
{
public CustomClassProvider(CustomClass customClass)
{
CustomClass = customClass;
}
public string CustomClass { get; private set; }
}
And register it something like:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ICustomClassProvider>().UsingFactoryMethod(
() => new CustomClassProvider(new CustomClass())).LifestylePerWebRequest());
}
You can use ViewModelBase as base class for all models , this class will take care of pulling data from session
class ViewModelBase
{
public User CurrentUser
{
get { return System.Web.HttpContext.Current.Session["user"] as User };
set
{
System.Web.HttpContext.Current.Session["user"]=value;
}
}
}
You can write a extention method on HttpContextBase to deal with session data
T FromSession<T>(this HttpContextBase context ,string key,Action<T> getFromSource=null)
{
if(context.Session[key]!=null)
{
return (T) context.Session[key];
}
else if(getFromSource!=null)
{
var value = getFromSource();
context.Session[key]=value;
return value;
}
else
return null;
}
Use this like below in controller
User userData = HttpContext.FromSession<User>("userdata",()=> { return user object from service/db });
The second argument is optional it will be used fill session data for that key when value is not present in session.