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.
Related
We have identified a Location entity in a database as a value object in our domain (DDD). Locations are used by other domain objects, but don't really "stand alone" -- they always belong to another entity.
Now we are trying to edit a list of these values in a simple MVC web application. So the view would show a list of locations in a view model LocationViewModel.
However, the value object is by definition immutable, yet does hold a reference to another entity (Business).
Domain:
public class Location : ValueObject<Location>
{
readonly locationId;
public int LocationId {get{return _locationId;}}
public Business Business {get;set;}
}
My problem is understanding how you can simply edit a bunch of value objects in a UI and change, e.g. what Business the location belongs to.
A value object is not supposed to have an "identity", but it does need an ID so the repository can update the database.
I also don't think you can make Location an entity just because you want to edit it in the UI. Or is Location, in this scenario indeed an Entity?
What am I not understanding?
Thank you!
It's a classic problem. In one context it's an entity and in another a value object. I found the example of a telephone number helpful to understanding this sort of problem.
In a CRM for example, a telephone number is a value object. The same one can be associated with multiple contacts. It varies by value (key concept here). So in this context it's a value object. In this example, I could store telephone numbers in the database and the 'ID' would be the telephone number itself. If the value object was made up of multiple parts then they would form a composite key.
If however we looked at a telephone number at a telephone company. That would most likely be an Entity. It could have all manor of information attached to it. All that info would vary by ID (which in this case would be the number).
In your case, Location sounds like a value object. If you need to save it in a database as a thing rather than just as part of an entity then use it's parts as a composite key. You will need to handle what happens when you 'change' one as it's not a change but the creation of new value object. One approach is to remove the old and insert the new. Or just keep all versions. It depends on your domain.
Hope that's helpful.
You don't change a value object. You create a new one with different values. If the value object has few properties that you want often to change, some helper methods are usefull. myObject.WithX(4711) will create a new instance with all properties the same as myObject but the X Property changed to 4711 for example.
To "edit" a value object in an UI you use a viewmodel. The Viewmodel is not a value object (and no entity by the way) and is not part of your domain. It's purely in the Presentation Layer. It is editable and mutable. It could have a constructor, taking your (immutable) value object to copy its values from and it could have a ToXXX Method to create a new (immutable) value object with its current (and changed) values.
If you want to store your value objects in a separate table (instead of roll out the fields in the table that stores the owning entity) this is purely data access layer related and not part of your domain model. This could be done by mapping. In the value object the database id is immutable and has no meaning in the domain model.
I have an entity/table that uses sqlgeography.
Since EF 4.X doesn't support spatial types I'm instead sending the bytes of the field back and forth.
I have stored procs on the database side that handles the converstion and properties on the code side to do that job.
To add the properties in the code I used a partial class.
One of those properties is for the SqlGeography which simply wraps around the byte[] property to handle getting and setting.
This property is hidden from EF using the NotMappedAttribute.
The other is the property exposing the byte[] itself and is decorated with the EdmScalarPropertyAttribute and DataMemberAttribute.
I then go to the EF model designer (*.edmx) to point the entity model at the Insert/Update/Delete stored procs.
It finds the stored procs alright and realises that they (when appropriate) take a VARBINARY parameter.
It also has a drop down allowing you to select a property on the entity class which maps to that parameter.
However this drop down doesn't list either of my properties. I don't care about the SqlGeography property since that is meant to be hidden from EF, however it is vital for me to be able to point it at the byte[] property, as that is where the data comes from.
I would very much like to avoid database triggers or wrapper classes and addiitonal fields to fudge this in to working.
I tried manually editing the .edmx file to include the byte[] property, but then it just complains it's unmapped.
Can anyone give me some insight in to how to get this to work? Or an alternative method of achiving the end result?
We could use a view to create the binary field for us, but this then involves manually creating a lot of the xml for the relationships within the data.
This pretty much voids the point of using EF which is to make life simple and easy.
For this project We'll just add a binary field to the table then have sprocs to handle the converstion on the server and a property in a partial entity class for exposing the geography type in the model.
Next project I doubt we'll be using EF. Dapper is so much more painless, even if theres a touch more code writing involved.
Here's the links for using views if anyone thinks it would be applicable to them:
http://thedatafarm.com/blog/data-access/yes-you-can-read-and-probably-write-spatial-data-with-entity-framework/
http://smehrozalam.wordpress.com/2009/08/12/entity-framework-creating-a-model-using-views-instead-of-tables/
In the end we created a computed column for each table that exposes the spatial data as bytes.
We then use stored procs for inserting and updating the spatial data.
I have a Client entity and PostCode entity in Linq to SQL model. The Clients table in database contains client_postcode column which is a FK column to postalcode column in PostCode table, which is a varchar column primary key for PostCode table.
When updating Client, in my code I do this
// postcode
updating.PostCode = (from p in ctx.PostCodes
where p.postalcode.Equals(client.PostCode.postalcode)
select p).First();
where client object is provided from ASP.NET MVC View form. This code seems to set the PostCode related entity fine. But when calling SubmitChanges() I receive the following exception:
Value of member 'postalcode' of an object of type 'PostCode' changed. A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead.
So I am currently unable to change the related entity. How is that done in Linq to Sql?
UPDATE:
After further review and troubleshooting I found out that the problem is in ASP.NET MVC UpdateModel() call. If I call UpdateModel() to update the existing entity with the edited data, something is wrong with the FK assignement for PostCode. If I don't call UpdateModel and do it by hand, it works.
Any ideas what goes wrong in UpdateModel() that it can't set the relationship to foreign key entities correctly?
I am updating this question and starting a bounty. The question is simplified. How to successfully use L2S and UpdateModel() to work when updating items (with related entities as FK) in ASP.NET MVC Edit views?
It seems to me that you are receiving PostCode.postalcode in the http post request.
Based on how model binding works, the UpdateModel call updates .PostCode.postalcode of the model you are passing to it.
Use this overload to include or exclude specific properties.
Wouldn't updating.client_postcode = client.client_postcode; accomplish what you want?
Client.PostCode should be looked up on seek based on client_postocde.
You can not do what you are trying, you cannot change the Postcode like that.
James' idea is in the right direction.
Updatemodel() takes the matching values from the Formcollection
How do these values come in the Formcollection? What are their keys?
basically there are 2 ways in editing an object.
option 1:
all the value names you want to update have corresponding keys in the Formcollection, which leaves you just to call UpdateModel() of the original object. do SubmitChanges()
option 2:
Get the original object, change it's values manually (because the keys dont correspond) and do SubmitChanges()
you are tying to change a link, you cant do that. you can only edit the updating.client_postcode which in this case is a string?
Can you please copy the whole action here? So I can write some code for you without gambling.
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.
I'm using Entity Framework with an AS.NET MVC application. I need to allow the user to create new records and modify existing ones. I am able to fetch existing records no problem, but when I pass back in the edited entity and try to save it it creates a new one and saves it and leaves the original unmodified.
I am getting the object from EF using the primary key (e.g. ID number for an employee record). I successfully retrieve it, and set the MergeOption like so:
Context.Sector.MergeOption = MergeOption.NoTracking;
I am able to trace that the object has the correct data (using the key of the original record) all the way down to the point where I call:
Context.SaveChanges();
However, after that, the new record is created instead of modifying the existing one.
Is there something obvious I am missing here? I would have thought that retrieving the object and changing some of its values (not the ID) and saving it would just work, but obviously not.
Thanks,
Chris
"NoTracking means that the ObjectStateManager is bypassed and therefore every access to the Entity Objects results in a fetch from the database and the creation of new objects."
-- http://blog.dynatrace.com/2009/03/11/adonet-entity-framework-unexpected-behaviour-with-mergeoptions/
I don't think NoTracking is what you want.
From your comment: "distributed across various tiers and some proprietary libraries"
Are you new()ing up a ObjectContext, closing it or losing the reference to it, and then trying to save your object to a new() or different ObjectContext?
If so your losing all of your change tracking information. If this is the case then you want to call the Attach() method to reattach the entity to the context, ApplyPropertyChanges() and then finally SaveChanges().
Julie Lerman has a pretty good blog post that outlines all the different change tracking options and techniques that are available. You should also check out this MSDN article on the same subject.