MVC An entity object cannot be referenced by multiple instances of IEntityChangeTracker - sql

I've got a model that represents a joint table (with payload) in my database:
public class UserHasCar
{
// Foreign keys
[Key, Column(Order = 0)]
public string ApplicationUserId { get; set; }
[Key, Column(Order = 1)]
public int CarId { get; set; }
// Navigation properties
[Required]
public virtual ApplicationUser ApplicationUser { get; set; }
[Required]
public virtual Car Car{ get; set; }
// Additional fields
public int YearsRidden { get; set; }
}
public class Car
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
public class ApplicationUser : IdentityUser
{
public int BirthYear{ get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
I have a form that includes multiple select boxes, and upon submitting I want to clear out all records related to that user who submitted the form in the UserHasCar table and replace them with the new updated information. I'm getting a An entity object cannot be referenced by multiple instances of IEntityChangeTracker. because I am doing something wrong, but I don't see where I am using more than one context. This code happens in my controller:
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
public string GetUserId()
{
string id = User.Identity.GetUserId();
var user = UserManager.FindById(id);
return user.Id;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManageCars(FormCollection form)
{
string id = GetUserId();
// Remove cars records with my id from database
var queryCars = (from m in db.UserHasCars where m.ApplicationUserId == id select m).ToList();
foreach (var record in queryCars )
{
// Could the problem be here?
db.UserHasCars.Remove(record)
}
// Add user-submitted cars to the database
string carval = form["Cars[0]"];
Car car = (from m in db.Cars where m.Name == carval select m).First();
int carid = car.ID;
// I get the abovementioned title error here
db.UserHasCars.Add(
new UserHasCar()
{
ApplicationUser = GetCurrentUser(),
ApplicationUserId = id,
Car = car,
CarId = carid,
YearsRidden = 0
}
);
db.SaveChanges();
}
I've seen many SO posts, but can't seem the problem as why my code doesn't want to save the new database entries.
EDIT
The solution was to remove the call to get the user and replace it with a query. Why? I was making database conflict errors by having both types of calls (database and DataManager calls in the same controller action). I ended up using a modified GetUser() function instead of GetCurrentUser()
Code:
public ApplicationUser GetUser()
{
// As opposed to:
// UserManager.FindById(User.Identity.GetUserId())
// We make a database call to grab our user instead
// So we don't get database context conflicts by using UserManager
string id = GetUserId();
return db.Users.Where(m => m.Id == id).First();
}
public string GetUserId()
{
return User.Identity.GetUserId();
}
// snip
// in ManageCars(FormCollection form)
ApplicationUser user = GetUser();
// snip
var newRow = db.UserHasCars.Create();
newRow.ApplicationUser = user;
// snip
db.UserHasCars.Add(newRow);

Try removing this line:
ApplicationUser = GetCurrentUser(),
from your object instantiation when adding.
Entity populates this object automatically once you set the foreign key ApplicationUserId. If UserManager.FindById(User.Identity.GetUserId()) uses a different db context that's where your exception is coming from.
Also to save yourself further trouble down the line, you should always call db.SaveChanges() in between the two operations. If you're worried about the atomicity of the db operation, just wrap the whole thing in a Transaction.
And when adding new rows to a table, I usually prefer to use something like:
var newRow = db.SomeTable.Create();
newRow.SomeColumn1 = "something";
newRow.SomeColumn2 = 5;
db.SomeTable.Add(newRow);
db.SaveChanges();

In order to delete entries from UserHasCars you need to change their EntityState to Deleted.
Example:
foreach (var record in queryCars )
{
db.ObjectStateManager.ChangeObjectState(record, EntityState.Deleted);
}
Hope this will fix your issue.

Related

Get actual model type from dynamic type

I have three tables in my database (Student, Course, All). When I add any data in Student & Course table, the data also gets added in All table.
So, I'm using dynamic type data to pass Student & Course type data to get saved in All table.
public IActionResult Add(Student model)
{ ...
bool studentData = _studentService.Add(model); //saving in Student table
bool allData= _allService.AddAll(model); // passing 'Student' type data
...
}
In the All service, I've a function like-
public bool AddAll(dynamic model) // 'Student' type data passed & received as dynamic
{...}
Now, I need the details about Student model type (table name, total data found etc.).
Is there any way to get it? And is it possible to get the Student or Course model type info if I use dynamic data type?
Any suggestion or help will be much appreciated :) Thanks in advance!
If you want to get model type,you can use model.GetType(),and you can use model.GetType().Name to get model type Name,here is a demo:
Model:
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
}
Action:
public void Add(Student model)
{
//Student s = new Student { StudentId=1, StudentName="s1" };
Course c = new Course { CourseId=1,CourseName="c1" };
bool allData = AddAll(c);
}
public bool AddAll(dynamic model)
{
var type = model.GetType();
var typeName = type.Name;
return true;
}
result:
Is this example demonstrate what you trying to do ?
// Some dummy classes
public class Student
{
public string Name { get; set; }
public Student(string name)
{
Name = name;
}
}
public class Course
{
public string Title { get; set; }
public Course(string title)
{
Title = title;
}
}
// put the method somewhere
private string TypeCasting(dynamic myContent)
{
if (myContent is Student littleStudent)
{
return littleStudent.Name;
}
if (myContent is Course someLovelyCourse)
{
return someLovelyCourse.Title;
}
return string.Empty;
}
// Example using
var student = TypeCasting(new Student("fossil"));
var course = TypeCasting(new Course("type casting"));

RavenDb changes to Property (nullable) on Entity that is a class doesn't save

I have a class like this:
public class Content {
public string Id {get; set;} = "content/"
public ContentJob? Job {get; set;}
}
public class ContentJob {
public string JobId {get; set;} = string.Empty;
}
I can create the content and if the job is there it will persist. with Store/SaveChanges.
But what I can't do is update (or more accurately set) ContentJob on Content and have it detect that there was a change. (HasChange(instance) == false) and SaveChanges doesn't update the database.
Why? And how do I make it detect the change?
(incidentally I tried using C# 9 records in hopes that because it automatically does deep object equality that this would solve it and no, it doesn't)
I created an unit-test based on your question, and it works as expected.
[Fact]
public void ShouldWork()
{
using (var store = GetDocumentStore())
{
string id = string.Empty;
using (var session = store.OpenSession())
{
var c = new Content();
session.Store(c);
session.SaveChanges();
id = session.Advanced.GetDocumentId(c);
var entity = session.Load<Content>(id);
entity.Job = new ContentJob()
{
JobId = "123"
};
Assert.True(session.Advanced.HasChanged(entity));
session.SaveChanges();
}
Assert.False(string.IsNullOrEmpty(id));
using (var session = store.OpenSession())
{
var entity = session.Load<Content>(id);
Assert.NotNull(entity.Job);
Assert.Equal("123", entity.Job.JobId);
}
}
}
public class Content
{
public string Id { get; set; } = "content/";
public ContentJob? Job { get; set; }
}
public class ContentJob
{
public string JobId { get; set; } = string.Empty;
}

MVC4 passing same parameters to multiple actions on controller

I am creating an MVC4 application that has an index page that shows a grid. The index action for the page has parameters for a search string, sort order, account id, and vendor id that control what is shown.
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
}
From this main page I can click on rows in the grid, which take me to the create and edit actions. In addition, I have several other actions on the control that are called from the index page.
When I press Save or Cancel on any of these views (the [HttpPost] action, as usual they redirect to the index action. But these views don't have the search string, sort order, account id, and vendor id so that the index view can "restore" what was being shown.
So I have been adding these parameters to the other actions methods, and making an object with the properties needed to in the view models for the methods.
public class PageFeatures
{
public string SearchFilter {get; set;}
public string SortOrder {get; set;}
public int AccountId {get; set;}
public int VendorId {get; set;}
}
public class CreateViewModel
{
public PageFeatures Features { get; set; }
}
public class EditViewModel
{
public EditViewModel(int id)
{
Id=id;
}
public int Id {get; set;}
public PageFeatures Features { get; set; }
}
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
public ActionResult Create(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new CreateViewModel();
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
public ActionResult Edit( int id=0,
string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new EditViewModel(id);
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
}
But what I don't like about this approach is that I am peppering my views and every controller method with these values.
Am I doing the right thing, or is there a better way, like an action filter that would make this stuff easier and less repetitive?
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
You are using the right approach.
If you dont want your URL to be ugly filled with query string parameters. You can try storing these values in a session and retrieving them in each action of your controller. Set them once and your done to use it anywhere, if you want to change the session values you can just over write it
Have you thought about using TempData and setting these params in there. The best part is that you can persist TempData across Controllers and you can perhaps modify the TempData values in a base controller class.

RavenDb Select() downcasts instead of selecting the neccessary fields

public class PersonBrief
{
public int Id { get; set; }
public string Picture { get; set; }
public PersonBrief(Person person)
{
Id = person.Id;
Picture = person.Picture;
}
}
public class Person : PersonBrief
{
public string FullName { get; set; }
}
var results = session.Query<Person>()
.Select(x => new PersonBrief(x))
.ToList();
Assert.IsNull(results[0] as Person); // Fails
Is this a bug? If not, what would be the correct way to select only the fields i'm interested in?
It would work if you move the .ToList before the .Select, but that would be doing the work on the client.
If you want to do it on the server, you need to use As in your query, and you need a static index that does a TransformResults. See these docs.

Best pratice for completing MVC model properties which aren't bound?

I am trying to find the best way to use MVC for models which are only partially edited.
Below is an simple example.
Model
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
View
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
Controller
using System.Web.Mvc;
public class SimpleController : Controller
{
public ActionResult Edit(int id)
{ return View(Get(id)); }
[HttpPost]
public ActionResult Edit(int id, SimpleModel model)
{
if (model.Name.StartsWith("Child")) //Some test that is not done client-side.
{
Save(model);
//Get the saved data freshly.
//model = Get(id);
}
else
{
ModelState.AddModelError("", "Name should start with 'Child'");
}
//Is this the way to set the Parent property?
//var savedModel = Get(id);
//model.Parent = savedModel.Parent;
return View(model);
}
//Mock a database.
SimpleModel savedModel;
private void Save(SimpleModel model)
{ savedModel = new SimpleModel() { Id = model.Id, Name = model.Name }; }
private SimpleModel Get(int id)
{
if (savedModel == null)
{ return new SimpleModel() { Id = id, Parent = "Father", Name = "Child " + id.ToString() }; }
else
{ return new SimpleModel() { Id = savedModel.Id, Parent = "Father", Name = savedModel.Name }; }
}
}
The Name field is editable. The Parent field is only for reference and should not be updated. Therefore, it is rendered using DisplayFor.
Upon post, I receive a model with property Parent set to null. That's no problem as it will not be saved. However, when I simply return the received model to the view, the Parent field will no longer be displayed. When the model is valid, I can easily get it again from the database and thus get the Parent field's value back.
When the model is not valid, I would like to allow the user to correct input and attempt to save once more. There, I the received model's values that are input should be used, but the displayed values should be shown as well.
In reality, there are many more fields to be shown for reference, most often from different database entities than the one that is being edited.
I have seen suggestions to pass the fields as hidden fields in the view, but I feel very reluctant to read data from the client that should not be updated.
Is there a more elegant way to do this than copying these values into the model manually or passing them as hidden fields?
What about giving those un-editable properties to another model and let it take care of those properties?
View Model
public class PersonViewModel
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public PersonDetailsModel DetailsModel { get; set; }
}
Details Model
public class PersonDetailsModel
{
public string Mother { get; set; }
public string Father { get; set; }
public PersonDetailsModel() { }
public PersonDetailsModel(int personId)
{
// pull required model data from databases
var db = DBParentContext;
Mother = db.Parent.Where(m => m.ChildId == personId)
Father = db.Parent.Where(m => m.ChildId == personId)
}
}
Controller
public class PersonController : Controller
{
[HttpPost]
public ActionResult Edit(PersonViewModel viewModel)
{
viewModel.DetailsModel = new PersonDetailsModel(viewModel.Id)
if (ModelState.IsValid) {
// ~
}
return View(viewModel)
}
}
View
#model PersonViewModel
#Html.DisplayFor(m => m.DetailsModel.Mother)
#Html.DisplayFor(m => m.DetailsModel.Father)
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
Since details like your Mother are un-editable then they're not really part of the "Edit" model, so I'd box like that away and try to let something else take care of them.
If you aren't going to update the Parent field, then it really doesn't matter if it's a hidden or not, since you won't update it on post.
I would use the hidden in this case, just make sure not to update that field.