Linq to SQL replacing related entity - asp.net-mvc

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.

Related

how to get the default values from the model into a new breeze clientside entity

i would like to create new entities which use the default values defined in the model.
i've checked the retrieved metadata, and the default values are there:
{"name":"LastName","type":"Edm.String","maxLength":"50","unicode":"true","fixedLength":"false","defaultValue":"admin:
Nachname"},
however they are not taken into consideration when creating a new entity.
This is a bug in Breeze that should be fixed in the next release, out in about week. When this fix gets in then breeze will honor any defaultValues it finds in the EntityFramework data model.
One problem though is while it is easy to get 'defaultValues' into a Model First Entity Framework model via the properties editor, it's actually difficult to get it into a Code First EF model, unless you use fluent configuration. Unfortunately, EF ignores the [DefaultValue] attribute when constructing Code First model metadata.
One workaround that you can use now is to poke the 'defaultValue' directly onto any dataProperty. Something like:
var customerType = myEntityManager.metadataStore.getEntityType("Customer");
var fooProperty = customerType.getProperty("foo");
fooProperty.defaultValue = 123;

How does Breeze handle database column defaults?

I can't find any info about this in the documentation, so I will ask here. How does breeze handle database column defaults? I have required columns in my database, but there are also default static values supplied for these in the database column definitions. Normally, I can insert null into these columns, and the new records will get the default. However, breeze doesn't seem to be aware of database column defaults, and the entities that have null in these columns fail validation on saving.
Thanks,
Mathias
Try editing the edmx xml by adding StoreGeneratedPattern = "Computed" attribute to the column with default value in the DB.
Edit:
Actually, before doing editing the xml, try setting the StoreGeneratedPattern property to Computed in the model editor itself.
Update:
This was fixed in Breeze 1.4.6 ( or later), available now.
Original Post:
There is currently in a bug in Breeze that should be fixed in the next release, out in about week. When this fix gets in then breeze will honor any defaultValues it finds in the EntityFramework data model.
One problem though is while it is easy to get 'defaultValues' into a Model First Entity Framework model via the properties editor, it's actually difficult to get it into a Code First EF model, unless you use fluent configuration. Unfortunately, EF ignores the [DefaultValue] attribute when constructing Code First model metadata.
One workaround that you can use now is to poke the 'defaultValue' directly onto any dataProperty. Something like:
var customerType = myEntityManager.metadataStore.getEntityType("Customer");
var fooProperty = customerType.getProperty("foo");
fooProperty.defaultValue = 123;

Entity Framework 4 Change Audit

I have a web application using EF4. I am somewhat new to EF and now trying to implement change Audit.I tried to do this by trapping the SavingChanges event of the Context Class as below
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(TicketContainer_SavingChanges);
}
So the event handler accesses the changed records by the following
this.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified);
This works fine and I am creating column level audit for selected tables. Every table/entity has an ID field which is an identifier with columnName="ID". So in my audit routine I simply accesses data from column with name "Id" to get the ID of audited record.
The problem I face is during insert . The new record has no ID yet as it is an identity column in the database and is always 0.
One solution I can think of is using GUID for all Ids.But is there a way to implement this using standard int32 Identity Ids?
thanks
When we insert data through EF the identity column is not generted while insertion. To get the Id of Identity columns we have to insert the data first then only we can get the Id of coulmn.
please go through the below which might be helpful to you.
http://www.codeproject.com/KB/database/ImplAudingTrailUsingEFP1.aspx
I don't now how many entities you have but in our own implementation of audit tracking we created a specific audit entity for each entity so we could link them together trough navigational properties and let the database set the identity keys.
If you use inheritance for your audit entities it's quit easy to query them.
Hope this helps :)
Identity columns are not generated while insertion. Once data is inserted then only you can get identity column data in EF. So, you can try some work around by getting Id after insertion and then populating audit table with that Id.

UpdateModel() cannot assign new value to navigation property (entity reference)

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.

ASP.NET MVC save new record verse update existing record conventions

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.

Resources