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.
Related
Im looping through all the results from the SQL query in a .Net Core project. here is Model
public class Mymessagesinfo
{
public int MyMessagesCount { get; set; }
public List<int> MessagesIDs { get; set; }
public List<int> MessagesSendersid { get; set; }
public List<string> MessagesSenders { get; set; }
public List<string> MessagesTitles { get; set; }
public List<string> MessagesInformation { get; set; }
public List<string> MessagesStatus { get; set; }
}
I loop through the users messages in my controller then i pass that model to the view
sqlcon.Open();
int? userid = HttpContext.Session.GetInt32("UserID");
SqlCommand sqlcom = new SqlCommand("select * from messages where Messagereceiver=" +userid , sqlcon);
SqlDataReader reader = sqlcom.ExecuteReader();
if(reader.HasRows)
{
int index = 0;
while(reader.Read())
{
string s;
s = reader[0].ToString();
Mymessages.MessagesIDs.Add(int.Parse(s));
Mymessages.MessagesSendersid.Add(int.Parse(reader[1].ToString()));
Mymessages.MessagesTitles.Add(reader[3].ToString());
Mymessages.MessagesInformation.Add(reader[4].ToString());
Mymessages.MessagesStatus.Add(reader[5].ToString());
index++;
}
Mymessages.MyMessagesCount = index;
}
the very first line Mymessages.MessagesIDs.Add(int.Parse(s)); it throws an exception saying System.NullReferenceException: 'Object reference not set to an instance of an object
i wanted to make sure that reader was holding the results so i added int s and checked on it and it was holding the value it was supposed to.
whats going wrong here? is this how we are supposed to pass list-like data to the view?
You need to initlize MessagesIDs in entity Mymessages, like this:
var Mymessages = new Mymessagesinfo()
{
MessagesIDs = new List<int>()
};
Mymessages.MessagesIDs.Add(id);
Or just define the class like this,
public class Mymessagesinfo
{
public int MyMessagesCount { get; set; }
public List<int> MessagesIDs { get; set; } = new List<int>();
public List<int> MessagesSendersid { get; set; } = new List<int>();
public List<string> MessagesSenders { get; set; } = new List<string>();
public List<string> MessagesTitles { get; set; } = new List<string>();
public List<string> MessagesInformation { get; set; } = new List<string>();
public List<string> MessagesStatus { get; set; } = new List<string>();
}
Here is how I would restructure what you have to make it work.
First, your model class:
public class Mymessagesinfo
{
public List<MessageInfo> Messages { get; set; } = new List<MessageInfo>();
}
public class MessageInfo
{
public int ID { get; set; }
public int Senderid { get; set; }
public string Sender { get; set; }
public string Title { get; set; }
public string Information { get; set; }
public string Status { get; set; }
}
With this approach you have a list of message objects, instead of a bunch of lists containing property data.
Here is how I would suggest you load it from SQL Server:
var data = new Mymessagesinfo();
int? userid = HttpContext.Session.GetInt32("UserID");
var messagesTable = new System.Data.DataTable("messages");
using (var sqlcom = sqlcon.CreateCommand())
{
sqlcom.CommandText = $"select * from messages where Messagereceiver='{userid}'";
using (var adapter = new SqlDataAdapter(sqcom))
{
adapter.Fill(messagesTable);
}
}
// we are now done with SQL and have the data in memory...
foreach(DataRow row in messagesTable.Rows)
{
data.Messages.Add( new MessageInfo {
ID = row.Field<int>(0),
Senderid = row.Field<int>(1),
Sender = row.Field<string>(2),
Title = row.Field<string>(3),
Information = row.Field<string>(4),
Status = row.Field<string>(5),
});
}
return View(data);
This is a lot cleaner and by using a DataAdapter and DataTable you minimize the amount of time that the connection to the database is connected.
Here is how you would use this model in an MVC View:
#Model Mymessagesinfo
<div>
<!-- This is where you can display the properties of the message. //-->
<ul>
#foreach(var message in Model.Messages)
{
<li> #message.Title - #message.Id </li>
}
<ul>
<div>
I have some troubles using sqlite provider to write integration tests.
If I attach a tree of entities to my DbContext, I get relations broken (the same thing with SqlServer provider works).
See sample for more information.
For precision, the PK of the 3 tables are Id + TenantId. TenantId is Set only in the SaveChangesAsync method, depending on the connected User.
If I want it to work on sqlite, I have to set the TenantId on the 3 objects before attaching them to the context... Why is this different with SQL Server provider ?
public class Store {
public int Id { get; set; }
public int TenantId { get; set; }
public List<Product> Products { get; set; }
}
public class Product {
public int Id { get; set; }
public int TenantId { get; set; }
public Store Store { get; set; }
public int StoreId { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
public class Category {
public int Id { get; set; }
public int TenantId { get; set; }
public List<Product> Products { get; set; }
}
[Test]
public void Test_Create(){
var store = new Store();
store.Products = new List<Product>();
var product = new Product();
product.Category = new Category();
store.Products.Add(product);
dbContext.Stores.Add(store);
// fails, product is detached from the Store after attaching to DbContext
Assert.Single(store.Products);
}
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.
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
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();
//...
}