I don't think the title is the best description for my problem but, the problem is as following:
I have a base class like:
public class BasePage : System.Web.UI.Page
{
public static ProjectDTO Project { get; set; }
// some other code
// Setting the project once per session.
public void SetProject()
{
Project = (ProjectDTO)HttpContext.Current.Session["Project"];
SiteMaster masterPage = Master as SiteMaster;
masterPage.Project = Project;
}
}
And then i have an aspx page like:
public partial class SomePage: BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
//callling the base method to set the project
SetProject();
}
//some db method which requires the Project.Id property.
GetSomeInfo(Project.Id)
{
//irelevant code
}
}
Everything works fine, but, when i have 2 users online at the same time, they will obviously call the SetProject method, and if one of them uses GetSomeInfo method, the Project.Id property will be the latest one set, not the one from the current user session.
Can anyone help me fix this problem?
PS:
A dirty fix would be to read the session variable every time i have to use the Project, won't be a problem, but my page has like 10 methods requiring that property (a lot of redundant code)
A secondary fix would be to declare a public property on SomePage and use that, but then, i would find Project from the BasePage redundant and i don't like that approach because there are quite a few pages requiring the Project property and using the BasePage (filters, searches, etc on objects belonging to that Project)
EDIT After some more testing, 2 different users at the same time, and after Glubus comments, the problem happens only when the page is loading for one of the users (the user which is not loading anything will get wrong results from the database while the other user is loading a page.)
EDIT2 The workflow is as following:
User goes to home page where a list of projects are available (read from db) -> clicks on one project (when the user clicks the project i'm setting a session variable to be read later). Then the user can see/use other data related to this project.
EDIT3
When a user click on a project, they will navigate to a Dashboard page. Here, on the Page_Load even i'm setting the session variable, like:
public partial class Dashboard : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
int projectId;
int.TryParse(Request.QueryString["projectId"], out projectId);
if (projectId > 0)
{
Session["Project"] = ProjectSvc.GetProjectById(projectId);
SetProject();
}
}
}
ProjectDTO class:
public class ProjectDTO
{
public int idProject { get; set; }
public string Title { get; set; }
public string Users { get; set; }
public string Description { get; set; }
}
I'm setting the Project to the Site Master because i have a label which requires to be seen on the screen with the Project Name and description.
In order to access the Project from the current Session from all places, including WebMethods, declare a static readonly property in BasePage that directly accesses the Session.
public class BasePage : System.Web.UI.Page
{
public static ProjectDTO Project
{
get {return (ProjectDTO)HttpContext.Current.Session["Project"];}
}
// some other code
// Passing the Project to the Master.
public void SetProject()
{
SiteMaster masterPage = Master as SiteMaster;
masterPage.Project = Project;
}
}
You can get rid of the SetProject() call altogether if you also use BasePage.Project to access the current project from the Site Master.
During testing, make sure that you are not using the same browser instance for testing. When session managements happens via cookies and you have two users logged in into the same browser instance, they will actually use the same ASP.net session.
Perhaps a worst case scenario solution - but....
If you are really intent on making the Project variable accessible to all subsequent users, you maybe need to move away from sessions and just declare it as a static?
This way it will be shared application wide, retaining the last assigned value.
Related
In my MainPage.xaml I've got a SplitView that loads many pages inside a frame created in it's SlplitView.Content.
I've got data in a MainPage's variable that needs to be sent to every page that loads in my SplitView content's frame according to the ListBoxItem clicked.
Also in the current page I may have to update the MainPage's variable before a new page is loaded.
How can I do this? Is there a way to declare a global variable? Can I transport that information from a page to another updating it's value on the parent page?
I think you can declare a public static variable in App Class in App.xaml.cs, and use it in any pages in the app.
In App.xaml.cs:
sealed partial class App : Application
{
...
public static string MyTestVar { get; set; }
...
}
In MainPage.xaml.cs:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
App.MyTestVar = "world";
}
}
And for some other cases like implementing a setting page, you can check Store and retrieve settings and other app data.
Complementing the last answer I solved my problem by declaring an internal static variable in my "App.xaml.cs".
internal static string foo = "";
Then to access it I used:
App.foo = "my string";
There is also a more elegant way to preserve and restore page data when leaving it (which is what I needed) as follows: https://msdn.microsoft.com/pt-br/library/windows/apps/ff967548%28v=vs.105%29.aspx
I am using MVC4 and what I want to be able to do is to check the status of a user's friend to see whether they are online or not for chat purposes. I have explored the WebSecurity and MEmebership classes but cannot see any functionality within these. Do I have to do something with getting the sessions from the IIS server?
Thanks in advance
There is MembershipUser.IsOnline Property but if this is not useful you can use signlR to check the current status and may be store it somewhere.
Else you can do an ajax call every few minutes to check user status and also store it somewhere.
One possible solution I am suggesting you is to create a global filter that populates LastActionTime field of every user and then use this field to determine whether the user is online or not:
First create a new property of type DateTime? called LastActionTime in your User class in case you are using Code-First approach. In case you are using Database-First approach just add nullable DateTime column in you Users table and update your local model.
public class User
{
// ...
public DateTime? LastActionTime { get; set; }
// ...
}
Then create a new class called LogUserActionTimeFilter and implement it's OnActionExecuting and OnActionExecuted method. If you have any services or DbContexts, initialize them in the constructor of the class.
public class LogUserActionTimeFilter : IActionFilter
{
private readonly IUserAuthService userAuthService;
public LogAdminRequestFilter()
: this(new UserAuthService())
{
}
public LogAdminRequestFilter(IUserAuthService userAuthService)
{
this.userAuthService = userAuthService;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
this.userAuthService.SaveCurrentTimeAsLastActionTime(
filterContext.HttpContext.User.Identity.Name);
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
The SaveCurrentTimeAsLastActionTime method just sets LastActionTime to DateTime.Now of the given user and saves changes. It should be easy to implement.
When done with the filter you can apply it to specific actions, controllers (e.g. in the chat only) or globaly:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LogUserActionTimeFilter());
}
}
When you need to determine if a user is online or not just check its LastActionTime column/property and if it is less than 5 minutes (for example) before DateTime.Now then the user may be considered online.
In a ASP.NET MVC 5 web site I have a GridView using the devexpress component binding using the LINQ method.
EF generated a partial class to map a table that i use to display in that gridview.
In this partial class generated by the EF i have a ID_Status property wich has a corresponding description in other table. I made another partial class to deal with this custom Property and it works ok, except when i try to make a 'Sort' operation clicking on the header of this column.
The partial class generated by the EF.
[Table("Test")]
public partial class Test
{
[Key]
public long ID_Test { get; set; }
public long ID_TestStatus { get; set; }
//other properties
}
My Custom partial class:
public partial class Test
{
private static readonly TestRepository _testRepository;
static TestRepository()
{
_testRepository= new TestRepository();
}
public string StatusDescription
{
get { return _testRepository.GetStatusDescriptionById(ID_TestStatus); }
}
}
When i try to Sort using another column it works fine, but when i try to Sort using the custom property Column all the grid cell values gets empty, without any value.
Any suggestion?
It's not a very good idea to have data access code inside an entity. One reason is that it makes it very hard to write unit test. Another reason is that it is very likely to give rise to the n + 1 anti pattern. In your case, it does: one (1) query to get the Tests, then each Test (n) sends a separate query to the database to get its StatusDescription.
The way you implemented it also raises some eyebrows, because
_testRepository is static, which meas there is probable some context instance living for the entire lifecycle of the application - unless GetStatusDescriptionById creates a new context for each call, but that wouldn't be a good idea either.
The GetStatusDescriptionById call is made each time the property is accessed. In a web application this may not be a big problem because the objects are newly created each time they are requested anyway, but in other environments this could be highly inefficient.
A better approach would be to fetch the Testss with their Status included:
context.Tests.Include(t => t.TestStatus)
and have an unmapped property like
public string StatusDescription
{
get { return TestStatus== null ? string.Empty : TestStatus.Description; }
}
better still (in my opinion) would be not to show Test objects directly, but TestDto objects like
public class TestDto
{
public string StatusDescription { get; set; }
//other properties that match Test's properties
}
and use a tool like AutoMapper to map a collection of Tests to TestDtos. If Test has a property Status and TestStatus has a property Description, AutoMapper will be able to flatten that into StatusDescription automatically.
Both this StatusDescription property and the Dto appraoch set the state of a Test(Dto) object once. I don't think any grid component can mess with that.
When a user signs in to my website, I want cache some data like email, confirmation status, mobile confirmation status, etc. Because I don't want fetch this data in each page request. The requirement is that the user must confirm email and mobile before do anything.
I am using code like this:
public static class CachedData
{
public static bool IsEmailConfirmed
{
get
{
if (HttpContext.Current.Session["IsEmailConfirmed"] == null)
Initialize();
return Convert.ToBoolean(HttpContext.Current.Session["IsEmailConfirmed"]);
}
set
{
HttpContext.Current.Session["IsEmailConfirmed"] = value;
}
}
public static bool IsMobileConfirmed
{
get
{
if (HttpContext.Current.Session["IsMobileConfirmed"] == null)
Initialize();
return Convert.ToBoolean(HttpContext.Current.Session["IsMobileConfirmed"]);
}
set
{
HttpContext.Current.Session["IsMobileConfirmed"] = value;
}
}
public static void Initialize()
{
UserAccount currentUser = UserAccount.GetUser();
if (currentUser == null)
return;
IsEmailConfirmed = currentUser.EmailConfirmed;
IsMobileConfirmed = currentUser.MobileConfirmed;
}
}
I have PageBase class that all page classes drive from it. I am using class CachedData in PageBase class:
public class PageBase : Page
{
protected override void OnInit(EventArgs e)
{
if (authentication.Required && User.Identity.IsAuthenticated && !IsPostBack)
{
if (CachedData.HasProfile && (!CachedData.IsEmailConfirmed || !CachedData.IsMobileConfirmed) && !Request.Url.AbsolutePath.ToLower().EndsWith("settings.aspx"))
Response.Redirect("/settings-page", true);
}
}
}
May be it is strange, but this code, sometimes work wrong and redirect to setting page for user confirmed email and mobile.
Is there any better solution.
I think, if this is your logic, you should create an object UserInfo. Something like this:
public class UserInfo
{
public string Name {get; set; }
public bool IsEmailConfirmed {get; set; }
public bool IsMobileConfirmed {get; set; }
....
}
Then set this object into session. Now! when any operation on user record are performed in your BLL, you should re-populate new Instance of UserInfo and replace old one in the session. This way your user info will be up to day and will always work.
But your problem may coming from the fact that you use a web farm and your sessions are not synchronized. You need to use a sticky session so each request from the unique user is processed on the same server. Right now there is thing called App Fabric. It is caching on steroids. It can find an item in cache on another server.
You should not store different fields of your object in the different session names.
If you do need to use sessions, you can store the whole user object in your session.
The choice where to store the data depands on the requirements (including how critical are the data and the requirements for such the things as IIS reset) and what and why you really have to store.
Depending on the answers you could store your data either in a session or in a viewstate or in cache or in application.
You can also look at the cache because it provides some nice features like automatic update, triggering, etc.
I have a button (view state enabled) in Master web page and set it to visible=false in one of the child web pages. If a second child page is opened, the button state (visible=false) is not persisting.
It seems viewstate is only valid for one page and is not transferred to other web pages. Is there some kind of trick to make viewstate global for all web pages?
No, viewstate is page specific. You will need to use something like a session variable or a querystring parameter to pass your state between pages.
No, You cannot make view state global, they are page specific. I would suggest to use cookies if you really want to make it client side otherwise you can use session.
If you need to store on a "global" level, you should be using the Application State. You could also use Cache Object.
You may be wanting to pass values from one page to another, you can achieve this by using the Context object in combination with the Server.Transfer.
1) You need a public property on the source page returning the Value to pass
namespace SomeNameSpace
{
public partial class SourcePage: System.Web.UI.Page
{
public string ValueToPass
{
get
{
if (Context.Items["ValueToPass"] == null)
Context.Items["ValueToPass"] = string.Empty;
return (string)Context.Items["ValueToPass"];
}
set
{
Context.Items["ValueToPass"] = value;
}
}
........
}
}
2) Do a Server.Transfer(DestinationPage.aspx)
3) In the Page_Load event of the destination page
namespace SomeNameSpace
{
public partial class SourcePage: System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var value = this.Context.Items["ValueToPass"];
}
}
}
Hope this helps