Update a model with nested collection, ASP.NET MVC - asp.net

I'd appreciate any advice.
I've been searching for the right approach of modifying a collection, but not sure which is the best one.
I've an entity with nested collection:
public class Customer
{
//customer properties
ICollection <Address> Addresses{get; set;}
}
My Edit view for Customer includes the Addresses as well, i.e. user adds Addresses dynamically, and the whole collection is passed to the controller on form submittion.
In the controller I update the Customer as usually:
Context.Entry(customer).State = EntityState.Modified;
And also I have to update the collection of Address:
customer.Addresses.ToList()
.ForEach(p =>
Context.Entry(p).State = EntityState.Modified);
It works fine untill I add a new record to Addresses. Since it doesn't exist in DB, saving throws the error.
I know I could check if the entry exists in DB - modify, otherwise - add.
But there's a problem in Address entity. It's primary key, say ID, is Identity, i.e. auto-increments when inserted to DB.
So, initially in the collection new Address will have ID equal to 0. Then if I add one Address to the context, then before adding another one,I check if the second Address is in context, it will return true, because the first one is also with ID = 0.
Also, okay, I can drop the whole collection and add it again, but it can affect the performance.
So, I'd be so grateful for your advices.

You could iterate over the addresses and check the id. Based on the value it is possible to set the state of the entity entry to Added or Modified. In that case you don't have to recreate the collection.
foreach (var a in customer.Addresses)
{
model.Entry(a).State = a.ID == 0 ? EntityState.Added :EntityState.Modified;
}
You could use the same approach for the customer of course.

Related

Add record to table with FK

I have a table UserStoreName,
Columns are :
int Id
string UserNameId (as a FK of the table AspNetUsers (Column Id))
sring StoreName
I have a page AddStore, a very simple page where user just enter the store name into the StoreName Field.
I already know the UserNameId, i'm taking it from the User.
So when user populate the storeName field and click submit i just need to add a record to the table UserStoreName.
sounds easy.
when i click submit the AddStore function from the controller is giving me ModelState.IsValid = false.
reason for that is cause userNameId is a required field.
i want to populate that field in the AddStore
function but when we get there the modelState is already invalid because of a required field in userStoreNameId enter code here
Here is the AddStore in case it will help :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddStore(UserStoreName userStoreName)
{
userStoreName.UserNameId =
(_unitOfWork.ApplicationUser.GetAll().Where(q => q.UserName == User.Identity.Name).Select(q => q.Id)).FirstOrDefault();
userStoreName.UserName = User.Identity.Name;
userStoreName.IsAdminStore = false;
if (ModelState.IsValid)
{
_unitOfWork.UserStoreName.Add(userStoreName);
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
return View(userStoreName);
}
Any idea what am i doing wrong? new to asp.net core mvc, its my first project.
Thanks :)
Thank you
If the UserNameId field is required, it must be supplied to pass model validation.
There are two ways around this. First, you could create a View Model, with just the fields you plan on actually submitting, and use it in place of the userStoreName variable. Then in the controller action, you can just instantiate a new UserStoreName object, and fill out the fields.
Alternatively, you could pass the UserNameId variable to the view, and populate the model client side using a hidden field, so it passes validation when returned to the controller. Hidden fields can potentially have their values edited client-side, however, so it may be worth checking the value again server side, especially if there are any security implications.
Foreign keys can be nullable so just make sure the UserNameId field is not marked with the "[Required]" Data Annotation in your model.
You'll also need to make sure that the column is nullable on the UserStoreName table to match the model otherwise it'll cause problems if your model is different from its underlying table.
Just a small suggestion also, I wouldn't foreign key on strings, I would change your model foreign key to an int, and make sure that the column in the table it's related to is also an int. It's a lot safer to do so, especially if you're dealing with IDENTITY columns.
If there is anything wrong with the reference, an exception will throw when the code tries to save your change, usually because the value it has in the FK reference cannot be found in the related table.

random EntityValidationErrors (The field is required)

I've been trying to update an Entity using the following code:
var db = new MyContext();
var data = db.Tableau.Find(Id);
if (data != null)
{
data.Name = model.Name;
data.EmbedCode = model.EmbedCode;
db.SaveChanges();
}
The problem is that my Tableaus table has a Parent field (FK not null to a DataTree table). Sometimes when I save the changes to this edited record, I get an error saying that "The Parent field is required". But I am not editing the Parent field. The parent field should be intact and existent, since I am only altering the Name and EmbedCode fields.
How to proceed? Thanks in advance.
That is because you are allowing null values in ParentId column in your Tableaus table, but in your Tableau entity you have ParentId as non-nullable property( which it means the relationship is required), and when you load a Tableau instance from your DB, EF expects that you set that property too. Try changing that property to nullable:
public int? ParentId {get;set;}
If you configure your relationship using Fluent Api it would be:
modelBuilder.Entity<Tableau>()
.HasOptional(t=>t.Parent)
.WithMany(dt=>dt.Tablous)// if you don't have a collection nav. property in your DataTree entity, you can call this method without parameter
.HasForeignKey(t=>t.ParentId);
Update 1
If you want ParentId property as Required in your Tableau entity, you need to make sure that you have a valid value in that column in your DB per each row. With a "valid value" I mean it should be different of the default value and it should exist as PK in your DataTree table.
Update 2:
One way to load a related entity as part of your query is using Include extension method:
var data = db.Tableau.Include(t=>t.Parent).FirstOrDefault(t=>t.Id==Id);

Entity Framework: Many to Many Relationship

I have two tables with a Many-To-Many relationship like this:
User(emailaddress, Name)
UserAlerts(emailaddress, AlertId)
Alert(AlertId,Title)
Alerts have already been added to the database. When inserting a new user, I am doing a lookup on the AlertRepository. The problem is, Instead of creating a record in the User and the UsertAlerts tables only, its also adding an extra Alert record.
I am using the following code:
public ActionResult Register(UserModel model, int[] Alerts)
User user = new MidTier.Models.User();
user.Name = model.Name;
user.EmailAddress = model.EmailAddress;
if (Alerts!=null)
{
IRepository<Alert> alertRepository = new AlertRepository();
foreach (int alertId in Alerts)
{
Alert alert = alertRepository.First(a=>a.ID== alertId);
alertRepository.Detach(alert);
if (alert != null)
{
alert.Enabled = true;
user.Alerts.Add(alert);
}
}
}
userRepository.Attach(user);
userRepository.Add(user);
userRepository.Save();
Why don't you try to search little bit before you ask a question? This problem is asked several times per week. In your previous question I said you that you should use same context for loading Alert and storing User. You didn't do it and complicated whole situation.
The context doesn't know anything about existence of the alert. Once you call Add for user it will add all entities which are not tracked yet. There are three ways to solve this:
Use the same context in both repositories and do not detach alerts. Because of loading alerts, context will know about their existence and doesn't insert them again.
If you don't use the same context for loading you must attach the Alert to the new context before you add it to User. That is hard to do when you wrap EF code to repositories.
If you don't use the same context and you will not attach Alert to the new context before you add it to User you must modify your Add method for User and after adding User to the context you must iterate every alert and change its state to Unchanged.

attaching an entity with a related entity to a new entity framework context

Im trying to get my head around attaching an entity with a related entity to a new context when I want to update the entity.
I have a Person Table (Generalised to Personnel), which has a LanguageID field. This field is linked as a FK via the EF to another table Language with LanguageID as the primary key (1-M). I need to update a particular Persons language preference, however, the relationship seems to remain linked to the old context as i get a "Object cannot be referenced by multiple instances of IEntityChangeTracker" error on the line marked below. Is there any way to attach the Language entity to the new context as a relationship of the Personnel (Person) entity???
The entities were not detached in the orginal GetPersonnel() method which uses an .Include() method to return the PreferredLanguage
PreferredLanguage is the NavigationProperty name on the Person table...
public static void UpdateUser(Personnel originalUser, Personnel newUser )
{
using (AdminModel TheModel = new AdminModel())
{
((IEntityWithChangeTracker)originalUser).SetChangeTracker(null);
((IEntityWithChangeTracker)originalUser.PreferredLanguage).SetChangeTracker(null);
TheModel.Attach(originalUser);--Error Line
TheModel.ApplyPropertyChanges("Person", newUser);
TheModel.SaveChanges();
}
}
Thanks
Sean
To avoid these sort of problems you should make GetPersonnel() do a NoTracking query.
I.e.
ctx.Person.MergeOption = MergeOption.NoTracking;
// and then query as per normal.
This way you can get a graph of connected entities (assuming you use .Include()) that is NOT attached. Note this won't work if you try to manually detach entities, because doing so schreds your graph.
Hope this helps
Alex

MVC - Insert data into multiple entities from same page

I have a single page which collects related data into a series of input fields.
When the user clicks submit I want to insert the data into two different database tables in one go. ie I want to get the primary key from the first insert, and then use it for the second insert operation.
I am hoping to do this all in one go, but I am not sure of the best way to do this with the Models/Entities in MVC.
Are you using LINQ or some other ORM? Typically these will support the ability to add a related entity and handle the foreign key relationships automatically. Typically, what I would do with LINQtoSQL is something like:
var modelA = new ModelA();
var modelB = new ModelB();
UpdateModel( modelA, new string[] { "aProp1", "aProp2", ... } );
UpdateModel( modelB, new string[] { "bProp1", "bProp2", ... } );
using (var context = new DBContext())
{
modelA.ModelB = modelB;
context.ModelA.InsertOnSubmit( modelA );
context.SubmitChanges();
}
This will automatically handle the insertion of modelA and modelB and make sure that the primary key for modelA is set properly in the foreign key for modelB.
If you are doing it by hand, you may have to do it in three steps inside a transaction, first inserting modelA, getting the key from that insert and updating modelB, then inserting modelB with a's data.
EDIT: I've shown this using UpdateModel but you could also do it with model binding with the parameters to the method. This can be a little tricky with multiple models, requiring you have have separate prefixes and use the bind attribute to specify which form parameters go with which model.

Resources