Passing C# custom objects in Session between asp.net web pages - asp.net

I have a frustrating issue when trying to pass a custom object from 1 page of a web site to another. When I try to use the object from session on the page 2 it is null. I believe that my syntax is correct as when I take the code out of my larger solution and run it on it's own it works fine.
I am wondering if there are any settings in visual studio, aspx files, project properties that may be set on my project that may be causing this session object not to work?
Simplied syntax that I am using is;
on page1
Person p = new Person;
p.name = "john";
p.secondName = "doe";
Session["person"] = p.
Response.Redirect("Page2.aspx")
Page 2 on page load method
Person p = (person)Session["person"]
textbox1.Text = p.name;
textbox2.Text = p.Secondname;
As I said this code works fine on it's own but not as part of my larger works project. Any ideas on why this may not be working would be greatly appreciated

There are three possible options.
First one is that inside your larger project session state is disabled. Please visit Turn Off ASP Session State in Active Server Pages and IIS for more details.
Second option would be that exception occurs somewhere inside your application (separate thread) resulting in application restart and session state loose. You can check this by hooking to Application error inside Global.asax like this:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
}
The last option would be that session["person"] value is changed in other module or page.

Try code with,
Response.Redirect("Page2.aspx", false);

See as per your code if there is database connected then i suggest you to do whatever you want to on page1 and save it to db in single or multiple tables, depending upon the requirements. Now paas the id to page2 as querystring. Format looks like
Response.Redirect("~/page2.aspx?id=67");
where is the dynamic unique id from database. Now on page 2.aspx query on the id you have passed.

Related

Concurrency exception with Devexpress ASPXGridView and EntityFramework 4.3.1

My Issue
I have a simple WebForms project for testing concurrency.
I am using:
1. Entity Framework 4.3.1 code first approach.
2. DevExpress ASP.net controls to visualize my data. Specifically an ASPXGridView control.
3. MySQL as database backend.
Now I am having an issue with the concurrency check.
Even if I am the only user editing the data, if I edit the same record twice using the DevExpress ASPXGridView I get a concurrency exception!
The exception I get is :
System.Data.OptimisticConcurrencyException
My Setup
** Simplified here for brevity
My code first entity is defined something like this:
public class Country
{
//Some constructors here
[Required, ConcurrencyCheck]
public virtual string LastUpdate { get; set; }
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int CountryID { get; set; }
//Various other data fields here
}
You can see I have added a single field called LastUpdate which the concurrecny check is being tested against due to setting the [ConcurrencyCheck] attribute.
On my web page with the DevExpress ASPXGridView I am using an EntityDataSource to make the binding between the grid view and the entity framework. The grid view is using a popup editor. I have the following events hooked:
protected void Page_Load(object sender, EventArgs e)
{
//Hook entity datasource to grid view
dbgCountries.DataSource = CountriesEntityDataSource;
dbgCountries.DataBind();
}
protected void CountriesEntityDataSource_ContextCreating(object sender, EntityDataSourceContextCreatingEventArgs e)
{
//Create and hook my DBContext class to the entity
//datasources ObjectContext property.
var context = new MyDBContextClass();
e.Context = ((IObjectContextAdapter)context ).ObjectContext;
}
protected void dbgCountries_InitNewRow(object sender, DevExpress.Web.Data.ASPxDataInitNewRowEventArgs e)
{
//I create a new MyDBContextClass here and use it
//to get the next free id for the new record
}
protected void dbgCountries_CustomErrorText(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewCustomErrorTextEventArgs e)
{
//My code to catch the System.Data.OptimisticConcurrencyException
//excpetion is in here.
//I try to rtefresh the entity here to get the latest data from
//database but I get an exception saying the entity is not being
//tracked
}
protected void dbgCountries_RowValidating(object sender, DevExpress.Web.Data.ASPxDataValidationEventArgs e)
{
//Basic validation of record update in here
}
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//I set the LastUpdate field (my concurrency field)
//to the current time here
}
I also have some button events hooked to test a direct concurrecny test.
eg
- Get Entity
- Update Entity
- Update DB directly with sql
- Save Entity
- Get concurrency exception as expected
eg
- Get Entity
- Update Entity
- Save Entity
- No issue.
- Get Entity again.
- Update Entity again.
- Save Entity again.
- No issue.
These buttons work as expected. Only ther grid updates seem to have an issue.
Maybe it is because the grid needs to use ObjectContect and my entity framework classes are using DBContext?
My Attempted Fixes
I have scoured the internet trying to find a solution. Checked DevExpress forums, checked other posts here on StackOverflow, various posts on the internet, Microsoft MSDN articles on concurrency and I just can not work this out.
None of the posts were as 'simple' as mine. They all had other data involved. eg a master/detail
relashionship. custom editors. etc. I am using all inbuild DevExpress controls and just display a
single grid view on my db table / entity.
Some posts suggest refreshing the entities. I tried this but get an exception saying the entity is
not being tracked in the object state manager.
I tried refreshing the entity framework by destroying and recreating my object context / db
context but somehow I still get the concurrency issue.
I tried refreshing using the DBContexct and also the ObjectContext. Neither worked.
(objContext.Refresh(RefreshMode.StoreWins, entity). I either get an exception as stated]
earlier sayign the entity is not being tracked, or if I tell it to refresh only non modifed
entities then nothing happens at all (no refresh, no excpetion)
I tried making my DBContext global but this is no good as WebForms appears to want to recreate its
entire state and rehook its grids data context etc after every web refresh. (page loads, user
clicks edit, user clicks ok to update)
Now all of these solutions seem to takle what to do AFTER the concurrency exception. Seeing that I should not even be getting the exception in the first place I guess they would not help.
Suggestions
Do any of you have suggestions on how to make this work?
Do I have to maybe force the entity framework to refresh manually after posting data from the grid?
(I only just thought of this one now)
It seems a pretty simple setup I have. Maybe I am missing something very obvious. I have not worked with WebForms or EntityFramework much yet so there could be simple (and perhaps obvious) solutions I am missing?
Any help appreciated.
Thanks
Peter Mayes
I have managed to solve my issue.
It may not be the most correct solution but it is working and any progress at this point is much appreciated.
Approach
I tried refreshing Entity Framework after posting data in the ASPXGridView.
Many attempts. None worked.
I tried using a TimeStamp attribute on my Country entity but this did
not seem to map very well to MySQL. (However I might try this again now
I have solved the issue)
I then thought maybe my DevArt MySQL dot connector and MySQL was at fault.
So I switched over to MSSQL and its standard connector. This showed the same
issue am having with MySQL & co.
Finally I was mucking around with various attempts and noticed that if I go to a different
page on my web site, then back again the issue does not occur.
E.g.:
Edit Country and Save. No Issues.
Switch to other site page.
Switch back to Countries.
Edit Country and Save. No Issues.
The difference being, if I do not switch pages the second edit creates a concurrency exception.
With some more testing with co-workers I got a hunch that maybe the viewstate for the
entity datasource was not being refreshed after a post/update on the ASPGridView.
So what I did was:
> Set EntityDataSource.StoreOriginalValuesInViewState = FALSE
This stopped all concurrency working as no old/pre edit values were being stored and so
were not available for the concurrecny check.
I then thought I would force the oldvalues to be what was in the editor before I edited.
I was using ASPXGridView.RowUpdating to do this.
I thought thats ok, I can just use the OldValues passed to ASPXGridView.RowUpdating to
ensure entity framework is good to go.
Doing this I found some very odd behaviour...
If I:
- open edit form in browser A
- open edit form in browser B
- save changes in browser B (DB updates with new values here)
- save changes in browser A (DB updated here too. but should have been a
concurrency exception!)
The reason post from A was succeeding was that OldValues on A had been magically updated
to the new values B had posted!
Remember the edit form on A was open the whole time so it should not have updated its OldValues underneath. I have no idea why this occurs. Very odd.
Maybe OldValues are not retrieved by the DevExpress ASPXGridView until the
edit form is closing?
Anyway, then I thought. Fine, I will just work around that oddity. So to do so I created
a static member on the web page to store a copy of my Country entity.
When the user goes to open the editor I get the current values and store them.
Then when ASPXGridView.RowUpdating fires I push the stored old values back into the
OldValues data. (I also update my timstamp/concurrency field here too in the NewValues
data)
With this approach my concurrency now works. Hurah!.
I can edit locally as much as I want and get no conflicts. If I edit in two browsers at once the second one to post raises concurrency exception.
I can also switch between MySQL and MSSQL and both work correctly now.
Solution Summary
Set EntityDataSource.StoreOriginalValuesInViewState = FALSE. (I did this in the designer.)
Create private member to hold pre-edit country values
private static Country editCountry = null;
Hook StartRowEditing on ASPXGridView. In here I get the current country values and store them as 'pre edit' values. Note that CopyFrom is just a helper method on my entity.
protected void dbgCountries_StartRowEditing(object sender, DevExpress.Web.Data.ASPxStartRowEditingEventArgs e)
{
if (editCountry == null)
{
editCountry = new Country();
}
var context = new MyDBContext();
var currCountry = context.Countries.Where(c => c.CountryID == (int)(e.EditingKeyValue)).Single();
editCountry.CopyFrom(currCountry);
}
Hook RowUpdating on ASPXGridView. Here is where I make sure old values are correct before update goes ahead. This ensures concurrency will work as expected.
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//Ensure old values are populated
e.OldValues["RowVersion"] = editCountry.RowVersion;
//No need to set other old values as I am only testing against
//the one field for concurrency.
//On row updating ensure RowVersion is set.
//This is the field being used for the concurrency check.
DateTime date = DateTime.Now;
var s = date.ToString("yyyy-MM-dd HH:mm:ss");
e.NewValues["RowVersion"] = s;
}

Folder browsing in ASP.net

What I'm trying to do here is to allow my user to select a path in a data server on a network, so that I could generate a configuration file.
I hope to be able to replicate the function of OpenFileDialog() on my asp.net page. However this function does not exist on asp.net, and I do know that there is this control in asp.net call FileUpload. But what I required here, is just the path/directory for the folder. I do not require my files to be uploaded.
How can it be done?
Doing this in a web application is tricky. You would have to enumerate the folders on the server that you want to browse (presumably this is the same server that's running the web application), and then present that hierarchy to the user to select a folder. If it's not too big a hierarchy, you could just enumerate the whole bunch up front, and display it in a tree. If it's big for that, you could use an Ajax approach: select the top-level folder, then send an Ajax request to get the next level, and so on.
To enumerate the folders, you'll need to walk the filesystem yourself. See http://msdn.microsoft.com/en-us/library/dd997370(v=vs.100).aspx for one way.
No, there is no inbuilt control for this. It is not a normal requirement cause most site don't let their users see their file structures.
Building a user control that does this will be simple though.
I suggest using a TreeView asp.net control, attached to your datasource where you have listed the files.
This sample on binding a treeview should get you started.
You can populate your data using
var path = Server.MapPath("/");
var dirs = Directory.[EnumerateDirectories][2](path);
var files = Directory.[EnumerateFiles][3](path );
Finally to make it look like a dialog, you could use the jQuery UI dialog component.
The solution I have found is, this is just for anyone looking for answer:-
protected void browse_Click(object sender, EventArgs e)
{
Thread thdSyncRead = new Thread(new ThreadStart(openfolder));
thdSyncRead.SetApartmentState(ApartmentState.STA);
thdSyncRead.Start();
}
public void openfolder()
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
DialogResult result = fbd.ShowDialog();
string selectedfolder = fbd.SelectedPath;
string[] files = Directory.GetFiles(fbd.SelectedPath);
System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
}
The asp.net site is a completely disconnected environment to your server. As other people have mentioned, to replicate an OpenFileDialog() you will need to look at the folder structure and present this to the user in the web/disconnected environment. In this case the user is abstracted from the actual file system... since this abstraction already occurs, it would be a good time to consider the route you're taking. It might be worth considering that a direct replication of the file system is not required, you could manage the "virtual" folder structure in the database with links/paths to files on disk are maintained there?

Request param missing on POST

I am re-writing an ASP.NET application and noticed a difference in behaviour ...
In my Page_Load event I have some code such as:
string id = Request["id"]
which gets the id param from the URL. On page load (ie a HTTP GET), this works as expected in both versions. I also have a button onclick event handler. Clearly, this performs a POST to the server, and also invokes the Page_Load handler. The difference is, that in the original version of the app, the id is successfully loaded from the request. In the new version of the app, id comes back as null. I have discovered that I need to use Request.Params["id"] instead, but am totally puzzled as to why Request["id"] works for POST requests in one app but not the other.
The only difference between the apps is that the first was created as File -> New Website and the second File -> New Web Application. I think this is what is causing the difference in behaviour, but am wondering why this subtle difference, and also if there is anything else I should be aware of between the 2.
Any advice greatly appreciated.
As you have mentioned, you have the id parameter coming through twice. This will be because you have one in the query string parameters and one in the form parameters. I'm not sure why this would be occurring in one web app and not the other, but you can make changes to your code to account for it in a more correct way.
If you view the source of the HTML in your browser, you will see that the action value for the form will be current pages URL, including the query string. This is why the first id is being sent through. Evidently, the second id is coming through via the form itself:
HTML Source of basic web form
<form method="post" action="Default.aspx?id=3" id="ctl01">
<input type="text" name="id">
</div>
There are a couple of things you can do here:
first off, I wouldn't use Request.Params["id"] for this, as it combines the query string, form, cookies and server variables into one collection. You should use Request.Querystring and Request.Form properties, based on what you require and when
In your Page_Load handler, use the Page.IsPostBack property to determine whether the page is loading for a GET or POST and use the Request properties described above.
Example of Page.IsPostBack usage:
protected void Page_Load(object sender, EventArgs e)
{
string id = string.Empty;
if (Page.IsPostBack)
{
id = Request.Form["id"];
}
else
{
id = Request.QueryString["id"];
}
}
I always use web applications project but the difference is compilation. Website has a dynamic compilation, which means that the first request will be slower and web app has pre-compiled release dlls.
Check this for pro's and con's : http://maordavid.blogspot.ca/2007/06/aspnet-20-web-site-vs-web-application.html

ssrs reportviewer loading to infinity without error message

when i run a report with parameter in my asp.net application i see loading div to infinity
without any indication of an error (so i dont know how to search about this issue in google)
note1:
i can run the same report directly from report server
note2:
if i removed the parameter it run normally from asp.net page as well as from report server
protected void Page_Load(object sender, EventArgs e)
{
oID = (Int64)Session["OID"];
ViewReport();
}
public void ViewReport()
{
string reportServerUrl = ConfigurationManager.AppSettings.Get("ReportServerPath");
ReportViewer.ServerReport.ReportServerUrl = new System.Uri(reportServerUrl);
ReportViewer.ServerReport.ReportPath = #"/StoReports/MyReport";
ReportViewer.ServerReport.SetParameters(new ReportParameter("OID", oID.ToString()));
ReportViewer.ServerReport.Refresh();
}
in a function called Sys$WebForms$PageRequestManager$_endPostBack(error, executor, data)
tools sql server Denali , visual studio 2010
this article explains the issue in details
1-Browser makes a GET request to the ASPX page to get the page content
and a loading indicator for the report.
2-Browser makes a POST request
to the ASPX page to get the HTML for the report (this content is in an
UpdatePanel).
3-Browser makes GET requests to the HTTP handler to get
all the images in the report In step 2 the request to get the report
content runs the ASP.Net page, including any code you have placed in
the page.
Why does this matter? code was added to the load event of the page
that altered the state of the report viewer. The most common example
I’ve seen is user code calling SetParameters in the load event, though
there are several methods and properties that will trigger this.
Changing the parameter values tells the ReportViewer that it needs to
restart report processing. Effectively, it tells the viewer to return
to step 1 – put the loading indicator in the browser and restart
report processing. If you do this during every postback, the viewer
never successfully completes step 2. It just goes into an infinite
loop.
Calling methods like SetParameters isn’t cheap. Each call triggers a
round trip to the report server. So it’s a call you want to minimize
anyway. By only calling SetParameters during the initial GET request
or only when parameter values have actually changed, you can improve
the performance of your application and break the loop. A simple
check of IsPostBack before calling SetParameters is usually
sufficient.

How to change the data format for the current culture so that it applies to the entire web application

I would like to set the date format to 'YYYY-MM-DD' for my entire web application. I can do this by creating an object of CultureAndRegionInfoBuilder and then registering it. But since my application is on a hosted environment, I cannot run executables to register the culture.
I need a way to do this within the Application_Start so that it applies to the entire web app.
I tried changing the date format using Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern, but it does not seem to propagate the change to the entire application.
In short, I need a way to change current culture settings of the entire web application programatically from with in the web app itself.
Edit: I tried the following on suggestion of #doitgood, but got exception System.InvalidOperationException: Instance is read-only., while the intellisense shows the property as Get or Set.
void Application_BeginRequest(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
}
To make the change global to the application I tried the following in Application_Start:
System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
I get the same exception 'Instance is read-only'.
The answer by Sebeto is correct. I had the same problem and I just tried it. Thought I'd add some more explanation. The current culture's properties are indeed read-only once the current culture has been set. You must therefore construct a new CultureInfo object (easiest way is by cloning the existing culture). Then, you can tweak the culture info object's properties before swapping the current threads culture object with your own.
CultureInfo newCultureDefinition = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
//make percentages n% instead of n % .ToString("p") adds a space by default english culture in asp.net
newCultureDefinition.NumberFormat.PercentPositivePattern = 1;
newCultureDefinition.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
Thread.CurrentThread.CurrentCulture = newCultureDefinition;
There is also the question of where to put the code to change the current thread's culture.
According to Microsoft's example, see http://support.microsoft.com/kb/306162 you can even change the current culture on Page_Load. Since you might have code that needs your formatting before Page_Load, I have put my change in my base page OnInit.
protected override void OnInit(EventArgs e)
{
initGlobalCulturalFormattingChanges();
}
and to cap it off:
private void initGlobalCulturalFormattingChanges()
{
CultureInfo newCultureDefinition = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
//make percentages n% instead of n % (.ToString("p0") adds a space by default in asp.net
newCultureDefinition.NumberFormat.PercentPositivePattern = 1;
newCultureDefinition.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
Thread.CurrentThread.CurrentCulture = newCultureDefinition;
}
Is it the best idea to swap out the current Thread's culture with every request one every page's init? I don't have enough data to weigh in on that but it seems to work ok :).
got exception 'System.InvalidOperationException: Instance is read-only.', while the intellisense shows the property as Get or Set.
Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern ="yyyy-MM-dd";
You can't directly change the things in Thread.CurrentThread.CurrentCulture apparently, I got the same problem.
But you can do it like this:
CultureInfo TempCulture = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
TempCulture.DateTimeFormat.ShortDatePattern ="yyyy-MM-dd";
Thread.CurrentThread.CurrentCulture = TempCulture;
If you do this on application_start, only the thread that started the application will be effected.
You need to ensure the change happens on every request - an HttpModule that sets this for every request is probably the cleanest solution.
Cant you put your code into global.asax Application_BeginRequest event and update appropriate Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture?

Resources