Up Save

Perform a save

Last modified on December 04, 2012 18:10

Saving is the process of sending a collection of added, modified or deleted objects to some backend datastore and having them be persisted there. The basics of performing a save are described on this page. A description of the ways in which a save operation can be intercepted or mediated will be discussed in subsequent pages.

Save API

In order to perform a save in DevForce, one of the following methods must be called. 

  Synchronous methods:

  Asychronous methods: 

Determining which entities to save

Two of the overloads shown above take a list of entities as the first argument. These versions allow a developer to explicitly specify which entities to save. The overloads which do not take a list of entities assume that all entities found in the EntityManager's cache that are marked as either added, modified or deleted should be saved.

If either of the overloads specifying a list of entities is used, DevForce will perform an additional check prior to sending these entities to the server.  This check is to insure that if any of the specified entities has a temporary id then every other entity that has a reference to this id must also be in the save list. If this is not the case, DevForce will throw an exception, with a description of which entities are causing a problem. Essentially, DevForce will not allow a 'partial' save that could cause data corruption.  This is not an issue when DevForce determines which entities to save.

Saving a single entity

You'll notice that all SaveChanges methods will either accept an IEnumerable of entities or will save all changes in the entity cache.  This may at first seem confusing if you need to save only a single entity.  It's easy, though; just create a list, maybe a List<Entity>, and add the changed entity to it.  Or create an array on the fly, like so:

manager.SaveChanges(new[] { aChangedCustomer });
manager.SaveChanges(New () {aChangedCustomer})

The SaveOptions class

Every save performed by DevForce makes use of an instance of the SaveOptions class.  This instance is provided either by passing it into the SaveChanges call or by using value of the EntityManager's  DefaultSaveOptions property. Note that several of the overloads above take a SaveOptions argument, those that do not make use of the EntityManager.DefaultSaveOptions. The EntityManager.DefaultSaveOptions property can be modified at any time to change these defaults.

Most of the time the default values for SaveOptions are sufficient and there is no need to use one of the SaveChanges overloads that takes a SaveOptions parameter.

The SaveOptions class has several interesting properties:

PropertyPropertyType    Access   Description
TransactionSettingsTransactionSettings get
Gets or sets the transactional behavior for the save. This property is described in more detail here: save-transactions.

Used to pass custom user information to the entityserver regarding the save operation. 

This property can be very useful in controlling the behavior of an EntityServerSaveInterceptor. The value of the SaveOptions property, including this tag, is passed to each instance of the EntityServerSaveInterceptor during its construction.

The value of this property must be serializable by the DataContractSerializer. 

The default value for this property is null (Nothing in VB).


Gets or sets the option to fixup all generated temporary Ids or only those Ids corresponding to entities passed into the SaveChanges method. Possible values are 

FixupTempIds.AllDefault setting. Perform Id fixup on all generated temporary Ids.
FixupTempIds.InSaveListOnly  Perform Id fixup only on those entities passed into the SaveChanges method.

This property is only relevant when a SaveChanges call is made that takes a collection of entities. 

EntityTypesExcludedFromPostSaveRefetch IList<Type>get

Gets or sets the types of entities to be excluded from the refetch that is performed after a save.

By default all entity types will be refetched after a save, this setting allows a developer to improve performance by telling DevForce which types cannot 'change' as a result of the save and where a refetch is therefore unnecessary.

IgnoreClientValidationErrorsboolean get
Gets or sets a flag whether to ignore client validation errors on save. When false, a save operation that contains entities with client validation errors will not proceed to the server. When true, client validation errors are ignored and the save operation will proceed to the server. Defaults to true.
Determines how to discover any custom server side POCO save methods.

and one useful method:

Method SignatureDescription
public SaveOptions WithTag(Object tag)Used to create a clone of this SaveOptions but with the "Tag" set to the specified value.

The SaveResult class

The synchronous SaveChanges calls return a SaveResult while the SaveChangesAsync versions all return an EntitySaveOperation which contains a SaveResult property. 

The SaveResult type contains the following properties:

PropertyPropertyType    Access   Description

Returns whether or not the save succeeded.

This will be true, unless the Save was either canceled or had an exception that was 'handled'.


Returns the kind of operation performed during this save.

IncompleteThe operation has not yet completed.
NoOperationThere was nothing to save, there were no added, modified or deleted entities to save ( and no many-many relationship changes).
CancelledThe operation was cancelled either in a Saving handler or in the EntityServerSaveInterceptor.
ExceptionHandledAn exception was both thrown and then handled by an EntityServerErrorInterceptor handler
NormalThe save completed normally.
SavedEntitiesIList<Object>getList of entities that were saved.
WasCancelledboolgetReturns true if the save was canceled, either during the Saving event or from within the EntityServerSaveInterceptor.
WasExceptionHandledboolgetReturns true if an exception was thrown but handled by an EntityServerError handler; otherwise false.
HandledExceptionEntityManagerSaveExceptiongetReturns the handled exception or null (Nothing in VB) if no exception or unhandled.
TemporaryIdMapUniqueIdMapgetGets copy of the UniqueIdMap that was (or would have been) used for fixup.

SaveResult.Ok returns "true" if the save was entirely successful. ( A SaveStatus of NoOperation is still considered 'Ok'). 

If the save was canceled in a life-cycle handler, SaveResult.WasCancelled will return “true” and SaveResult.Ok will return "false". 

If the save threw an exception and was handled, SaveResult.WasErrorHandled will return "true" and SaveResult.Ok will return "false".

If a save throws an exception and is not handled, no synchronous result will be returned at all because an exception will have been thrown. If this occurs during an asynchronous call then SaveResult.WasErrorHandled will be false and SaveResult.Ok will return "false".

After the Save

DevForce immediately re-queries all inserted or updated entities after saving them successfully. This re-query is essential because the insert or update may provoke a data source trigger that modifies the data source object. We often use a trigger to update an optimistic concurrency column. A database-maintained timestamp is another common example. In such cases, the row in the database is no longer exactly the same as the row we wrote. 

The SaveOptions.EntityTypesExcludedFromPostSaveRefetch property ( mentioned above) may be used to modify this behavior.

After the refetch the EntityServer sends the possibly 'updated' entity back to the client’s EntityManager. The revised entity is merged back into the cache, replacing the original and its EntityState becomes Unchanged.

Created by DevForce on February 19, 2011 14:12

This wiki is licensed under a Creative Commons 2.0 license. XWiki Enterprise 3.2 - Documentation. Copyright © 2015 IdeaBlade