I have very standart scenario. I send entity from controller to view for user to edit it. User pushes submit button I get entity back from browser in my controller. Its detached now so I attach it back to context. How can I detect if there were changes made compared to database?
You need to pull the entity back from the database when the user submits. There are a number of reasons you can't know for certain what to do just by looking at what came back:
Can't trust data from the user. The user has full control over what the browser sends back to you, so this is a possible exploit if you key off something in the request
Another user might have modified the same data
Pulling an entity from the database is fast, so just pull it, set the fields you want to set and SaveChanges()
Addition: As Eduard noted in the comments below, it looks like there is a utility method ApplyCurrentValues to do this: msdn.microsoft.com/en-us/library/dd487246.aspx
Related
Is it possible to keep on to the 'changes' you will be wanting to make with Entity Framework?
For instance I do an update query then the connection fails, I close the app and then tomorrow I want it to do that update query when a connection is restored.
Is a thing like that possible with Entity Framework 6?
You could possibly create your own ChangeTracker, like in this tutorial, which saves all changes to a file. Parsing the file for a later use might be tricky.
The other option would be to use retry logic and hope the connection problem was just a slight hiccup
I think ZorgoZ is right and this answer will not address your actual question, but what I think it is your actual problem (see XY problem): you fail to save some changes (business-wise) and you want to be able to retry saving them later, not necessary some EF changes that you want to persist a later moment.
One way to do it is to store the business information that trigger the change and retry the whole flow:
define some sort of queue stored in a file, isolated storage etc. (depends on your technology)
your update flow should persist some sort of record / object in the queue that contains all relevant information + a status (e.g. Queued, Save error etc.)
update is tried. If it fails, you can have a status update (e.g. Save error)
the user closes the application
later, the user opens the application seeing the queue items status (item 1 saved OK, item 2 save error etc.)
Besides EF technical issues, this implementation allows for flow changes that include things outside EF save changes (e.g. also check some external API) and abstracts away the data access layer entirely (EF changes serialisation implementation has a big chance to depend on some EF specifics).
Also I sense context information serialization (e.g. data behind some form) is easier to implement than EF context changes serialization.
While looking at this answer to the question Why do we use ViewModels?, I came across this section:
"A view should not contain any non-presentational logic" and "You
should not trust the View" (because a View could be user-provided). By
providing a Model object (potentially still connected to an active
DatabaseContext) a view can make malicious changes to your database.
What exactly does this refer to? If I have UserId and Password in my Model and my ViewModel, where does the security come in? Some kind of check in the controller? What do we check?
How do we determine we can trust the data from the view? Is this handled by the antiforgery token?
I believe the answer is referring to the over-post problem. When you utilize an entity class directly with your view, and particularly if you save that posted entity directly to your database, a malicious user could modify the form to post fields they should not be able to modify.
For example, let's say you had a form that allows a user to edit widgets. Let's also say that you have row-level permissions, such that a user can only edit widgets that belong to them. So, Joe, our fictitious malicious user, edits a widget he's allowed to edit with id 123. But, he decides he wants to mess with Jane's widget, so he adds a field to the form named Id and gives it the value of Jane's widget id. When Joe then posts the widget form, Jane's widget is updated instead.
A view model is not solely for solving this problem, but it does basically negate the issue because, inherently, you cannot directly save the view model to the database. Instead, you must map the view model's values onto the entity, before saving the entity to the database. As a result, you then explicitly control what does and does not get mapped, so in the same example above, Joe changing the id ends up having no effect because you're not mapping that onto the entity.
In truth, the real problem here is in directly saving anything posted by a user directly to the database. You could actually still feed your entity class to the view as the "model", but then not save the posted instance. Instead, you then create a new instance of the entity or pull an instance from the database fresh, and simply map the values from the posted instance over to that. Again, you wouldn't map a property like Id, so again Joe is foiled. In other words, it's not the view model that solves the problem, it's the never trusting a user enough to directly save anything created via a POST that solves the issue.
Microsoft gives another alternative solution in the form of the Bind attribute, which essentially allows you to include/exclude certain properties on an entity class from the modelbinding process (ignoring any posted values, in other words). So, for example, you could potentially solve the issue above by decorating the param on your action with [Bind(Exclude = "Id")], which would then discard any posted value for Id. However, Bind is horrible for a number of reasons, and you should not actually use it. Always use a view model instead, or simply don't ever directly save the entity instance created by the modelbinder.
I have a controller with a get and post action, on the post it checks if the model state is valid. If it is valid the page process the data and redirect. If the model state is not valid it will return the view back with the model. To ensure the drop downs have data I need to repopulate the items from the database which means I need to make another call to the database.
Is there any way to cut the call out to the database by caching or any other method?
The problem is that the browser is only submitting the values for your drop downs and not the text. You could get around this by creating a hidden element which submits the text in addition to the values.
But is that a good idea? In my opinion, no. You're creating extra network traffic between browser and the server in order to save traffic between the server and the database. In most cases it will be more efficient to retrieve the data from the database than the client.
Also, the data may have changed between when you sent it to the client and when you send it back the second time.
Ryan has a good point. I'm assuming you're coming from a WebForms background where everything was cached in the view state and posted back to the server. Asp.net MVC by its very architectural lends itself to a different approach where it's standard to re-query the database for data presentation to the user i.e. dropdown list values.
So you should only post back to the server the values the user has input. This is happening via the view-model. I would make a common method which accepts your view model and does the standard database pull and map to model. This way you can use the same method for the initial Get request and also for validation failure post-backs.
Also, if we're talking about small data sets then I would most definitely re-query as it's not expensive to do. If the drop down lists are huge then it depends on the data itself. Does this data change infrequently? Then it might be feasible to cache it on the web-server in a static list. If it does change frequently then you could look into more advanced solutions like memcached or redis.
I am currently working on a profile for my website. I am trying to make it so that I can save profile data that is stretched across multiple tables.
For example I have Personal info which is the main User Table. It holds personal information about the user.
Then I have separate tables for the usernames they have, as I will have other information pertaining to the user under that username.
When trying to save the information to my database. I get this error: A referential integrity constraint violation. I have tried making individual forms for this, but I don't see that working. My question is, do I make a HTTPPOST method for each table I want to save data to or can I do it in just 1 POST method?
My question is, do I make a HTTPPOST method for each table I want to
save data to....
You can make an POST for each form you are posting to the server. If the formfields in that form match a domain object (at least the primary key), then you might be able to use your database objects in this way. It depends on how your database objects are constructed.
or can I do it in just 1 POST method?
Yes, create a view model that contains members for all of the formfields on the form. Be sure to include hidden formfields for whatever primary keys you need for database lookups. When you POST, map the view model members to your database object.
I am trying to force a ChangeConflictException by altering records in two different browsers. This has worked for me in the past. But now it just wont throw the exception.
Last one in is winning.
I have checked the column properties of the entity I am updating and each column is set to always check.
Is there anything I can look for?
I haven't extended this data context or done any modifications to any of the properties.
Thanks.
EditThis is an ASP.net application.
Are you sure this is not what is happening:
Browser A loads entity X
Browser B loads entity X
Browser A submits form
Browser A loads entity X again, changes property and stores again
Browser B submits form
Browser B loads entity X again, changes property and stores again
Key point here is that the entity is reloaded on postback (HTTP = stateless) and in 6) you are actually loading changes made by 4) and overwriting them. Linq2Sql does not "stamp" your form you would have to do this manually.
You write in the comments that you have another app that works. If that is a Windows app, then the situation is completely different, since the entity is then most likely not reloaded again.
That's a race condition. Consider you are probably retrieving the info, updating and sending to the db. If the first commits the changes to the db, before the second retrieves the info you wouldn't get a conflict.
Update: About the comment on not being able to do it. You can do it, you use the Attach method but you need to keep any original values you want it to check for the concurrency. Check these:
linq2sql-update-object-not-created-in-datacontext
how-to-update-a-single-column-in-linq-without-loading-the-entire-row
That is surely what is happening under the linq datasource.