I have an issue with my method that updates the database with user information.
during HttpGet it uses a view model to ensure the integrity of the data, and in HttpPost, the data is passed back and is there. (ran some checks with breakpoints and everything was holding the correct user data that they should have been)
However, when I run my .Save() method, this information is not stored into the DB.
I've checked to see if its pointing to the correct database by changing the data manually, it comes up in a list view just fine. I'M missing something, just can't figure out what ><
Below is the code there is form data in all of the relevant view models, but it just doesn't save!
[HttpPost]
public ActionResult HouseCreate(CreateHouseViewModel viewModel)
{
if (ModelState.IsValid)
{
var house = new House();
house.address = viewModel.address;
house.postCode = viewModel.postCode;
house.noOfDisputes = viewModel.noOfDisputes;
_db.Save();
return RedirectToAction("House");
}
return View(viewModel);
}
house is an object of my house database, modelled correctly with correct Primary keys in place (double checked this in the database viewer.)
save calls:
void DataSources.Save()
{
SaveChanges();
}
No errors come up, which makes it even worse.
using (var context = new UnicornsContext())
{
var unicorn = new Unicorn { Name = "Franky", PrincessId = 1};
context.Unicorns.Add(unicorn); // your missing line
context.SaveChanges();
}
Source
Your "_db" object, I assume is entity framework.
This will have a collection on it, maybe _db.Houses ? You need to add the object to that.
Since it appears you are using Entity Framework, you'll need to inform Entity Framework that your model has been updated. Inbetween the HttpGet and the HttpPost methods, the model becomes dissassociated from the database context. I think the following code work to re-attach it to the context so you can save the changes:
context.Entry(myEntity).State = EntityState.Modified;
context.MyEntities.Attach(myModel);
context.SaveChanges();
Edit: Forgot to add the re-attach method part, assuming this is an existing record in the DB that your updating.
This I find is a far more reliable means to adding items to the database:
// Note: db is your dbContext, model is your view model
var entity = db.Entry(model); // Required to attach the entity model to the database
// you don't need the "var entity = " but its useful
// when you want to access it for logging
db.Entry(model).State = EntityState.Added; // Required to set the entity as added -
// modified does not work correctly
db.SaveChanges(); // Finally save
Related
I'm new to EF, and I'm trying to understand the best way to handle inserting and updating data. Just for some context, I'm using the .net mvc website boiler plate, and I've created a customers table with a 1:1 relationship to aspnetusers. I've created a view to manage customer data.
Here is my HttpPost ActionResult:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddCard(External.Stripe.Customer customer)
{
if (!ModelState.IsValid)
{
return View(customer);
}
External.Stripe.CustomerOperations co = new External.Stripe.CustomerOperations();
customer = co.Create(customer);
var user = UserManager.FindById(User.Identity.GetUserId());
customer.UserId = user.Id;
var context = new External.Stripe.CustomerContext();
context.Customers.Add(customer);
context.SaveChanges();
return RedirectToAction("Index", "Manage");
}
I feel like I'm going down the wrong path, because if I make any updates to the customer model, I actually need to check EF if my key already exists, and if it does, run an update instead. I figured EF would have a native way of saving my model if it needs to be updated. I also don't know if this is even where I would persist my model, or if it is better placed somewhere else in my code.
Short Answer
...if I make any updates to the customer model, I actually need to check EF if my key already exists, and if it does, run an update instead.
AddOrUpdate() does just this. From the docs:
Adds or updates entities by key when SaveChanges is called. Equivalent to an "upsert" operation from database terminology
Example
In other words, change your code to this:
context.Customers.AddOrUpdate(c => c.UserId, customer);
context.SaveChanges();
In the above example, the c => c.UserId is an expression specifying that the UserId should be used when determining whether an Add or Update operation should be performed.
Considerations
AddOrUpdate() matches database tables based on an arbitrary, user supplied key value. In the example, this key is UserId. Be sure to use an appropriate key.
AddOrUpdate() updates all entity values and sets database cells to null for properties that a lack value. Be sure to set all the property values of the object (e.g. customer) before using it.
See also
Update Row if it Exists Else Insert Logic with Entity Framework It has a few answers that talk about four or more different approaches.
I personally like a long winded approach of pulling the data object and using if null to determine if it should be added or updated. However, I do have a table with a lot of columns that gets changed frequently due to new or deprecated business rules. So I use the below
var currentModel = dbContext.Table.First(t => t.Id == _id);
dbContext.Entry(currentModel).CurrentValues.SetValues(newModel);
dbContext.SaveChanges();
I get my newModel from my viewModel where I have a ToModel method that returns the DTO. Now all I have to update is my view and viewModel when a change to the database table is made.
I know it can be silly question, but although ...
Im learning ASP.NET MVC 4 and got one question about the dbcontext.Entry method, lets assume we got such method :
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
How the db.Entry(movie) determine which exactly row from the database is this "movie" instance? If all properties of this movie parameter object will be changed, how will it be possible to determine which row this instance should be attached to? I got smth on my head like it will determine by the Id which we cant change but not sure, so better if I ask :)
When EF generates the models it includes information on what column is the primary key in the database. db.Entry(model) binds the model to the context using that primary key and whatever primary key it shares in the database will be overwriten by the data in the model when SaveChanges() is called. If the primary key is 0, or empty you must set the State to EntityState.Added instead of EntityState.Modified.
As another answer said this can fail. The reason that binding can fail is that a context can only track a single distinct entity once (entity being a distinct class and id). If you attempt to do the following you'll get an exception:
var dbModel = db.Movie.Find(movie.ID);
db.Entry(movie.ID) // Doesn't matter what you do, this will always fail
Because the db context is already tracking a Movie with the same ID.
This is one of the larger reasons that you don't want to share db contexts between users, because another user attempting to update the same entry will cause the second one to throw an exception. While the transaction will be ACID, you can end up with edge case race conditions (for lack of a better term) between users.
Every Table that used by EF must have a Primary key, unless it is a collection , so the EF know the row by the primary key that stay the same.
This method has a parameter Movie, but it has nothing to do with database. You must update object that you will get from db like:
public ActionResult Edit(Movie updatedMovie)
{
if (ModelState.IsValid)
{
//db=your entities context
//here you will get instance of record from db that you want to update
Movie movie = db.Movie.Find(updatedMovie.Id);
TryUpdateModel(movie);
ctx.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Maybe there is more efficient way , but I'm using it like this and it works fine.
If code in your question is a version that is supposed to be a working version, then it wasn't working for me when I have been trying to solve a similar problem so I had to solve it by using code above finally.
So I'm trying to do an update to an Entity model but it's not going as well as I'd like. I'm only trying to update a few fields in my model, but when I have the following code run, I get a SqlException complaining that there are fields (that I'm not trying to edit) that do not allow nulls. It's seeming like it's trying to create a new row in the database, instead of just updating the existing one? Not too sure what to make of it. Any help would be appreciated.
[HttpPost]
public ActionResult Budget(Proposal proposal)
{
if (ModelState.IsValid)
{
db.Proposals.Attach(proposal);
db.ObjectStateManager.ChangeObjectState(proposal, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Details", new {id = proposal.ProposalID});
}
return View(proposal);
}
Here's the SqlException that I'm getting: "System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Ulid', table 'casurg2_dev2.dbo.Proposals'; column does not allow nulls. UPDATE fails.
The statement has been terminated."
And here's the view in question: http://pastie.org/7984222
Ok, the source didn't really help as much (and btw - this is SO, you can paste your code here; no need to be pasting it elsewhere ;)
I did ask for the class definition of the Proposal entity aswell... but in it's absence i can only suggest [at a guess] that your put the original value of Ulid as a hidden field alongside your ProposalId like such:
#Html.HiddenFor(model => model.ProposalID)
#Html.HiddenFor(model => model.Ulid)
so that the model binder can wire it up correctly on the post.
There are 2 ways i can think of to get around the effort with all the hiddenfor's
Use the Add Controller Wizard to set the template to EntityFramework - then modify the related View code. throw away what you don't want etc.
Change the Action method to re-fetch the Proposal record (using its ProposalId) and then bind the changed data to it. This is not the kinda advice i should be giving (an extra, unnecessary round trip to the db)
[HttpPost]
public ActionResult Budget(Proposal proposal)
{
if (ModelState.IsValid)
{
var proposalToMerge = db.Proposals.Find(proposal.ProposalId);
UpdateModel(proposalToMerge);
// don't need this anymore as entity instance is being tracked
// db.Proposals.Attach(proposalToMerge );
// db.ObjectStateManager.ChangeObjectState(proposalToMerge , EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Details", new {id = proposal.ProposalID});
}
return View(proposal);
}
Or summing like that. Untested.
Using MVC3.0 with the razor view engine. I have an entity titled "Vendors" which contains a many to many reference to another entity called "Contacts". All I want to do is remove all of the references from a vendor in a specific controller. Here is my code:
public ActionResult _Edit(Vendor vendor)
{
if (ModelState.IsValid)
{
//Clear contact list
vendor.Contacts.Clear();
db.Vendors.Attach(vendor);
db.ObjectStateManager.ChangeObjectState(vendor, EntityState.Modified);
db.SaveChanges();
}
}
For some reason this doesn't work. and I am not sure why? Thanks
You should load vendor and its contacts from database, then update its properties and SaveChanges:
var vendor = ...retrieve vendor from db...;
vendor.Contacts.Load();
vendor.Contacts.Clear();
...update vendor properties...
db.SaveChanges();
I know it is not as efficient as attaching entity and saving state, but no one said EF is perfect.
By the way, direct binding to Entity Framework object can be very dangerous. Malicious user can prepare POST with values, that are not present in form you provided for him and change values of field, that you didn't want to be even modified in this action. You should create special view models.
Hi,
I am using EntityFramework for my ASP.NET MVC website but have some problems with the update.
This is how my update code looka like :
using (BissEntities context = new BissEntities())
{
if (adCategoryFilter.Id < 1)
context.AddToAdCategoryFilter(adCategoryFilter);
else
context.Refresh(System.Data.Objects.RefreshMode.ClientWins, adCategoryFilter);
if (context.SaveChanges() > 0)
return true;
}
return false;
When executing the context.Refresh i get the following exception :
The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.
Stacktrace : at System.Data.Objects.ObjectContext.RefreshCheck(Dictionary`2 entities, Object entity, EntityKey key)
at System.Data.Objects.ObjectContext.AddRefreshKey(Object entityLike, Dictionary`2 entities, Dictionary`2 currentKeys)
at System.Data.Objects.ObjectContext.RefreshEntities(RefreshMode refreshMode, IEnumerable collection)
at System.Data.Objects.ObjectContext.Refresh(RefreshMode refreshMode, Object entity)
at Biss.Models.FilterModel.UpdateCategoryFilter(AdCategoryFilter adCategoryFilter) in C:\Users\Snowman\Documents\Visual Studio 2010\Projects\Biss\Biss\Models\FilterModel.cs:line 86
This is not the first time I get this problem. First I thought that it might have to do with the relations in the database but after these was removed from the effected table the same exception remained.
Where does the adCategoryFilter come from?
The adCategoryFilter is instansiated(new) and then filled with data from the ViewObject(from the website). It does have the required data like filter Id (to map the filter to correct row in db).
Pleas explain why Im getting this problem and how I could solve it.
BestRegards
Because your using ASP.NET MVC, your working in a stateless environment. That means, once a request has finished processing, there is no more "Entity Framework Memory", or "The Graph".
So, you need to explicitly tell EF you wish to add or update.
Here's how you do it:
using (BissEntities context = new BissEntities())
{
if (adCategoryFilter.Id < 1)
context.AdCategoryFilters.AddObject(adCategoryFilter);
else {
var stub = new AdCategoryFilters { Id = adCategoryFilter.Id };
context.AdCategoryFilters.Attach(stub);
context.AdCategoryFilters.ApplyCurrentValues(adCategoryFilter);
}
context.SaveChanges();
}
That is referred to as the stub technique.
In short, you create a new entity with the same entity key as the entity you are trying to UPDATE (in your case, the entity key is "Id").
You then "attach" this stub (so it's tracked by the EF internal graph), then override the values on this stub with your entity to UPDATE, then save changes.
I can't use UpdateModel, as i have a multi-layered architecture and use POCO's, custom viewmodels, etc - so i have created a custom "UpdateModel" method on my service/repository - which does a (more complicated) version of the above.
Also try not to use "if Id < 1, it's an add" with ASP.NET MVC - as if you forget to bind the ID on the view, it will be passed as 0, so even though you could be doing an update, your above code will try and do an add.
Instead be more explicit - have seperate action methods for Add/Update.
HTH.
Instead of refreshing, try retrieving the object and updating its properties using something like an auto-mapper (or UpdateModel in MVC controller)
The EntityKey is a separate thing to the id property, with some other stuff going on under the hood. Your newly created object is missing this stuff, which is where the problem is coming from.
The pattern goes a little like (not a C# guy so please excuse syntax):
var context = new MyEntities();
var originalObject = context.MyObjectSet.Single(x => x.Id == viewmodel.Id);
UpdateModel(originalObject);
context.SaveChanges();
The crucial difference is that the newly retrieved object has got the EntityKey all set correctly. You can validly use the id property to detect a new/existing object, but there is more to the EntityKey than just that property.