I'm saving properties in my database with the CreateAMeeting method. When I check the database I can see all properties saved except my list, I can't see my list properties in my database. I have been googling but I can't find if it's possible to see them or if I'm just not saving them properly. If I use a breakpoint I can see that the meeting(db.Meetings.Add(meeting);) model have the times i added in the Times list.
When I later try to retrieve my list in the ChooseTimes method, it's null. I don't know if the problem is that I'm saving it wrong or its something else. What am I doing wrong and is it possible to see saved lists in the database?
I'm not done with the fooreachloop, pay no attention to it.
public class MeetingController : BaseController
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public static Meeting NewMeeting;
public static List<DateTime> TempTimes = new List<DateTime>();
public ActionResult CreateMeeting()
{
return View();
}
public ActionResult CreateAMeeting(Meeting meeting)
{
var userName = User.Identity.Name;
var user = db.Users.Where(u => u.UserName == userName).SingleOrDefault();
var model = new MeetingPeopleViewModel();
meeting.Creator = user;
meeting.Invited.Add(user);
meeting.Times = TempTimes;
meeting.Start = DateTime.Now;
meeting.End = meeting.Start.AddMinutes(meeting.Minutes);
db.Meetings.Add(meeting);
db.SaveChanges();
return View("AddToMeeting", model);
}
public ActionResult AddTempTime(DateTime Start, Meeting meeting)
{
TempTimes.Add(Start);
meeting.Times = TempTimes;
return View("CreateMeeting", meeting);
}
public ActionResult ChooseTimes()
{
var userName = User.Identity.Name;
var user = db.Users.Where(u => u.UserName == userName).SingleOrDefault();
Meeting meeting = new Meeting();
foreach(var item in db.Meetings)
{
if(item.Invited.Contains(user))
{
meeting = item;
}
}
return View(meeting);
}
This is my Meeting model.
public class Meeting
{
public int Id { get; set; }
public string Title { get; set; }
public ApplicationUser Creator { get; set; }
public ICollection<ApplicationUser> Invited { get; set; }
public double Minutes { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public ICollection<DateTime> Times { get; set; }
public bool AllDay { get; set; }
public ICollection<ApplicationUser> User { get; set; }
public ApplicationUser UserNotis { get; set; }
}
As far as Im aware you can not save a literal list to a database, you can save all the items in a list to a database, by iterating through the list and saving each unique record.
public class MeetingController : BaseController
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public static Meeting NewMeeting;
public static List<DateTime> TempTimes = new List<DateTime>();
public ActionResult CreateMeeting()
{
return View();
}
public ActionResult CreateAMeeting(Meeting meeting)
{
var userName = User.Identity.Name;
var user = db.Users.FirstOrDefault(u => u.UserName == userName);
var model = new MeetingPeopleViewModel();
meeting.Creator = user;
meeting.Invited.Add(user);
meeting.Times = TempTimes;
meeting.Start = DateTime.Now;
meeting.End = meeting.Start.AddMinutes(meeting.Minutes);
db.Meetings.Add(meeting);
db.SaveChanges();
return View("AddToMeeting", model);
}
public ActionResult AddTempTime(DateTime Start, Meeting meeting)
{
TempTimes.Add(Start);
meeting.Times = TempTimes;
return View("CreateMeeting", meeting);
}
public ActionResult ChooseTimes()
{
var userName = User.Identity.Name;
var user = db.Users.FirstOrDefault(u => u.UserName == userName);
Meeting meeting = new Meeting();
List<Meeting> Meetings = db.Meetings.ToList();
foreach (var item in Meetings)
{
if (item.Invited.Contains(user))
{
meeting = item;
}
}
return View(meeting);
}
There are better ways to do multiple things you are trying to do, but this will solve your issue.
You have to create a list of the meetings populated from the database so it is an Enumerable at the time you iterate rather than an query able,
I tried to save my entire list in one field, wich obviusly isn't possible. So i had to create a new model with a database to save evrything in my list. Here is my save method if anyone else is having problems saving items in a list.
public void SaveTimes(Meeting meeting)
{
foreach (var time in TempStart)
{
var mt = new MeetingTimes
{
MeetingId = meeting.Id,
Meeting = meeting,
Time = time
};
db.MeetingTimes.Add(mt);
db.SaveChanges();
}
}
Related
I want users to be able to select multiple skills from a MultiSelectList dropdown. I am able to save the multiple skill selections of each user to the database, but I discovered if I remove a skill option for one user, and I log in with a different user who previously had the same skill saved in his selections, it would have been removed for this user and every other user that had similar skill option saved.
Just so I am being clear. Let's say user A had these skills saved ["C#", "Python", "Java"]. User B current currently saved skills are ["C++","Scala"]. User B then logs and decides to add the C# he has just learnt. Once he updates his profile and his selections become this ["C++","Scala", "C#"]. C# would have been removed from User A's selections so it becomes ["Python", "Java"].
This is my custom IdentityUser class.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Skills = new List<Skill>();
}
public string Location { get; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
This is the Skill model.
public class Skill
{
public int SkillId { get; set; }
public string SkillType { get; set; }
}
And this is how I save the skill selections in the controller.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
List<Skill> tempSkills = new List<Skill> { };
foreach (var skillID in profileModel.SelectedSkillIds)
{
user.Skills.Add(_context.Skills.FirstOrDefault(x => x.SkillId == skillID));
var skill = _context.Skills.Find(skillID);
if (skill != null)
{
user.Skills.Add(skill);
tempSkills.Add(skill);
}
var allSkills = _context.Skills.ToList();
var skillsToRemove = allSkills.Except(tempSkills);
foreach (var sk in skillsToRemove)
{
user.Skills.Remove(sk);
}
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
Update - How I delete selections
if (profileModel.SelectedSkillIds != null)
{
List<UserSkill> tempSkills = new List<UserSkill> { };
foreach (var skillID in profileModel.SelectedSkillIds)
{
var skill = _context.Skills.Find(skillID);
if (skill != null)
{
var userskill = new UserSkill { AppUserId = user.Id, SkillId = skill.SkillId };
user.UserSkills.Add(userskill);
tempSkills.Add(userskill);
}
var allSkills = _context.UserSkills.ToList();
var skillsToRemove = allSkills.Except(tempSkills);
foreach (var sk in skillsToRemove)
{
user.UserSkills.Remove(sk);
}
}
You should create an class like UserSkills that have UserId and SkillId and in this case any user can have multiple skills and any skill can use for many users. see many-to-many, 1,2
You should change your model to this
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Skills = new List<Skill>();
}
public string Location { get; set; }
public virtual ICollection<UserSkills> Skills { get; set; }
}
public class Skill
{
public int SkillId { get; set; }
public string SkillType { get; set; }
public virtual ICollection<UserSkills> Skills { get; set; }
}
public class UserSkills
{
public int Id { get; set }
public int UserId { get; set }
public int SkillId { get; set }
public Skill Skill { get; set; }
public ApplicationUser User { get; set; }
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
List<Skill> tempSkills = new List<Skill> { };
foreach (var sk in user.UserSkills)
{
user.UserSkills.Remove(sk);
}
foreach (var skillID in profileModel.SelectedSkillIds)
{
var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
user.UserSkills.Add(userSkills);
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
Then add UserSkills to your DbContext
public DbSet<Skill> Skills { get; set; }
public DbSet<UserSkill> UserSkills { get; set; }
Finally use Add-Migration and Update-DataBase in package manager console
Another options
You can inject DbContext in your controller and add UserSkills data in UserSkill Tables
private readonly YourDbContext _dbContext;
public UserController(YourDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (ModelState.IsValid)
{
if (user.Location != profileModel.Location) user.Location = profileModel.Location;
if (profileModel.SelectedSkillIds != null)
{
var userSkillsForDelete = _dbContext.UserSkills.Where(a => a.UserId == user.Id).ToList();//<-- NOTE THIS
foreach (var sk in userSkillsForDelete)
{
//user.UserSkills.Remove(sk);
_dbContext.UserSkills.Remove(sk);<--NOTE THIS
}
foreach (var skillID in profileModel.SelectedSkillIds)
{
var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
_dbContext.UserSkills.Add(userSkills);<--NOTE THIS
}
await _userManager.UpdateAsync(user);
await _signInManager.RefreshSignInAsync(user);
return RedirectToAction("Profile", "Account");
}
return View(profileModel);
}
}
I have added the following UserLog table in the database using asnetidentity but i don't know how to perform add,update operation on the table.
public class User : IdentityUser
{
public virtual ICollection<UserLog> UserLogs { get; set; }
}
public class Context : IdentityDbContext<User, Role, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public Context(string connString)
: base(connString)
{
}
}
public class UserLog
{
[Key]
public Guid UserLogID { get; set; }
public string IPAD { get; set; }
public DateTime LoginDate { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
}
public System.Data.Entity.DbSet<UserLog> UserLog { get; set; }
Like in the following code, I am using inbuilt methods of these aspnetidentity tables but how to add an entry in the "UserLog" table when user login every time in the following method?
public override async Task AuthenticateLocalAsync(LocalAuthenticationContext ctx)
{
var username = ctx.UserName;
var password = ctx.Password;
var message = ctx.SignInMessage;
ctx.AuthenticateResult = null;
if (userManager.SupportsUserPassword)
{
var user = await FindUserAsync(username);
if (user != null)
{
if (userManager.SupportsUserLockout &&
await userManager.IsLockedOutAsync(user.Id))
{
return;
}
if (await userManager.CheckPasswordAsync(user, password))
{
if (userManager.SupportsUserLockout)
{
await userManager.ResetAccessFailedCountAsync(user.Id);
}
var result = await PostAuthenticateLocalAsync(user, message);
if (result == null)
{
var claims = await GetClaimsForAuthenticateResult(user);
result = new AuthenticateResult(user.Id.ToString(), await GetDisplayNameForAccountAsync(user.Id), claims);
}
ctx.AuthenticateResult = result;
}
else if (userManager.SupportsUserLockout)
{
await userManager.AccessFailedAsync(user.Id);
}
}
}
}
Step 1: Create an instance of UserLog
var userLog = new UserLog() { /* Set value for all the properties here */ };
Step 2: Add instance of UserLog to DbContext
context.Set<UserLog>().Add(userLog);
Step 3: Call DbContext.SaveChanges() to save changes to database.
context.SaveChanges();
Complete source code will looks like:
var userLog = new UserLog {
UserLogID = Guid.NewGuid(),
IPAD = "Some Value",
LoginDate = DateTime.Now,
UserId = "User Id Here"
};
var context = new Context();
context.Set<UserLogs>().Add(userLog);
context.SaveChanges();
I am submitting a form that contains a user id and a pipe-delimited set of numbers.
The model looks like this:
public class SeasonPassData
{
public int UserID { get; set; }
public string SpaceNumbers { get; set; }
}
That is getting handed off to this controller:
[HttpPost]
public ActionResult SignUp(SeasonPassData data)
{
var user = db.Users.Find(data.UserID);
List<SeasonPass> passes = new List<SeasonPass>();
char[] delimiter = { '|' };
var numbers = data.SpaceNumbers.Split(delimiter).Select(n => Convert.ToInt32(n)).ToArray();
foreach(int number in numbers)
{
passes.Add(new SeasonPass { SpaceNumber=number, User = user });
}
passes.ForEach(p => db.SeasonPasses.Add(p));
db.SaveChanges();
return RedirectToAction("Confirmation", "Home");
}
And should be creating and saving SeasonPasses:
public class SeasonPass
{
public int ID { get; set; }
public int SpaceNumber { get; set; }
public virtual User User { get; set; }
}
However, this - passes.ForEach(p => db.SeasonPasses.Add(p)); keeps raiding this exception:
An exception of type 'System.InvalidOperationException' occurred in
System.Data.Entity.dll but was not handled in user code
Additional information: The relationship between the two objects
cannot be defined because they are attached to different ObjectContext
objects.
How do I fix that?
I tried it with these changes based on a comment. It still doesn't seem to work...
[HttpPost]
public ActionResult SignUp(SeasonPassData data)
{
using(var context = new TTUContext())
{
var user = context.Users.Find(data.UserID);
List<SeasonPass> passes = new List<SeasonPass>();
char[] delimiter = { '|' };
var numbers = data.SpaceNumbers.Split(delimiter).Select(n => Convert.ToInt32(n)).ToArray();
foreach (int number in numbers)
{
passes.Add(new SeasonPass { SpaceNumber = number, User = user });
}
foreach (var pass in passes)
{
context.SeasonPasses.Add(pass);
}
context.SaveChanges();
}
return RedirectToAction("Confirmation", "Home");
}
I'm surprised that what you are doing does not work, but here is a way to sidestep the issue.
Add public int UserID { get; set; } to SeasonPass and set it to assign the relationship instead of setting User.
I have two simple classes, User and Task:
class User
{
public int UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
class Task
{
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<User> Followers { get; set; }
}
The Task class has a property Followers which is an ICollection<User>
Here is the db context class:
class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Task> Tasks { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<User>().HasKey(u => u.UserId);
mb.Entity<Task>().HasKey(t => t.TaskId);
}
}
and here is the code in the Main program:
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
db.Users.Add(user);
db.SaveChanges();
var follower = db.Users.Where(u => u.Name == "John Doe").FirstOrDefault();
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers.Add(follower); // ERROR
db.Tasks.Add(task);
db.SaveChanges();
The trouble is I am getting an error when trying to add the follower to the task.
Object reference not set to an instance of an object.
What am I doing wrong?
The problem is that the Followers collection is null. Instead of newing up your classes, let EF create them for you ...
var user = db.Users.Create();
and
var task = db.Tasks.Create();
If you're still getting problems then your proxies are not being created. You can initialise the collections in the class constructors, make each of them a HashSet<T>. It would be better though to identify why the proxies are not getting generated ...
public class Task
{
public Task()
{
Followers = new HashSet<User>();
}
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<User> Followers { get; set; }
}
try this. just initialize Follower
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
db.Users.Add(user);
db.SaveChanges();
var follower = db.Users.Where(u => u.Name == "John Doe").FirstOrDefault();
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers = new Collection<User>()
task.Followers.Add(follower);
db.Tasks.Add(task);
db.SaveChanges();
Try this. You will have to chan ge the constructor as mentioned by qujck
var db = new MyContext();
var user = new User();
user.Name = "John Doe";
user.Email = "jd#email.com";
var task = new Task();
task.Name = "Make the tea";
task.CreatedAt = DateTime.Now;
task.Followers.Add(user);
db.Tasks.Add(task);
db.SaveChanges();
You can initialize the List, because ICollection is an interface then it can't be initialized, but List can be (the following worked for me)
Instead of:
task.Followers.Add(follower);
Write:
task.Followers= new List<User>();
task.Followers.Add(follower);
This should solve your problem :)
I've made a Profile Model that stores certain property values. Such as firstName, lastName... etc... Among others state is one of them.
Now problem occurred when I've replaced TextBox with DropDownList for State property.
This is the way I've made my Edit method in ProfileController.
When opened app will populate if any existing values. First issue, how to get selected value from the dropdown, so i can pass it into Profile property like I did in this method.
public ActionResult Edit(string username)
{
ViewBag.StateID = new SelectList(db.States, "StateID", "StateName");
ProfileBase _userProfile = ProfileBase.Create(username);
ProfileModel _profile = new ProfileModel();
System.Web.HttpContext.Current.Session["_userName"] = username;
if (_userProfile.LastUpdatedDate > DateTime.MinValue)
{
_profile.FirstName = Convert.ToString(_userProfile.GetPropertyValue("FirstName"));
_profile.LastName = Convert.ToString(_userProfile.GetPropertyValue("LastName"));
_profile.Address = Convert.ToString(_userProfile.GetPropertyValue("Address"));
_profile.City = Convert.ToString(_userProfile.GetPropertyValue("City"));
_profile.State = Convert.ToString(_userProfile.GetPropertyValue("State"));
_profile.Zip = Convert.ToString(_userProfile.GetPropertyValue("Zip"));
}
return View(_profile);
}
This worked fine when State was a string passed in TextBox, and then saved with Edit post method.
[HttpPost]
public ActionResult Edit(ProfileModel model)
{
if (ModelState.IsValid)
{
ProfileBase profile = ProfileBase.Create(System.Web.HttpContext.Current.Session["_userName"].ToString(), true);
if (profile != null)
{
profile.SetPropertyValue("FirstName", model.FirstName);
profile.SetPropertyValue("LastName", model.LastName);
profile.SetPropertyValue("Address", model.Address);
profile.SetPropertyValue("City", model.City);
profile.SetPropertyValue("State", model.State);
profile.SetPropertyValue("Zip", model.Zip);
profile.Save();
}
else
{
ModelState.AddModelError("", "Error writing to Profile");
}
}
return RedirectToAction("Index");
}
This is how I created dropdown for State.
Model:
public class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public IEnumerable<RegisterModel> RegModel { get; set; }
public IEnumerable<ProfileModel> Profiles { get; set; }
}
Controller:
ViewBag.StateID = new SelectList(db.States, "StateID", "StateName");
View:
#Html.DropDownList("StateID", (SelectList)ViewBag.StateID, new { #class = "dropdown" })
I've tried several things. No luck so far. What am I missing?!