Multiple user seeing the same items in shopping cart - c#

I have created a shopping cart using session to store the item being added to cart.Upon deployment i found out that the shopping cart is not unique to the user that is other user are getting items added to the cart by other user.
I tried to use the login username in the session name to make the session unique to that user but it is not working.
public class ListOfDataset
{
static ListOfDataset()
{
string username = ClaimsPrincipal.Current.Identity.Name;
// If the cart is not in the session, create one and put it there
// Otherwise, get it from the session
if (HttpContext.Current.Session[string.Format("ASPNETShoppingCart-{0}", username)] == null)
{
Instance = new ListOfDataset();
Instance.Items = new List<DataSet>();
HttpContext.Current.Session[string.Format("ASPNETShoppingCart-{0}", username)] = Instance;
}
else
{
Instance = (ListOfDataset)HttpContext.Current.Session[string.Format("ASPNETShoppingCart-{0}", username)];
}
}
}
Updated code,error in else statement-object reference not set and function ChekIfdatasetexist always return false:
public class ListOfDataset
{
public static ListOfDataset Instance
{
get
{
ListOfDataset cart = null;
if (HttpContext.Current.Session["ASPNETShoppingCart"] == null)
{
cart = new ListOfDataset();
cart.Items = new List<DataSet>();
HttpContext.Current.Session["ASPNETShoppingCart"] = cart.Items;
}
else
{
cart.Items =(List<DataSet> )HttpContext.Current.Session["ASPNETShoppingCart"];
}
return cart;
}
}
public List<DataSet> Items { get; private set; }
public void AddItem(DataSet itemdataset)
{
Items.Add(itemdataset);
HttpContext.Current.Session["ASPNETShoppingCart"] = Items;
}
public bool CheckIfDataSetExist(string servicename)
{
DataSet DataSetexist = null;
if (Items != null)
{
DataSetexist = Items.Where(i => i.DataSetName == servicename).FirstOrDefault();
}
if (DataSetexist != null) return true;
return false;
}
}

There are two issues here.
As someone said in comments you have made the Instance member static, which means it will remain the same application wide and shared among users.
Session is not the best place to store per user data cause it will blow up your server memory and wont scale. Better use a database to store the data.

Related

How to update list box items with a timer

I'm working on a messenger program and I have a timer which constantly deletes and adds new list box items so the list box flickers all the time. I'm trying to make the flickering stop. The reason I'm constantly deleting and adding new list box items is because if a friend logs in, it will change there status from offline to online.
Timer code:
private void Requests_Tick(object sender, EventArgs e)
{
LoadData();
}
LoadData() code:
FriendsLb.BeginUpdate();
_S = new Status();
Image Status = null;
FriendsLb.Items.Clear();
try
{
var query = from o in Globals.DB.Friends
where o.UserEmail == Properties.Settings.Default.Email
select new
{
FirstName = o.FirstName,
LastName = o.LastName,
Email = o.Email,
Status = o.Status,
Display = string.Format("{0} {1} - ({2})", o.FirstName, o.LastName, o.Email)
};
newFriendsLb.DataSource = query.ToList();
newFriendsLb.ClearSelected();
FriendsLb.DrawMode = DrawMode.OwnerDrawVariable;
foreach (object contact in query.ToList())
{
string details = contact.GetType().GetProperty("Display").GetValue(contact, null).ToString();
string email = contact.GetType().GetProperty("Email").GetValue(contact, null).ToString();
string status = _S.LoadStatus(email);
if (status == "Online")
{
Status = Properties.Resources.online;
}
else if (status == "Away")
{
Status = Properties.Resources.busy;
}
else if (status == "Busy")
{
Status = Properties.Resources.away;
}
else if (status == "Offline")
{
Status = Properties.Resources.offline;
}
FriendsLb.Items.Add(new Listbox(_A.LoadFriendAvatar(email), Status, details));
}
contact = query.ToList();
FriendsLb.MeasureItem += FriendsLb_MeasureItem;
FriendsLb.DrawItem += FriendsLb_DrawItem;
FriendsLb.EndUpdate();
Is there a way to update the current list box items constantly rather than constantly deleting and adding new ones?
Here's the GUI:
The are several ways to remove the flicker - all basically involve not completely repopulating the list each time. For this, you want to get the current status for the users and simply update the existing list.
In order for the control to see changes to the list items, rather than an anonymous type, you need a User class so that you can implement INotifyPropertyChanged. This "broadcasts" a notice that a property value has changed. You will also need to use a BindingList<T> so those messages get forwarded to the control. This will also allow additions/deletions from the list to be reflected.
You will also need a concrete way to find each user, so the class will need some sort of ID.
public enum UserStatus { Unknown, Online, Offline, Away, Busy }
class User : INotifyPropertyChanged
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Image StatusImage;
private UserStatus status = UserStatus.Unknown;
public UserStatus Status
{
get{return status;}
set{
if (value != status)
{
status=value;
PropertyChanged(this, new PropertyChangedEventArgs("Status"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public override string ToString()
{
return string.Format("{0}, {1}: {2}", LastName, FirstName, Status);
}
}
Then the collection:
private BindingList<User> Users;
private Image[] StatusImgs; // See notes
The BindingList is then used as the DataSource for the control:
Users = GetUserList();
// display the list contents in the listbox:
lbUsers.DataSource = Users;
timer1.Enabled = true;
Updating the user status just involves resetting the Status on each user which has changed. The BindingList<User> will then notify the control to update the display:
private void UpdateUserStatus()
{
// get current list of user and status
var newStatus = GetCurrentStatus();
User thisUser;
// find the changed user and update
foreach (User u in newStatus)
{
thisUser = Users.FirstOrDefault(q => q.Id == u.Id);
// ToDo: If null, there is a new user in the list: add them.
if (thisUser != null && thisUser.Status != u.Status)
{
thisUser.Status = u.Status;
thisUser.StatusImage = StatusImgs[(int)u.Status];
}
}
}
Results:
Note that there is a potential leak in your app. If you drill into the code to get an image from Resources you will see:
internal static System.Drawing.Bitmap ball_green {
get {
object obj = ResourceManager.GetObject("ball_green", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
GetObject() is creating a new object/image each time you call it, your code doesnt show the old one being Disposed() so, it is likely leaking resources.
Since each online user doesn't need their own unique instance (or a new one when the status changes), load them once into a List or array so they can be reused:
// storage:
private Image[] StatusImgs;
...
// populate:
StatusImgs = new Image[] {Resources.ball_black, Resources.ball_green,
Resources.ball_red, Resources.ball_yellow, Resources.ball_delete};
...
// usage:
thisUser.StatusImage = StatusImgs[(int)u.Status];
You could also change it so the User class updates that itself when the Status changes.
Finally, you might want to consider a simple UserControl for the UI rather than what appears to be an owner drawn Listbox.
If you don't want to change your code structure to eliminate the repeated Clear/Reload cycle, you should suspend UI drawing while you are rebuilding your list using;
using(var d = Dispatcher.DisableProcessing())
{
/* your work... */
}
As suggested here In WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms

DataGrid sets all items to same value , when i want to change only 1 item

when i try to set a specific Item in dataGrid , it changes all other item's values to that same value. I'm not sure if it's a bug or i done something wrong. Here is my code:
(Datagrid is in another window (Main window), so i called a function in that window to edit the value)
private void AAbutton1_Click(object sender, RoutedEventArgs e)
{
Account selected = new Account();
if (textBox2.Text != null)
selected.username = textBox2.Text;
if (textBox12.Text != null)
selected.password = textBox12.Text;
if (locationTxtBox2.Text != null)
selected.location = locationTxtBox2.Text;
MainWindow.Instance.editAccount(selected);
MainWindow.Instance.updateData();
MainWindow.Instance.needsSave = true;
}
And here is the function in the main window:
public void editAccount(Account acc)
{
Account acc2;
Account selected = (Account)dataGrid.SelectedItem;
acc2 = Manager.accounts.ElementAt(Manager.accounts.FindIndex(a=> a == selected));
acc2.username = acc.username;
acc2.password = acc.password;
acc2.location = acc.location;
}
I really couldn't find a solution for this problem.
And here is the Account class in case you need it:
public class Account
{
public String username { get; set; }
public String password { get; set; }
public String location { get; set; }
public Account(String username,String password, String location)
{
this.username = username;
this.password = password;
this.location = location;
}
public Account()
{
}
}
Just to mention , i use Mahapps.metro controls.
I was right! I read your mind.
This isn't a WPF question, a binding question, or a DataGrid question. It's a "how do references work in C#?" question. It's a good question.
On file load, you start with a list of encrypted Accounts, but in decryption, you copy all the decrypted properties of each one of the accounts into the same instance of Account, and add that one instance multiple times to the list. The decrypted ones are all the same instance. You start off OK, but then you go off the rails in DecryptAccounts().
Here's the bug:
public static void DecryptAccounts()
{
// Hmmm. What's he planning to do with this?
Account holder = new Account(null, null, null);
accounts.Clear();
foreach (Account acc in Encryptedaccounts)
{
// HERE IT IS. This is the same instance of holder on every
// iteration. After file load, every Account in accounts is the
// same object as every other.
// You need to create a new Account object for each account.
holder.username = Decrypt(acc.username, user.Decryptedpassword);
holder.password = Decrypt(acc.password, user.Decryptedpassword);
holder.location = Decrypt(acc.location, user.Decryptedpassword);
accounts.Add(holder);
}
}
public static void LoadFromFile()
{
if (File.Exists(Path.Combine(appdata, folder, file)))
{
Encryptedaccounts = JsonConvert.DeserializeObject<List<Account>>(File.ReadAllText(Path.Combine(appdata, folder, file)));
}
DecryptAccounts();
}
Here's the fix
Manager.cs
public Account DecryptAccount(Account acc)
{
return new Account {
username = Decrypt(acc.username, user.Decryptedpassword),
password = Decrypt(acc.password, user.Decryptedpassword),
location = Decrypt(acc.location, user.Decryptedpassword)
};
}
public static void DecryptAccounts()
{
accounts.Clear();
foreach (Account acc in Encryptedaccounts)
{
accounts.Add(DecryptAccount(acc));
}
}
// You've got the same issue here
private static void EncryptAccounts()
{
Encryptedaccounts.Clear();
foreach (Account acc in accounts)
{
Encryptedaccounts.Add(EncryptAccount(acc));
}
}
public Account EncryptAccount(Account acc)
{
return new Account {
username = Encrypt(acc.username, user.Decryptedpassword),
password = Encrypt(acc.password, user.Decryptedpassword),
location = Encrypt(acc.location, user.Decryptedpassword)
};
}
Some other issues here. Not bugs, but life will be easier if you do stuff the "proper WPF way":
Manager.accounts should be of type ObservableCollection<Account>. Then it will automatically notify the DataGrid whenever you add or remove items from it and you won't have to do this updateData() thing to manually refresh the grid all the time.
Manager and Account both ought to implement INotifyPropertyChanged and fire notifications on their properties when their values change. In C#6, this is very simple:
using System.Runtime.CompilerServices;
using System.ComponentModel;
// ... snip ...
public event PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
Then your properties look like this:
private String _username = null;
public String username {
get { return _username; }
set {
if (value != _username)
{
_username = value;
OnPropertyChanged();
}
}
}
When they do that, anything you bind them to in the UI will be notified whenever you change the values. You'll be able to set properties on the selected grid item and the UI will update without any grid refresh or anything -- it'll just know. Very convenient.

Adding Item to Generic List in Session

I have a session helper so that my session vars are strongly typed:
public sealed class SessionHelper
{
private static HttpSessionState Session
{
get
{
return HttpContext.Current.Session;
}
}
public static List<TestObject> Tests
{
get
{
List<TestObject> objects = new List<TestObject>();
if (Session["Tests"] != null)
{
objects = (List<TestObject>)Session["Tests"];
}
return objects;
}
set
{
Session["Tests"] = value;
}
}
}
Now I am trying to add an item to theTestObjects List so I thought I could just do:
SessionHelper.Tests.Add(new TestObject("Test name", 1));
But when I step through the code and look at the SessionHelper.Tests after the above line is run, the list count remains at 0.
If I do:
List<TestObject> tests = SessionHelper.Tests;
tests.Add(new TestObject(testName, version));
SessionHelper.Tests = tests;
Then it works properly.
Why can't I add the test object directly to the SessionHelper?
Session["Tests"] is null when you start. Therefore SessionHelper.Tests returns a new, empty list; however, this new list is not in the session object yet. Therefore SessionHelper.Tests will return a new, empty list every time. Store the new list in the session object after creating it.
public static List<TestObject> Tests
{
get
{
List<TestObject> objects = (List<TestObject>)Session["Tests"];
if (objects == null)
{
objects = new List<TestObject>();
Session["Tests"] = objects; // Store the new list in the session object!
}
return objects;
}
set // Do you still need this setter?
{
Session["Tests"] = value;
}
}

ASP.NET Entity Framework property not loaded while ID filled

This is something that worked up to now and now it just, stopped working (I know, weird, there's probably some silly mistake..)
I have a TripsVM, which contains a list of trips. I load these in my service, returning a List<>.
The problem occurs when I iterate over the trips collection and try to get trip.TripCategory.Name, as the TripCategory is empty, even though TripCategoryID has a value.
This all happens at the backend, I load the trips and then try to iterate over them, they are not being send from the page.
I could probably just load the trip by trip itself, but it used to work and this bug just came up after months of usage.
Any suggestions of where to look for bugs would be really appreciated.
Thanks
Error:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Where error occurs:
foreach (Trip trip in tripsVM.TripsList) {
var a = trip.TripCategory.Name;
TripsVM:
private List<Trip> _TripsList;
public List<Trip> TripsList
{
get
{
if (_TripsList == null)
{
_TripsList = TripsService.GetTrips();
if (_TripsList == null)
_TripsList = new List<Trip>();
}
return _TripsList;
}
set { _TripsList = value; }
}
Service:
public static List<Trip> GetTrips()
{
return DB.Trips.Where(...).OrderBy(...).ToList();
}
Trip class:
public partial class Trip
{
public int TripID { get; set; }
public int TripCategoryID { get; set; }
....
public virtual TripCategory TripCategory { get; set; }
}
Its looks like your DB context disposed before foreach code or LazyLoadingEnabled set to false in context.
In Service add using
using System.Data.Entity;
And modify loading method
public static List<Trip> GetTrips()
{ return DB.Trips.Where(...).Include(t=>t.TripCategory).OrderBy(...).ToList(); }
I think your code looks fine but you should add some if statements to avoid null exception, because you are returning something with where clause, so you might end up with empty query result and empty list, and in that list you are trying to reach an element of a list object:
if(tripsVM.TripsList != null){
foreach (Trip trip in tripsVM.TripsList) {
var a = trip.TripCategory.Name;
}
}
else
{
// handle empty list
}
private List<Trip> _TripsList;
public List<Trip> TripsList
{
get
{
_TripsList = new List<Trip>();
if(TripsService.GetTrips() != null)
{
_TripsList.add(TripsService.GetTrips());
}
return _TripsList;
}
set { _TripsList = value; }
}

Prism NavigationService get previous view name

Currently I'm implementing a Screen indicating wheater a module is not existing or still in development.
The Back Button has the following code:
regionNavigationService.Journal.GoBack();
This is working as expected. But the user is not coming from the Home Screen. So I need to access the View Name from the last Entry in Navigation Journal.
Example: User is coming from Settings Screen => The text should display "Back to Settings Screen"
Assuming the view name you are looking for is when you do new Uri("Main", UriKind.Relative) that you would want the word Main as the view name.
The forward and backward stacks in the RegionNavigationJournal are private. You could use reflection to get access to it.
var journal = regionNavigationService.Journal as RegionNavigationJournal;
if (journal != null)
{
var stack =
(Stack<IRegionNavigationJournalEntry>)
typeof (RegionNavigationJournal).GetField("backStack",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(journal);
var name = stack.Peek().Uri.OriginalString;
}
Or a better way is to implement your own IRegionNavigationJournal that is a wrapper around it. This is using Unity to constructor inject the default RegionNavigationJournal if using MEF you might need to put the ImportingConstructorAttribute on it.
public class RegionNavigationJournalWrapper : IRegionNavigationJournal
{
private readonly IRegionNavigationJournal _regionNavigationJournal;
private readonly Stack<Uri> _backStack = new Stack<Uri>();
// Constructor inject prism default RegionNavigationJournal to wrap
public RegionNavigationJournalWrapper(RegionNavigationJournal regionNavigationJournal)
{
_regionNavigationJournal = regionNavigationJournal;
}
public string PreviousViewName
{
get
{
if (_backStack.Count > 0)
{
return _backStack.Peek().OriginalString;
}
return String.Empty;
}
}
public bool CanGoBack
{
get { return _regionNavigationJournal.CanGoBack; }
}
public bool CanGoForward
{
get { return _regionNavigationJournal.CanGoForward; }
}
public void Clear()
{
_backStack.Clear();
_regionNavigationJournal.Clear();
}
public IRegionNavigationJournalEntry CurrentEntry
{
get { return _regionNavigationJournal.CurrentEntry; }
}
public void GoBack()
{
// Save current entry
var currentEntry = CurrentEntry;
// try and go back
_regionNavigationJournal.GoBack();
// if currententry isn't equal to previous entry then we moved back
if (CurrentEntry != currentEntry)
{
_backStack.Pop();
}
}
public void GoForward()
{
// Save current entry
var currentEntry = CurrentEntry;
// try and go forward
_regionNavigationJournal.GoForward();
// if currententry isn't equal to previous entry then we moved forward
if (currentEntry != null && CurrentEntry != currentEntry)
{
_backStack.Push(currentEntry.Uri);
}
}
public INavigateAsync NavigationTarget
{
get { return _regionNavigationJournal.NavigationTarget; }
set { _regionNavigationJournal.NavigationTarget = value; }
}
public void RecordNavigation(IRegionNavigationJournalEntry entry)
{
var currentEntry = CurrentEntry;
_regionNavigationJournal.RecordNavigation(entry);
// if currententry isn't equal to previous entry then we moved forward
if (currentEntry != null && CurrentEntry == entry)
{
_backStack.Push(currentEntry.Uri);
}
}
}
If using unity in your Prism Bootstrapper you will need to replace the default registration of the IRegionNavigationJournal
protected override void ConfigureContainer()
{
this.RegisterTypeIfMissing(typeof(IRegionNavigationJournal), typeof(RegionNavigationJournalWrapper), false);
base.ConfigureContainer();
}
If using MEF you will need to put the ExportAttribute on top of the RegionNavigationJournalWrapper
[Export(typeof(IRegionNavigationJournal))]
You can see http://msdn.microsoft.com/en-us/library/gg430866%28v=pandp.40%29.aspx for more information on replacing their default implementation with your own. Once you have the wrapper you will still need to cast it as RegionNavigationJournalWrapper to get access to the PreviousViewName so still not perfect or create an interface that RegionNavigationJournalWrapper also implements to cast to that to get you access to the PreviousViewName

Categories