We have an WCF service with an update method that updates a Customer in the DB.
This method get a detached entity from the client.
void UpdtaeCustomer(Customer detachedCustomer);
We came up with two ways to write this method :
1)
context.CustomerSet.Attach(detachedCustomer);
context.ObjectStateManager.ChangeObjectState(detachedCustomer, entityState.Modified);
context.SaveChanges();
2)
Customer customer = context.GetObjectByKey(detachedCustomer.EntityKey);
context.ApplyCurrentValues<Customer>("CustomerSet", detachedCustomer);
context.SaveChanges();
We want to consider the cons\pros of each method. The first one has a clear advantage of having only one trip to the DB. But what are the pros of the second method. (or maybe they don't act quite the same) ?
Use the first approach. There is no general advantage in using second method with detached entity on the contrary it can make things even worse.
Suppose that you use timestamp. Timestamp is special DB type representing row version. Each time the record in database changes the timestamp is automatically increased. Timestamp is used for concurrecny checks and when using with EF it is handled as Computed column. Each time the EF wants to update the record it compares timestamp in database with the timestamp you retrieved when you loaded the object (must be transpored in your entity to client and back). If timestamps are same the record is saved. If they are different an exception is thrown.
The difference between those two methods is that first method uses timestamp from detached object whereas second method uses timestamp from loaded object. The reason is the computed column. Computed values can't be updated in the application.
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 a scenario where I know the primary key of an entity (retrieved from an unrelated source), and I want to update just 1 property (db column). I have NOT already retrieved the entity from the database. If possible I would like to not have to make this extra round trip.
I create the entity using manager.createEntity.
I update one of the properties.
Then set the entityAspect to setModified();
When saving changes, all the properties that were not updated are set to their default values, and the generated SQL UPDATE statement attempts to update all mapped columns.
Is there a way to tell breeze to only generate SQL for specific properties/columns?
thanks
As you discovered, the properties of the originalValuesMap guide the Breeze server's ContextProvider as it prepares the save request. This is documented in the ContextProvider topic.
In your example, you call setModified after you've changed the property. All that does is change the EntityState; it doesn't create an entry in the client entity's entityAspect.originalValuesMap ... therefore the originalValuesMap sent to the server is empty.
I'm a little surprised that the EFContextProvider.SaveChanges prepared an EF update of the entire entity. I would have guessed that it simply ignored the entity all together. I'm making a mental note to investigate that myself. Not saying the behavior is "right" or "wrong".
You do not have to manipulate the originalValuesMap to achieve your goal. Just change the sequence. Try this:
var foo = manager.createEntity('Foo', {
id = targetId
}, breeze.EntityState.Unchanged); // create as if freshly queried
foo.bar = 'new value'; // also sets 'originalValues' and changes the EntityState
manager.saveChanges(); // etc.
Let us know if that does the trick.
I have a "last updated" field on my model. Any time a managed object model gets changed, I'd like that field to get updated. Is there any way I can have this happen automatically? Or do I need to just update that field manually when I make the other modifications?
"Is there any way I can have this happen automatically?"
No. Core Data is not a database, it's an API for abstract object graph management. It has no concept of uniqueness, auto-incrementing values, etc.
"Or do I need to just update that field manually when I make the other modifications?"
Correct, for example having a lastModified property that your application sets with each change.
You could KVO all keys in your entity but maybe this is not necessary. I would suggest you only update the lastUpdated property when you actually save the object because before that you could still just throw away the changes (either on purpose or e.g. because the app terminates).
Therefore, you can simply override willSave. This is specifically designed for this kind of situation (it mentions a "last-modified" attribute), but note the caveats mentioned in the documentation
If you want to update a persistent property value, you should typically test for equality of any new value with the existing value before making a change. If you change property values using standard accessor methods, Core Data will observe the resultant change notification and so invoke willSave again before saving the object’s managed object context. If you continue to modify a value in willSave, willSave will continue to be called until your program crashes.
For example, if you set a last-modified timestamp, you should check whether either you previously set it in the same save operation, or that the existing timestamp is not less than a small delta from the current time. Typically it’s better to calculate the timestamp once for all the objects being saved (for example, in response to an NSManagedObjectContextWillSaveNotification).
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.
This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???
This is what I don't like about LINQ-to-SQL.
It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.
Here's what the documentation says:
Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.
Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.
A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".
Also, interestingly enough, here's what it says about the DataContext object:
In general, a DataContext instance is
designed to last for one "unit of
work" however your application defines
that term. A DataContext is
lightweight and is not expensive to
create. A typical LINQ to SQL
application creates DataContext
instances at method scope or as a
member of short-lived classes that
represent a logical set of related
database operations.
So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.
What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.
The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.
I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.
public ActionResult MySaveAction(int id, FormCollection form)
{
Customer updateCustomer = _Repository.GetCustomer(id);
TryUpdateModel(updateCustomer, "Customer", form);
_Repository.Save(updateCustomer);
}
You will have to add in all your own exception handling and validation of course, but that's the general idea.
You want to use the attach method on the customers table on the data context.
datacontext.customers.Attach(customer);
to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.
EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as #Odd suggests.