How to join my tables with identity tables? - asp.net

I started a default MVC project with Identity and EF.
In my app users will be able to create and edit some records.
In the table for these records, I want to have the ids of users who created the record and who updated lastly.
My model class is like:
public class Record
{
public int ID { get; set; }
public DateTime CreateTime { get; set; }
public string CreatingUserID { get; set; }
public string UpdatingUserID { get; set; }
public DateTime UpdateTime { get; set; }
public Enums.RecordStatus Status { get; set; }
}
And in RecordsController, I save new records to db like this:
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(FormCollection form, RecordCreateVM vm)
{
string userId = User.Identity.GetUserId();
DateTime now = DateTime.Now;
Record rec = new Record ();
if (ModelState.IsValid)
{
int newRecordId;
using (RecordRepository wr = new RecordRepository())
{
UpdateModel(rec);
rec.CreateTime = now;
rec.UpdateTime = now;
rec.CreatingUserID = userId;
rec.UpdatingUserID = userId;
rec.Status = Enums.RecordStatus.Active;
Record result = wr.Add(rec);
wr.SaveChanges();
newRecordId = result.ID;
}
}
}
When I am listing these records, I also want my page to display these users' usernames.
I get all the active records from the repository I created.
public ActionResult Index()
{
RecordListVMviewModel = new RecordListVM();
using (RecordRepository wr = new (RecordRepository())
{
viewModel.Records = wr.GetAll();
}
return View(viewModel);
}
And this is the repository code:
public class RecordRepository: Repository<Record>
{
public override List<Record> GetAll()
{
IQueryable<Record> activeRecords = DbSet.Where(w => w.Status == Enums.RecordStatus.Active);
return activeRecords.ToList();
}
}
Where do I have to make changes? Can you give me an sample code for usages like this?
Thank you.

You need to change
public string CreatingUserID { get; set; }
public string UpdatingUserID { get; set; }
to something like:
public User CreatingUser { get; set; }
public User UpdatingUser { get; set; }
Set the ID's during the creation of new RecordRepository()
Then access them as Record.CreatingUser.FirstName ect

Related

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId"

I'm getting this error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ConsoleUserInfoes_dbo.ConsolesCheckBoxes_consoleId". The conflict occurred in database "aspnet-ForePlay-20180525122039", table "dbo.ConsolesCheckBoxes", column 'ConsoleId'.
I'm using Entity Framework and ASP.NET MVC 5 and IdentityUser and try to insert data form checkListBox to table into my database.
This is happening on the register view, when user need to register and fill the form.
public class ConsoleUserInfo
{
[Key]
public int identity { get; set; }
[Required]
[StringLength(255)]
[ForeignKey("User")]
public string userid { get; set; }
[Required]
[ForeignKey("consolesCheckBox")]
public int consoleId { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ConsolesCheckBox consolesCheckBox { get; set; }
}
This is the table that need to get a user id (form applictionUser) and consoleId
(form ConsolesCheckBox )
This is the ApplicationUserUser model class:
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(255)]
override
public string UserName { get; set; }
[Required]
[StringLength(50)]
public string Phone { get; set; }
public byte[] UserPhoto { get; set; }
public virtual UserAddress Address { get; set; }
public virtual ICollection<ConsolesCheckBox> consoleCheckBox { get; set; }
}
and this is the checkBoxList table:
public class ConsolesCheckBox
{
[Key]
public int ConsoleId { get; set; }
public string ConsoleName { get; set; }
public bool IsChecked { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
}
This is my account controller, all in the register get and post
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//using database
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
//data will save list of the consoleCheckBoxItem
var data = dbo.consolesCheckBox.ToList();
// because the view is request a common model, we will create new one
CommenModel a = new CommenModel();
a.ConsolesCheckBoxList = data;
// we will need to return common model, that way we will return a
return View(a);
}
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register([Bind(Exclude = "UserPhoto")]CommenModel model)
{
if (ModelState.IsValid)
{
// To convert the user uploaded Photo as Byte Array before save to DB
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase poImgFile = Request.Files["UserPhoto"];
using (var binary = new BinaryReader(poImgFile.InputStream))
{
imageData = binary.ReadBytes(poImgFile.ContentLength);
}
}
var user = new ApplicationUser
{
UserName = model.registerViewModel.Email,
Email = model.registerViewModel.Email,
Phone = model.registerViewModel.Phone
};
user.UserPhoto = imageData;
var result = await UserManager.CreateAsync(user, model.registerViewModel.Password);
//after the user create, we will use the id and add the id to the userAddress table include
// Address, longitude and latitude.
using (ApplicationDbContext dbo = new ApplicationDbContext())
{
var currentUserId = user.Id;
var pasinfo = dbo.userAddress.FirstOrDefault(d => d.Userid == currentUserId);
if (pasinfo == null)
{
pasinfo = dbo.userAddress.Create();
pasinfo.Userid = currentUserId;
dbo.userAddress.Add(pasinfo);
}
pasinfo.Address = model.useraddress.Address;
pasinfo.latitude = model.useraddress.latitude;
pasinfo.longitude = model.useraddress.longitude;
dbo.SaveChanges();
foreach (var item in model.ConsolesCheckBoxList.Where(x => x.IsChecked).Select(x => x.ConsoleId))
{
var consoleUserInfo = new ConsoleUserInfo
{
userid = currentUserId,
consoleId = item
};
dbo.consoleUserInfo.Add(consoleUserInfo);
}
dbo.SaveChanges();
}
}
}
In the register GET I have a common model, because I used 3 models in the view
this is the common model:
public class CommonModel
{
public UserAddress useraddress { get; set; }
public RegisterViewModel registerViewModel { get; set; }
public List<ConsolesCheckBox> ConsolesCheckBoxList { get; set; }
}
I need your help here, I've been trying to fix this all day.

Created and Modified date issue

I was practicing User.Identity and timestamps functions in ASP.NET MVC 5,
So I created a student class filled some properties, I just wanted to test if it is capturing timestamps and userId, so user id is getting captured and datetime too, problem is whenever I'm editing a record and save it, its created date becomes Null and modified date is updated, please review the code and help.
Thanks in advance.
Below is the Code
{
public class BaseEntity
{
public DateTime? DateCreated { get; set; }
public string UserCreated { get; set; }
public DateTime? DateModified { get; set; }
public string UserModified { get; set; }
}
public class Student : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Subject { get; set; }
public string Class { get; set; }
public Section Section { get; set; }
public byte SectionId { get; set; }
}
then I used Codefirst approach and created an application Database and added this code in Identity Model
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Student> Students { get; set; }
public override int SaveChanges()
{
AddTimestamps();
return base.SaveChanges();
}
//public override async Task<int> SaveChangesAsync()
//{
// AddTimestamps();
// return await base.SaveChangesAsync();
//}
private void AddTimestamps()
{
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));
var currentUsername = !string.IsNullOrEmpty(System.Web.HttpContext.Current?.User?.Identity?.Name)
? HttpContext.Current.User.Identity.Name
: "Anonymous";
foreach (var entity in entities)
{
if (entity.State == EntityState.Added)
{
((BaseEntity)entity.Entity).DateCreated = DateTime.UtcNow;
((BaseEntity)entity.Entity).UserCreated = currentUsername;
}
else
((BaseEntity)entity.Entity).DateModified = DateTime.UtcNow;
((BaseEntity)entity.Entity).UserModified = currentUsername;
}
}
public DbSet<Section> Sections { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I have created a simple controller with create,edit and dispay actions.
The code you posted doesn't show DateCreated being set to null as far as I can see. I think the issue is when you save an existing record you do not have the DateCreated or UserCreated fields in your view. So when you post the form the MVC model binder doesn't see them and thus sets them to null (I'm assuming your are binding to the Student model in your controller action).
In your edit view add the following hidden fields:
#Html.HiddenFor(model => model.DateCreated)
#Html.HiddenFor(model => model.UserCreated)
Now when you post the form the MVC model binder will bind these values to your model and save them to the database.

MVC ASP.NET Entity Framework Not Saving a List of Assocciated Objects

This question is in reference to the project discussed here. After resolving the previous problem I have run into a new one. When The Student object is saved, the list of courses associated with it is not saved. I can see the collection of course objects when I mouse over the student object after setting a breakpoint:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddCourseVM (AddCourseViewModel vModel)
{
Student stu = db.Students.Find(vModel.Student.ID);
foreach (Course c in vModel.PossibleCourses)
{
if (c.Selected)
{
BaseCourse bc = db.BaseCourses.Find(c.BaseCourse.ID);
c.BaseCourse = bc;
c.Student = stu;
stu.CoursesTaken.Add(c);
}
}
if (stu != null)
{
db.Entry(stu).State = EntityState.Modified; //breakpoint here
db.SaveChanges();
}
return RedirectToAction("ListTakenCourses", stu);
}
public ActionResult ListTakenCourses (Student stu)
{
List<Course> taken = stu.CoursesTaken.ToList();
foreach (Course c in taken)
{
c.BaseCourse = db.BaseCourses.Find(c.BaseCourse.ID);
}
ViewBag.CoursesTaken = taken;
return View(stu);
}
But when I pass the object to the next method, the list of courses taken comes back null. The courses are being saved to the database, I can see them when I go into the SQL Server explorer, but for some reason they are not being attached to the student object. The code for the objects:
public class Student
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string WNumber { get; set; }
public int HoursCompleted { get; set; }
public double GPA { get; set; }
public Concentration StudentConcentration { get; set; }
public virtual ICollection<Course> CoursesTaken { get; set; }
public virtual ICollection<Course> CoursesRecommended { get; set; }
}
and:
public class Course
{
public int ID { get; set; }
public string Semester { get; set; }
public Grade? Grade { get; set; }
public bool Selected { get; set; }
public BaseCourse BaseCourse { get; set; }
public Student Student { get; set; }
}
Something that may be important, but that I don't really understand: when I look at the table for the Course object in the database, there are three columns, called Student_ID, Student_ID1, and Student_ID2. I assume they relate to the student associated with the object and the two ways it can be associated (recommended or taken), but the odd thing is that Student_ID is always null, while the other two sometimes have a value and sometimes do not. I have not even begun to implement the recommendation process, so there is no way that list is being filled.
I reworked the classes and now it seems to be working. I changed the Course object to:
public class Course
{
public int ID { get; set; }
public string Semester { get; set; }
public Grade? Grade { get; set; }
public bool Selected { get; set; }
public int BaseCourseID { get; set; }
public int StudentID { get; set; }
public BaseCourse BaseCourse { get; set; }
public Student Student { get; set; }
}
and the controller methods to:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddCourseVM (AddCourseViewModel vModel)
{
Student stu = db.Students.Find(vModel.Student.ID);
foreach (Course c in vModel.PossibleCourses)
{
if (c.Selected)
{
BaseCourse bc = db.BaseCourses.Find(c.BaseCourse.ID);
c.BaseCourse = bc;
c.Student = stu;
stu.CoursesTaken.Add(c);
db.Entry(c).State = EntityState.Added;
}
}
if (stu != null)
{
db.Entry(stu).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("ListTakenCourses", stu);
}
public ActionResult ListTakenCourses (Student stu)
{
List<Course> taken = db.Courses.Where(c => c.StudentID == stu.ID).ToList();
foreach (Course c in taken)
{
c.BaseCourse = db.BaseCourses.Find(c.BaseCourseID);
c.Student = stu;
stu.CoursesTaken.Add(c);
}
ViewBag.CoursesTaken = taken;
return View(stu);
}
And it is now displaying the courses I add on the next page, but it seems odd that I have to save the child objects separately from the parent and that I have to get the list from the database manually instead of being able to use the object structure. Is this intended behavior, or is there a better way of doing what I'm trying to do (add a list of child objects (courses) to a student object, save the relationship to the database, and then display the list of added objects)?
You are not "passing the object to the next method". You are serializing the object and passing it on the URL, then deserializing it on the other end with this method:
return RedirectToAction("ListTakenCourses", stu);
This is not the way to go about things. What you should be doing is passing a single id, such as the student id. Then, in ListTakenCourses you look up the student again in the database, which if you are doing your query correctly will fully populate the objects.
return RedirectToAction("ListTakenCourses", new { id = stu.StudentID });
public ActionResult ListTakenCourses (int id)
{
List<Course> taken = db.Courses.Where(c => c.StudentID == id).ToList();
//...
}

asp.net mvc querying from different table to view

I need to query data from 2 table
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
}
and
public class PrivateMessage
{
public int MessageId { get; set; }
public string Sender { get; set; }
public string Receiver { get; set; }
public string Subject { get; set; }
public string Message { get; set; }
private DateTime _date = DateTime.Now;
public DateTime sentDate { get { return _date; } set { _date = value; } }
}
and this what i tried on my controller
public ActionResult Index()
{
var x = User.Identity.Name;
var query = from p in db.PrivateMessages
join u in db.UserProfiles on p.Sender equals u.UserName
where p.Receiver == x
select new
{
u.UserName,
u.Name,
p.Receiver,
p.Subject,
p.Message,
p.sentDate
};
return View(query);
}
this is my view model
#model IEnumerable<SeedSimple.Models.PrivateMessage>
but i got this error
The model item passed into the dictionary is of type
'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType95[System.String,System.String,System.String,System.String,System.DateTime]]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[SeedSimple.Models.PrivateMessage]'.
all i want is to get username and name from UserProfile table and receiver, subject, message and sentDate on PrivateMessage table
Well you're passing as a Model an anonymous type yet you have a strongly typed View.
You can either create a new ViewModel that contains all the fields you're using for your query and pass that, or you can pass all the properties in the ViewBag (not a pretty solution).
EDIT
Thought I'd give you an example.
Here is a ViewModel containing the data you need:
public class MessageViewModel
{
public string UserName { get; set; }
public string Name { get; set; }
public string Receiver { get; set; }
public string Subject { get; set; }
public string Message { get; set; }
public DateTime SentDate { get; set; }
}
In your view:
#model IEnumerable<SeedSimple.Models.MessageViewModel>
In your Controller:
public ActionResult Index()
{
var x = User.Identity.Name;
var result = from p in db.PrivateMessages
join u in db.UserProfiles on p.Sender equals u.UserName
where p.Receiver == x
select new MessageViewModel
{
UserName = u.UserName,
Name = u.Name,
Receiver = p.Receiver,
Subject = p.Subject,
Message = p.Message,
SentDate = p.sentDate
};
return View(result);
}
I hope this helps.

How do you do lookup tables via code first

I've been searching this without any luck on how to resolve. I have a list of available departments that can be used within my stores. Since stores vary, some departments may not exist and I want to keep track of how much shelving space each department has for each store. What's the best way to create this?
Here's my model:
public class Store
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; } //StoreNumber
public virtual List<StoreDepartment> StoreDepartments { get; set; }
}
public class StoreDepartment
{
[Key]
public int ID { get; set; }
public int StoreID { get; set; }
public Department Department { get; set; }
public int ShelvingLinealFT { get; set; }
}
public class Department
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; } //DepartmentNumber
public string Name { get; set; }
public bool InActive { get; set; }
}
I've already populated my Department tables, but when I attempt to save a StoreDepartment object, I get an error stating that it can't insert a row since its trying to create a duplicate key. It's like it's trying to create a new record.
Any help would be appreciated.
Here's the code for my DbContext:
public class StoresRepository:DbContext
{
public DbSet<Store> Stores { get; set; }
public DbSet<StoreDepartment> StoreDepartments { get; set; }
public DbSet<Department> Departments { get; set; }
}
Here is my Save method:
/// <summary>
/// Saves a StoreDepartment Object to the store("dept.storeid")
/// Adds a new record if ID is 0
/// </summary>
/// <param name="dept"></param>
/// <returns></returns>
public bool Save(StoreDepartment dept)
{
bool retval = false;
try
{
using (var db = new StoresRepository())
{
if (dept.ID.Equals(0))
{
//Add Store Department
db.StoreDepartments.Add(dept);
}
else
{
//this is an update
StoreDepartment department = db.StoreDepartments.Where(p => p.ID.Equals(dept.ID)).FirstOrDefault();
department.Department = dept.Department;
department.ShelvingLinealFT = dept.ShelvingLinealFT;
}
int rowsupdated = db.SaveChanges();
retval = true;
}
}
catch (Exception ex)
{
Utils.Trace(string.Format("StoresContext.cs: StoreDepartments.Save(). ID:{1}. Exception: {0}", ex, dept.ID), Utils.ErrorTypes.Error);
}
return retval;
}
You probably change the state of the Department to added when you add the StoreDepartment object. Something like this:
using(var db = new MyContext())
{
var storeDepartment = new StoreDepartment();
storeDepartment.StoreId = storeId;
storeDeparemtent.Department = department;
db.StoreDepartments.Add(storeDepartment); // also marks Department as added
db.SaveChanges();
}
The solution is to move up the line where you add the object:
using(var db = new MyContext())
{
var storeDepartment = new StoreDepartment();
db.StoreDepartments.Add(storeDepartment);
storeDepartment.StoreId = storeId;
....
}
You can also add a DepartmentId to the StoreDepartment class and set its value, as you do with StoreId. Together with the Department property this is called a foreign key association.
I figured it out.
Here are the correct models:
public class StoreDepartment
{
[Key]
public int ID { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
public int ShelvingFootage { get; set; }
}
public class Department
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public virtual ICollection<StoreDepartment> StoreDepartments { get; set; }
}
Using Affluent API, I setup my relationships as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StoreDepartment>().HasRequired(d => d.Department);
modelBuilder.Entity<Department>().HasMany(d => d.StoreDepartments);
}
Once I had this setup, one of the main issues I had was with populating the object.
Normally, you would do the following:
StoreDepartment sd = new StoreDepartment();
sd.Department = new Department(){
ID = 302,
Name = "Deli"
};
sd.ShelvingFootage = 100;
However when trying to save this object, Entity would attempt to add a new record in the Department table which of course would throw an exception due to a violation in the primary key.
The trick was to not update this directly and to build my StoreDepartment object as follows:
StoreDepartment sd = new StoreDepartment();
sd.DepartmentID = 302;
sd.ShelvingFootage = 100;
By doing this, you are only updating the foreign key for the StoreDepartment.Department object.

Resources