MVC 5 EF 6.
I've created an edit page using Scaffolding. Saving works fine until I want to restrict the editing of certain properties.
I didn't just use:
#Html.HiddenFor(model => model.Property)
as I wanted the fields to still be visible in the UI. and I didn't want to make it uneditable in the view as it's not really MVC - I wanted to control the editing of the Property from the Model (this is a field that should NEVER be changed but isn't the primary key).
I've implemented the solution from here: How to create readonly textbox in ASP.NET MVC3 Razor and it works perfectly. I can use the annotation:
[ReadOnly(true)]
on my Properties in the model and know that which ever view the property is displayed, it won't be editable.
I thought I had the perfect solution, until I clicked save and get the 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.
Using SQL profiler I have found that using the annotation [ReadOnly(true)] means that NULL will be sent to the database for saving.
Does anyone know why this happens, and is there anything I can do that will allow me to carry on in the way I think is a good solution for displaying data I don't want edited and controlling this in the Model.
Any other suggestions of achieving the same are welcome - or reasons why my whole design is flawed.
If you set the the property with ReadOnly when you press save the model binding will not get the value for readonly for a security reason, let's say if someone removes the readonly attribute through the developer console, then it will enable the field enabling edition, then it's not readonly anymore , that's why you are receiving null on the server ( model binding ), the right why it's to get the value of the readonly again on the server before persisting on the DB
in a few words, the readonly it's working fine as it should be
Related
yeah the question is: can mcv display on the details and index form the text data values vs just ID value that gets stored in the database .
It seems that mvc is very limited in what it can display to the screen. All of the demos and tutorials seem to stop short of showing this is a possibility most just show the user typing in the the full text value. So for example the user has a user interface form that has a list of values drop down element. THey click the value and the ID gets stored to the database.
Later on recall the user wants to pull the data back up for review... in mvc it seems you are stuck with displaying the ID field in the textbox.
but in most other systems you can make a datatable and return the text value of the field back to the form... Is this possible with MVC or are you limited to saving the full text value to the the database?
enter image description here
enter image description here
yeah the answer is: Make a view in SQL Server and then Link that View to the application via the EDMX. Details below.
Finally! I was able to figure out and I will post the solution so that it may help some one get around the mvc / normalized database limitation that seems to exist when you try to follow some of the tutorials.
The key is to build a SQL view outside of MVC. This can then be linked to the application and it will appear to be a table.
So if you use the database first method and you are using SQL server you will need to simply make a view in SQL server. This take mvcs out of the picture all and is much easier.
Step 1: Make a SQL view... that has everything you need joined in one big super view for what you need to display.
So in my case I made a sql server view that has the storage table linked to the support tables. Think of the query builder in MS Access where you link your tables to make a query.
You draw out your tables and links.
Add that to your Database First EDMX and in your controller use that new super view... and in your index and details forms you change the #model to your new super sql view and change the field name in the textboxfor... Boom problem solved... pretty swell how it all works... in this way you don't have to build some complicated view model. You just take the class that the wizard builds and then make a partial class to rename fields to give them user friendly names.
And you are no longer limited by mvc limitations
hope this helps!
Is it possible to safely programmatically get a list of fields that are in the View that has just posted back to a Controller?
I noticed a problem with the default implementation of the scaffolding, in
DB.Entry(model).State = EntityState.Modified
DB.SaveChanges()
The problem is that if I haven't included a field to be edited in the view, it is being overwritten by the default value of the field that .NET assigns when creating the object. eg. If I have a User class with ID, Email and PasswordHash and I want to allow the user to update their Email address only, if I don't include anything for the PasswordHash field, it is reset to NULL as it is passed into the controller as NULL. At the moment, I am working around it by retrieving the current object from the database and updating only the fields which I know are in the View from the model passed in. That isn't such a problem for a small table, but I would like to have a general solution that I can apply across the board, especially for large tables which may during development and I don't want to have to update the code every time.
I know that I could loop through the POST variables and examine them to see what has been posted, but that creates a security issue as the user could inject additional fields that I don't want them to edit. I suppose I could explicitly exclude ones that I don't want them to edit, but then again, I would rather not have to list those if I can avoid it as it is an extra thing to maintain.
I think that there are 2 problems here and I'm not sure either are solvable...
Getting the View that posted back
Establishing which fields are included in that View (I might need to construct it again temporarily to do that?)
I suppose that I can probably get away with ignoring the first one as I could just only ever use that method on the Controller for a single View. That is still a little less neat than I'd like, but it does reduce the issue to just establishing which fields are in the View.
If a view needs only certain properties, create an interface with only those properties. Use this interface in the HttpGet and HttpPost methods.
And then you can use something like AutoMapper to map the viewmodel to your entity.
I'm using Entity Framework (DbContext with database first) with MVC. When user save from a form, I have a condition in the controller that send the entity to the update of insert method depending of some internal flag of mine.
When sending entity to the update method, I flag it to modified using context.Entry(myEntity).State = EntityState.Modified;, I call saveChanges() and everything work well.
When sending the entity to the insert method, I flag it to added using context.Entry(myEntity).State = EntityState.Added; but when calling saveChanges() I receive error about 2 fields that are required...
The problem is that thoses 2 fields are not empty and they effectively contain valid data just before saving... I have even try to force new values to thoses 2 fields just before saving but same error.
It may be usefull to mention that I'm using Devart DotConnect For PostgreSQL as db provider.
Any idea how to debug this problem?
EDIT:
Here is the error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
When looking for this EntityValidationErrors I receive the 2 following specific errors:
The flg_actif field is required
The user_creation field is required
As mentionned before, those fields are filled with data just before saving so I don't understand what is happening.
I'm using EF v4.0.30319 (system.data.entity=> v4.0 and EntityFramework=> v4.4)
EDIT2:
Just to clarify a little bit more: The entity I'm trying to insert already exist in database. The form show the data of this database row. When saving, I decide if I update the row (this work well) but sometime, I need to insert the edited row as a new register instead of updating it to keep an history of the change in database.
Could you verify if the EntityKey property is set or null on the items you are trying to save?
If it already has a key, the context is already aware of the item, and you should use Attach instead of setting the state to added manually.
EDIT: To summarise the point from below. It looks like what you are doing is inserting a new copy of a row already associated with a context. That is almost certainly your problem. Try creating a fresh object based on your original row (i.e. copy the variable values or use a copy constructor), then add that new object.
Additionally, you should not need to set the state manually on a newly added object. You are trying to force the state here because the context doesn't see that item as a new one.
This happens in ASP.NET MVC 2, .NET 4 (EF 4). My Address entity has a reference to the Post reference. Zip is the primary key of the Post entity. Another property in Post entity is CityName. In my views I allow users to change the CityName for the address which automatically (via jquery) loads up the corresponding Zip and stores it inside a hidden field.
When posted, both values are posted fine and binded to the Address's Post reference. But UpdateModel() fails to update them. It says that the Zip is part of the entity's Entity Key and cannot be changed.
I would gladly load up the Post entity by the new Zip and manually assign it to the existing Address but for all other properties I stall want to rely on UpdateModel().
How can I achieve that? One would think that in EF4 stuff like this has been resolved..
By default the entity framework generated classes put restrictions on changing primary key values. This is good. You shouldn't change a PK for any reason at all. Changing PKs outside of add scenarios has pretty huge ramifications for state tracking and the general health of your system.
To solve this problem you want to tell UpdateModel not to update your primary keys using the exclude parameter.
I'm working on my first ASP.NET MVC (beta for version 3) application (using EF4) and I'm struggling a bit with some of the conventions around saving a new record and updating an existing one. I am using the standard route mapping.
When the user goes to the page /session/Evaluate they can enter a new record and save it. I have an action defined like this:
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(EvaluteSessionViewModel evaluatedSession)
{
}
When they save I grab an entity off the view model and attach it to my context and save. So far, so good. Now I want the user to be able to edit this record via the url /session/Evaluate/1 where '1' is the record ID.
Edit: I have my EF entity attached as a property to the View Model.
If I add an overloaded method, like this (so I can retrieve the '1' portion automatically).
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(ID, EvaluteSessionViewModel evaluatedSession)
{
}
I get an "The current request for action 'Evaluate' on controller type 'SessionsController' is ambiguous between the following action" error. I'm not sure why they're ambiguous since they look unique to me.
I decided that I was just going to skip over this issue for now and see if I could get it to update an existing record, so I commented out the EvaluateSave that didn't have the ID parameter.
What I'd like to do is this:
// Load the original entity from EF
// Rebind the postback so that the values posted update the entity
// Save the result
Since the entity is populated as the parameter (evaluatedSession) the rebinding is happening too soon. But as I look at the approach I'd like to take I realized that it opens my code up to hacking (since a user could add in fields into the posted back page and these could override the values I set in the entity).
So it seems I'm left with having to manually check each field to see if it has changed and if it has, update it. Something like this:
if (evaluatedSession.MyEntity.myField <> savedSession.myField)
savedSession.myField = evaluatedSession.MyEntity.myField;
Or, save a copy of the entity and make sure none of the non-user editable ones have changed. Yuck.
So two questions:
First: how do I disambiguate the overloaded methods?
Second: is there a better way of handling updating a previously saved record?
Edit: I guess I could use something like Automapper...
Edit 9/22/2010 - OK, it looks like this is supposed to work with a combination of two items: you can control what fields bind (and specifically exclude some of them) via the [Bind(Exclude="field1,field2")] attribute either on the class level or as part of the method doing the saving, ex.
public ActionResult EvaluateSave([Bind(Exclude="field1")] EvaluateSessionViewModel evaluatedSession)
From the EF side of things you are supposed to be able to use the ApplyCurrentValues() method from the context, ex.
context.ApplyCurrentValues(savedEval.EntityKey.EntitySetName, evaluatedSession);
Of course, that doesn't appear to work for me. I keep getting "An object with a key that matches the key of the supplied object could not be found in the ObjectStateManager. Verify that the key values of the supplied object match the key values of the object to which changes must be applied.".
I tried attaching the original entity that I had just loaded, just in case it wasn't attached to the context for some reason (before ApplyCurrentValues):
context.AttachTo(savedEval.EntityKey.EntitySetName, savedEval);
It still fails. I'm guessing it has something to do with the type of EF entity object MVC creates (perhaps it's not filled in enough for EF4 to do anything with it?). I had hoped to enable .NET framework stepping to walk through it to see what it was attempting to do, but it appears EF4 isn't part of the deal. I looked at it with Reflector but it's a little hard for me to visualize what is happening.
Well, the way it works is you can only have one method name per httpverb. So the easiest way is to create a new action name. Something like "Create" for new records and "Edit" for existing records.
You can use the AntiForgeryToken ( http://msdn.microsoft.com/en-us/library/dd492767.aspx ) to validate the data. It doesn't stop all attempts at hacking but it's an added benefit.
Additional
The reason you can only have one action name per httpverb is because the model binders only attempt to model bind and really aren't type specific. If you had two methods with the same action name and two different types of parameters it can't just try and find the best match because your intent might be clearly one thing while the program only sees some sort of best match. For instance, your might have a parameter Id and a model that contains a property Id and it might not know which one you intend to use.