add a list to the database - asp.net-mvc

Well im doing a application where i have a model like this
public class Saldo
{
public Saldo()
{
Expenses = new List<Expese>();
Incomes = new List<Income>();
}
public int SaldoId { get; set; }
public List<Expense> Despesas { get; set; }
public List<Income> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
what i want to do is add a single expense to the list but it is not working, when i output on the console the count it is always null
in the controller im doing this:
public ActionResult Create([Bind(Include = "ExpenseId,TipoDespesaId,DespesaDescricao,DespesaValor,TipoPagamentoId,Data,Comentario")] Expense expense)
{
var userId = User.Identity.GetUserId();
if (ModelState.IsValid)
{
var balance = db.Balance.Where(d => d.ApplicationUserId == userId).FirstOrDefault();
expense.ApplicationUserId = userId;
if (balance == null)
{
Balance s = new Balance();
s.Expense.Add(expense);
s.ApplicationUserId = userId;
db.Balance.Add(s);
}
else
{
Balance.Expense.Add(expense);
}
db.Expense.Add(expense);
db.SaveChanges();
return RedirectToAction("Index");
}
The expense comes from the form and is passed to the Action
I created a new instance of Balance it worked adding the ApplicationUserId but the expense on the list didnt work, can someone explain me why it happen?
Ps: Sorry for my bad english

Perhaps it's just lost in the translation, but your model doesn't make sense. You have a list of Expense called "Despesas" but in the constructor you call it "Expenses" Whatever that list is called is what you need to add your expense object to. Instead of
s.Expense.Add(expense);
it would be
s.Despesas.Add(expense);

Related

Two ViewModel Tuple

I have two viewmodels. One displays employee information and the other displays information about any emergency contacts added to the employee (1-to-Many).
I am managing to display their information separately, but for some reason when I try to combine them in one view things go wrong.
At first I thought the best approach to containing all of that information in one view would be to just create 1 ViewModel and add all of the required fields. But then when I added emergency contact fields which are the "many" part of the relationship, I wasn't sure how to go about iterating through them inside same returned model (1-to-many). Because of that I tried to attempt creating a combined view using Tuple<>. Sadly, this didn't go to well either. Any help greatly appreciated.
Model Class: PersonInfoViewModel
public class PersonInfoViewModel
{
public int PersonId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Surname")]
public string LastName { get; set; }
}
Model Class: EmergencyContactViewModel
public class EmergencyContactViewModel
{
public int EmergencyContactId { get; set; }
[Display(Name = "First Name")]
public string EmergencyContactFirstName { get; set; }
[Display(Name = "Surname")]
public string EmergencyContactLastName { get; set; }
}
Model Class: CombinedView
public class CombinedView
{
public Person PersonC { get; set; }
public List<EmergencyContact> EmergencyContactsList { get; set; }
}
Controller Action: CombinedView
public ActionResult CombinedView(int id)
{
var person = _context.People.Find(id);
ViewBag.PersonId = _context.People.Find(id);
if (person != null)
{
List<EmergencyContactViewModel> ec = new List<EmergencyContactViewModel>(id);
PersonInfoViewModel pi = new PersonInfoViewModel();
return View(Tuple.Create(pi, ec));
}
return View();
}
Controller Action: PersonInfo
public async Task<ActionResult> PersonInfoViewModel(int? id)
{
var person = await db.People.FindAsync(id);// pull record from DB by id
if (person != null)
return View(new PersonInfoViewModel()
{
FirstName = person.FirstName,
LastName = person.LastName
});
return View();
}
Controller Action: EmergencyContactViewModel
public ActionResult EmergencyContactViewModel(int? id)
{
List<EmergencyContact> emergecyContactsList = db.EmergencyContacts.ToList();
List<EmergencyContactViewModel> personVmList = emergecyContactsList.Select(x =>
new EmergencyContactViewModel
{
EmergencyContactId = x.EmergencyContactId,
EmergencyContactLastName = x.EmergencyContactLastName,
EmergencyContactFirstName = x.EmergencyContactFirstName,
}).ToList();
return View(personVmList);
}
So yeah, the answer was quite simple. As Stephen has mentioned above, all I had to do was return an instance of CombinedView. In case someone else is stuck like me, this might help you:
public ActionResult CombinedView(int id)
{
var person = _context.People.Find(id);
ViewBag.PersonId = _context.People.Find(id);
if (person != null)
{
return View(new CombinedView()
{
PersonC = person,
EmergencyContactsList = new List<EmergencyContact>(person.EmergencyContacts)
});
}
return View();
}
Also, in the CombinedView:
#model [appname].Models.CombinedView
Additionally, it's worth noting I've tried to do repeat the same steps, but this time with the actual ViewModels as mentioned in the title of this question, but so far have failed.
public ActionResult CombinedView2(int id)
{
var person = _context.People.Find(id);
if (person != null)
{
return View(new CombinedView2()
{
PersonInfo = new PersonInfoViewModel(),
EmergencyContactList = new List<EmergencyContactViewModel>()
});
}
return View();
}

Do we need to get objects from db to create/update related data?

There is an article about how to update related data with the Entity Framework in an ASP.NET MVC application. It implements a simple University in which you can choose your courses as an instructor.
Here is simplified version for its courses controllers:
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
var selectedCoursesHS = new HashSet<string>(selectedCourses);
var instructorCourses = new HashSet<int>
(instructorToUpdate.Courses.Select(c => c.CourseID));
foreach (var course in db.Courses)
{
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedCourses)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.Courses)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
//Log the error
}
}
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
As you can see UpdateInstructorCourses fills instructorToUpdate.Courses with objects retrieved from db.Courses based on selectedCourses which is a string array.
So, is it the only way to create many-to-many relationships? Do we need to get objects from db and then Add them to our List member? Isn't it better to only pass related object's Id and update related data?
So, for many-to-many mappings, there are two ways to do it in EF:
1) Two ICollection properties (which you are already using):
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<Course> Courses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<Instructor> Instructors { get; set; }
}
In this case, EF will generate/use a mapping table, such as:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[Instructor_Courses_Mapping]([Id],[InstructorId],[CoursesId])
Now, as you pointed out, there's no way to get a list from that mapping table, making you load the collection into memory using a navigation property. So:
2) However, you can override EF's mapping-table generation with your own custom mapping entity:
public class Instructor
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class Course
{
public Int32 Id { get; set; }
public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class InstructorCourse
{
public Int32 Id { get; set; }
public Int32 InstructorId { get; set; }
public Instructor Instructor { get; set; }
public Int32 CourseId { get; set; }
public Course Course { get; set; }
}
Now, EF will generate these tables:
CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[InstructorCourses]([Id],[InstructorId],[CourseId])
This will allow you to then query for InstructorCourses using a dbContext:
var instructorCourses = dbContext.Set<InstructorCourse>().Where( c => c.InstructorId == instructorId ).ToList()
That will return you a list of InstructorCourse objects, with all InstructorId values matching the one your looking for. Then, if you wanted to add/remove mappings:
//add item
dbContext.Set<InstructorCourse>().Add(new InstructorCourse()
{
InstructorId = instructorId,
CourseId = courseId
});
dbContext.SaveChanges();
//remove item
var itemToRemove = dbContext.Set<InstructorCourse>().FirstOrDefault( c => c.InstructorId == instructorId && c.CourseId == courseId);
dbContext.Set<InstructorCourse>().Remove(itemToRemove);
dbContext.SaveChanges();
I found this method to be much more cleaner, represents your database structure more clearer, but it does make nested Linq statements more complicated, and nulls (without proper foreign key restrictions) potentially more common.

Getting Error : A circular reference was detected while serializing an object of type 'Abc.Models.ProductItem'

I'm going for the edit data by ProuctId.i have 2 table like Product and productitems.so i m click on edit go for the id by getting data but that time i m fetch data by id in the product table is getting propare but after going for the list productitems data getting like this error.
this is my class ProductItmes:
[Table("ProductItems ")]
public class ProductItems
{
[Key]
public int id { get; set; }
[ForeignKey("Products")]
public int pid {get; set;}
public int Qty { get; set; }
public Decimal Rate { get; set; }
public virtual Products Products { get; set; }
}
this is my api method:
public ActionResult GetProductByid(int id)
{
var Pro = db.Product.Find(id);
var ProItemList = db.Promotion_ctc.Where(x => x.pid == Pro.id).ToList();//here i am getting list of items by productid
var Details = new
{
Pro.id,
Pro.name,
Pro.image_url,
ProItemList
};
return Json(new { data = Details });
}
idk where is my problem any one know please let me know.
When working with MVC and Entity Framework, there are cases where we make our child entities reference the parent, like you did, by declaring this property here:
public virtual Products Products { get; set; }
it's ok for entity framework, but it's not when you try to serialize this.
What's going on:
The serializer will try to serialize the parent, which has a collection of ProductItem.
The serializer tries to serialize each child.
The child has a reference to parent, so the serializer tries to serialize the parent again.
Infinite loop.
That's why people use ViewModels. Instead of just returning your entities from your action, project them into a view model, and return it. Actually, you're returning an anonymous object containing a ProItemList, which I'd guess it's a List of ProductItems. Just create a view model for it:
public class ProductItemViewModel
{
public int ItemId { get; set; }
public int ProductId {get; set;}
public int Qty { get; set; }
public Decimal Rate { get; set; }
// public virtual Products Products { get; set; } NO PRODUCT HERE
}
...then fix your action to return a List of ProductItemViewModel, instead of returning directly ProductItems, like this:
var ProItemList = db.Promotion_ctc.Where(x => x.pid == Pro.id)
.Select(i => new ProductItemViewModel
{
ItemId = i.ItemId,
ProductId = i.ProductId,
Qty = i.Qty,
Rate = i.Rate
})
.ToList();
var Details = new
{
Pro.id,
Pro.name,
Pro.image_url,
ProItemList
};
return Json(new { data = Details });
}
send only required columns to ui
like this
var passToUi = from s in listResult
select new { s.Id, s.Name };
return Json(passToUi, JsonRequestBehavior.AllowGet);

Entity framework will replace a newly added record , with a record created inside the action method

I want to implement this simple scenario ,which I though EF will support out of the box.
I have a parent record named (Skill) and I am adding child records named (LinktoKB) to it. Now after adding a new LinktoKB, I want to return a view containing the up-to-date list of LinkToKBs (inclusing the newly added one).
Now my Post action method to add new LinktoKB is :-
[HttpPost]
[ValidateAntiForgeryToken]
[CheckUserPermissions(Action = "Edit", Model = "Skill")]
public async Task<ActionResult> AddKBLink(AssignKBLinksToSkill assignkblinkToSkill)
{
try
{
if (assignkblinkToSkill.LinkToKB == null)
{
return HttpNotFound();
}
if (ModelState.IsValid)
{
unitofwork.SkillRepository.AddKBLinkToSkill(assignkblinkToSkill, unitofwork.StaffRepository.GetLoginUserName(User.Identity.Name));
await unitofwork.Save();
//i have removed the values from the model state to prevent showing validation error "that the URL and name is required after succfully adding a new link"
// also to show the modified values and not the binded values
string oldlinkURL = assignkblinkToSkill.LinkToKB.URL;
ModelState.Clear();
var skillAfterAddingKBLink = await unitofwork.SkillRepository.FindSkill(assignkblinkToSkill.Skillid, r => r.LinkToKBs);
assignkblinkToSkill.LinktoKBList = skillAfterAddingKBLink.LinkToKBs.ToList(); //get the new lsit from DB after addign the new link
assignkblinkToSkill.LinkToKB.URL = "http://";//reset the values , so that user will not get old vlues
assignkblinkToSkill.LinkToKB.Name = String.Empty;
if (Request.IsAjaxRequest())
{
TempData["Partialmessage"] = string.Format("{0} URL have been Added", oldlinkURL);
return PartialView("AddKBLink", assignkblinkToSkill);
}
TempData["message"] = string.Format("{0} URL have been Added", oldlinkURL);
return View("AddKBLink", assignkblinkToSkill);
}
}
And my repository methods are:-
public async Task<Skill> FindSkill(int id, params Expression<Func<Skill, object>>[] includeProperties)
{
var query = context.Skills.AsQueryable();
if (includeProperties != null || includeProperties.Count() != 0 || includeProperties[0].Name == "0")
query = includeProperties.Aggregate(query, (current, include) => current.Include(include));
return await query.SingleOrDefaultAsync(a => a.SkillID == id);
}
&
public void AddKBLinkToSkill(AssignKBLinksToSkill assignKBLinkToSkill,string username)
{
var skill = context.Skills.SingleOrDefault(a=>a.SkillID == assignKBLinkToSkill.Skillid);
skill.LinkToKBs.Add(assignKBLinkToSkill.LinkToKB);
skill.Modified = System.DateTime.Now;
skill.ModifiedBy = staffrepo.GetUserIdByUserName(username);
context.Entry(skill).State = EntityState.Modified;
}
Currently I am getting a very strange behavior is that , the list that is returned to the view will not contain the newly added LinkToKB value and it will be replaced by the following value:-
assignkblinkToSkill.LinkToKB.URL = "http://"
so can anyone advice on this please, although I am explicitly retrieving the LinkToKB list from database?
visual studio will how the following at two different stages:-
First this is the newly added LinkToKB:-
Second EF have replace it with the value inside the action method:-
I spend the whole day trying to understand what is going on ... and if i removed these lines:-
assignkblinkToSkill.LinkToKB.URL = "http://";//reset the values , so that user will not get old vlues
assignkblinkToSkill.LinkToKB.Name = String.Empty;
i will get the new up-to-date list correctly (but i need them)..
I have two model classes (Skill & LinktoKB):-
public partial class Skill
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Skill()
{
this.SkillLevels = new HashSet<SkillLevel>();
this.SkillLevelStaffs = new HashSet<SkillLevelStaff>();
this.Customers = new HashSet<Customer>();
this.LinkToKBs = new HashSet<LinkToKB>();
this.SkillVersionHistories = new HashSet<SkillVersionHistory>();
this.Skill1 = new HashSet<Skill>();
this.Skills = new HashSet<Skill>();
}
public int SkillID { get; set; }
public string Name { get; set; }
//code goes here
public virtual SkillStatu SkillStatu { get; set; }
public virtual SkillType SkillType { get; set; }
public virtual ICollection<LinkToKB> LinkToKBs { get; set; }
}
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public LinkToKB()
{
this.Skills = new HashSet<Skill>();
}
public int LinkToKBID { get; set; }
public string URL { get; set; }
public string Name { get; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
and the following viewModel class:-
public class AssignKBLinksToSkill
{
public ICollection<LinkToKB> LinktoKBList { set; get; }
public LinkToKB LinkToKB { set; get; }
public int Skillid { set; get; }
}
In your code there's always one assignkblinkToSkill.LinkToKB instance. When it enters the method it's got some value that you store in the database. Later you re-assign its value to be "http://".
But this is still the instance that you added to the list skillAfterAddingKBLink.LinkToKBs!
You only have to create a new instance in the view model:
assignkblinkToSkill.LinkToKB = new LinkToKB();
assignkblinkToSkill.LinkToKB.URL = "http://";

Entity Framework Create and Update in List

I have an entity that I am updating and the method in the controller has these lines in it;
db.Entry(userdetails).State = EntityState.Modified;
try {
db.SaveChanges();
}
I think this is fine, however the entity has a collection in it and these records needs to be created, not updated. I am therefore getting this error;
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
I have read that this may be because my AppUserInfo objects have an id of 0 because they need to be added.
The class for the outer entity looks like this;
public class User {
public Guid UserId { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<AppUserInfo> InfoList { get; set; }
}
Now in this situation for example, the phone number could have been modified, and the 'InfoList' collection has new items in that need to be created. Some may need to be updated as well. The 'AppUserInfo' class looks like this;
public class AppUserInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AppUserInfoId { get; set; }
public string info { get; set; }
}
How do I handle this in EF? Am I allowed to save collections in this way? I do not know how to say that there are modifications and additions and possible deletions in the list to EF. Do I simply just remove the line;
db.Entry(userdetails).State = EntityState.Modified;
Any info on the correct way to do this would be very helpful.
Thanks,
db.Entry(userdetails).State = EntityState.Modified;
This line of code is used for editing your objects, if you have no trouble creating your User then all you need is another controller to handle AppUserInfo
Lets imagine that you have a AppUserInfo controller, and you have a AppUserInfo viewModel somewhere. Then you can somthing like the following to accomplish what you want to do.
Your view model like this:
puclic class AppUserInfoCreateViewModels
{
public Guid UserId { get; set; }
public string Info { get; set; }
}
Then your Controller like this:
public class AppUserInfoController : Controller
{
private readonly IYourDataSource _db;
public AppUserInfoController(IYourDataSource db)
{
_db = db;
}
[HttpGet]
public ActionResult Create(int userId)
{
var model = new AppUserInfoCreateViewModels();
model.UserId = userId;
return View(model);
}
[HttpPost]
public ActionResult Create(AppUserInfoCreateViewModels viewModel)
{
if(ModelState.IsValid)
{
var user = _db.Users.Single(d => d.UserId == viewModel.UserId);
var appUserInfo= new AppUserInfo();
appUserInfo.Info= viewModel.Infor;
user.AppUserInfos.Add(appUserInfo);
_db.Save();
return RedirectToAction("detail", "user", new {id = viewModel.UserId});
}
return View(viewModel);
}
}
I hope this helps, ask any questions you may have

Resources