Why is list emptying itself between events? - c#

I'm using a List<> as a container for some data returned from database, like this:
List<BookInfo> result {get;set;}
protected void SearchButton_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
result = (new BookInfo()).Search(TextBox1.Text);
ListView1.DataSource = result;
ListView1.DataBind();
}
}
Everything works ok. But when I'm trying to sort this List in other event like this one, it's empty.
protected void ListView1_Sorting(object sender, ListViewSortEventArgs e)
{
IComparer<BookInfo> comparer = new BookInfoOrdering();
if (result != null)
{
result.Sort(comparer);
}
ListView1.DataSource = result;
ListView1.DataBind();
}
I'd just like to know why is it happening. One solution is to call the search method and get the data again but isn't it unnecessary?

You were trapped by the same trap that many people are trapped by many times - it's the page lifecycle. ASP.NET fools you by pretending that you had one environment including server and browser, but that's not the case. HTTP is state-less. If you need to persist state between two requests, you have several options:
Serialize the state into the so-called ViewState such as to send it to the browser and post it back to the server
Save it in a database
Save it in the session
Introduce caching for the data that need to be retrieved, that is, sequential calls to BookInfo.Search with equal parameter values don't issue a new database request (or whatever else is required to get the data)

This would work in a desktop application, but not a asp.net website. Server side data retrieved from one postbacks is not stored for the next postback.
There are several methods for storing data between postbacks. For example using the Session State of the website:
// storing
HttpContext.Current.Session["list"] = result;
// retrieving
List<BookInfo> temp = (List<BookInfo>)HttpContext.Current.Session["list"];
you should also be able to retrieve the list from the ListView where it was bound.
IComparer<BookInfo> comparer = new BookInfoOrdering();
List<BookInfo> temp = (List<BookInfo>)ListView1.DataSource;
temp.Sort(comparer);
ListView1.DataSource = temp;
ListView1.DataBind();

Related

variable not use in another webform

managing connection string on dropdown selection now the question is this "connection" object is not working in another webform because i need to use this connection on hole app using this connection object how to solve
String connection = String.Empty;
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if (DropDownList1.SelectedItem.Text.Equals("RVL LOGISTICS (I) PVT LTD"))
{
connection = ConfigurationManager.ConnectionStrings["CompMasterConnectionString"].ConnectionString;
}
else if (DropDownList1.SelectedItem.Text.Equals("SIMONS SHIPPING PVT LTD"))
{
connection = ConfigurationManager.ConnectionStrings["DUM01ConnectionString"].ConnectionString;
}
else
{
DropDownList2.Enabled = false;
}
}
Page state is not persistent: it goes out of memory once the page is rendered.
You have to store the value elsewhere in order to reuse it. Where you save it depends on where you want to use it.
Some options:
Pass it through an URL. This might have security implications;
View state;
Session object storage;
Application object storage.

save and load Listbox Items locally and pass them to other pages

I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..

Prevent multiple running of a server side method

I have a webpage with a button that does postback, connects to an external database to download some data and perform some database updates. The issue I have is the possibility that two or more people runs this download simultaneously or while the function is still running, which may cause problems.
How do I create some form of semaphore so that if the second person clicks the button, he'll get a message saying it's currently being updated?
Use a proper transaction on your external database and apply row locking there as needed; the DB system should handle the concurrency just fine.
At the first sight, I would use an ASP.NET Application variable enclosed in a lock statement to check and update it if needed.
protected void Page_Load(object sender, EventArgs e)
{
if (!GetSem_SomeoneIsDownloading())
{
PerformDownload();
ClearSem_SomeoneIsDownloading();
}
else
{
DisplayMessageSomeoneIsDownloadingAlready();
}
}
bool GetSem_SomeoneIsDownloading()
{
bool isSomeoneDownloading;
Application.Lock();
isSomeoneDownloading = (bool)(Application["SomeoneIsDownloading"] ?? false);
if (!isSomeoneDownloading)
Application["SomeoneIsDownloading"] = true;
Application.UnLock();
return isSomeoneDownloading;
}
void ClearSem_SomeoneIsDownloading()
{
Application.Lock();
Application["SomeoneIsDownloading"] = false;
Application.UnLock();
}

navigation between page windows phone without reloading

private void btn_friends_pressed(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/Friends.xaml", UriKind.Relative));
}
When I press the button I go to the Friends page, which loads many friends from isolated storage.Than I press "back" button and go to the Menu page, when I press again the button, I have "Operation not permitted on IsolatedStorageFileStream." message.
How I can not reload page and keep it in RAM.
Something like:
if (Friends.Page.IsRunning==true)
NavigationService.Navigate("/Friends.xaml");
else
NavigationService.Navigate(new Uri("/Friends.xaml", UriKind.Relative));
Whenever you navigate to a page, it is reloaded automatically. The pages themselves are not kept in memory once you've navigated away from them. If you want to store it memory, and not read it from Isolated Storage each time, then you can simply create a static class that contains a static List that stores your friends. Once you've loaded your friends, depending on their type, you can add it to the list. Whenever you need to access them, simply call it from the static List. For example, in your solution, create a new class:
using ... //your using directives
namespace MyApp //Your project Namespace
{
public static class FriendsStorage //rename `FriendsStorage` to whatever you want
{
public static List<Friends> ListOfFriends = new List<Friends>(); //Your list
}
}
To set it, you can load the information from IsolatedStorage and add it to the list:
foreach(Friend f in Friends)
FriendsStorage.ListOfFriends.Add(f);
Whenever you need to query the Friends list you can call it like this:
var friendList = FriendsStorage.ListOfFriends;
Even if you use the above method, you should try and fix the error you're getting. Can you post your Isolated Storage code?
If you want to get rid of the error message, you should use your stream in a using() block,
using (var stream = new IsolatedStorageFileStream(...))
{
// load your data here
}
Regarding saving page, it's generally not a good idea because your memory can exponentialy grow and your application will be very unresponsive.
Although you can always use your App.xaml.cs as a global instance of your application to cache some of your data sources:
List<Friend> _Friends;
List<Friend> _Friends
{
get
{
if(_Friends == null) _Friends = GetFriends();
return _Friends;
}
}
but if you did this be very careful not to store loads of data.

Tracking external changes to a database with LINQ-to-SQL

Is there a way to get SQL Server 2005 to call back to a connected application, such that the connected application will know when a record in a table has had a field modified by another application using the same database?
A simple example would be two instances of the same application connecting to the same table in the same database. When one instance of the application makes a change to a table, the other instance would get a notification that something has changed and be able to query the database for the change.
UPDATE
Thanks so much for the help so far. I would have never even known to look for the SqlDependency class. I've followed the instruction on this page http://msdn.microsoft.com/en-us/a52dhwx7.aspx in creating the SqlDependency test demo. However, I wasn't able to get that to work. I never see the OnChange event get called.
I've also attempted to modify my own application using the instructions as a guide with no luck. I've included the code from my own application below. Basically, the Position table has a PositionID field along with a LocationX and LocationY field. I've written another application that allows me to update the LocationX field of a given row.
What am I missing? Why won't the database changes trigger my even handler?
UPDATE #2
Also note that I am using a hard coded SQL string for my command. I would prefer not to and use the commented out LINQ statement instead. Is it considered OK to use LINQ in this way to generate the SQL string that will be used to build the command?
UPDATE #3
So I managed to figure out what was wrong with my code below. Apparently you have to execute the command once so there will be a cache of data, or else the server doesn't know when to notify you? I added in a line to do a DataAdapter.Fill() with my SqlCommand and the event now seems to fire when expected.
Which brings me to my next problem. The SqlDependency.OnChange event only lets you know that something has changed. How can I figure out from my old DataSet and the new DataSet what the row-by-row changes are?
I could of course, read the entire query again and update all of my data structures, but that seems excessive.
I can call the DataContext.Refresh() and have it do all the updates to my data structures, but that doesn't seem to trigger any of the DataContext generated OnChanging() events. It seems that Refresh() actually tears down all my structures and creates new ones. So I can never figure out what has changed.
Does anyone have any recommendations?
public partial class MainForm : Form
{
private ArpPhase2DbContextDataContext db = null;
private SqlConnection connection = null;
private SqlCommand command = null;
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
this.canRequestNotifications();
this.db = ArpPhase2DbContextDataContext.Instance;
this.setupSqlDependency();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
SqlDependency.Stop(this.db.Connection.ConnectionString);
if (this.connection != null)
{
this.connection.Close();
}
this.db.SubmitChanges();
}
private bool canRequestNotifications()
{
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void setupSqlDependency()
{
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop(this.db.Connection.ConnectionString);
SqlDependency.Start(this.db.Connection.ConnectionString);
if (this.connection == null)
{
this.connection = new SqlConnection(this.db.Connection.ConnectionString);
}
if (this.command == null)
{
var sql = (from position in this.db.Positions
select position);
//string commandString = sql.ToString();
string commandString = "SELECT * FROM Positions;";
this.command = new SqlCommand(commandString, connection);
}
this.getData();
}
private void getData()
{
// Make sure the command object does not already have
// a notification object associated with it.
this.command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(this.command);
dependency.OnChange += new OnChangeEventHandler(this.dependency_OnChange);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// Updating the UI from a worker thread is not permitted.
// The following code checks to see if it is safe to
// update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code
// is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch.
OnChangeEventHandler del = new OnChangeEventHandler(this.dependency_OnChange);
object[] args = { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(del, args);
return;
}
// Remove the handler, since it is only good
// for a single notification.
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= this.dependency_OnChange;
// Add information from the event arguments to the list box
// for debugging purposes only.
Console.WriteLine("Info: {0}, Source: {1}, Type: {2}", e.Info.ToString(),
e.Source.ToString(), e.Type.ToString());
// Rebind the dependency.
this.setupSqlDependency();
}
}
SQL Server can do this with Query Notifications. There's nothing built into L2S to support this, but also nothing to stop you from using it outside of L2S in the same app.
Query Notifications uses the indexed view technology to detect data changes and notify subscribed queries when the result set has possibly changed. This is the technology that powers the ASP SqlCacheDependency for cache invalidation. You can read more into how it works at The Mysterious Notification.
In .Net Framework the most comonly used component that leverages Query Notifications is SqlDependency. There are various samples on how to integrate linq2sql with SqlDependency, like linqtosqlcache.
You should not use this technology to watch for data that changes frequently but solely for catalog reference data that is worth caching. The cost of setting up and delivering the notification is significant.
Why do you want to do that?
Linq-to-SQL was dead before you began using it.
Now they push EF, WCF-DS etc. (who knows when they will kill them too).
Even query notifications are not a safe bet anymore (as they are so fragile if you have an app that is going to last more than a few years).

Categories

Resources