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.
In order to perform a save in DevForce, one of the following methods must be called.
Synchronous methods:
Asychronous methods:
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.
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:
C# | manager.SaveChanges(new[] { aChangedCustomer }); |
VB | manager.SaveChanges(New () {aChangedCustomer}) |
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.
The SaveOptions class has several interesting properties:
Property | PropertyType | Access | Description | ||||
---|---|---|---|---|---|---|---|
TransactionSettings | TransactionSettings | get set | Gets or sets the transactional behavior for the save. This property is described in more detail here: save-transactions. | ||||
Tag | Object | get set | 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). | ||||
FixupTempIds | FixupTempIds | get set | 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
This property is only relevant when a SaveChanges call is made that takes a collection of entities. | ||||
EntityTypesExcludedFromPostSaveRefetch | IList<Type> | get set | 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. | ||||
IgnoreClientValidationErrors | boolean | get set | 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. | ||||
PocoSaveMode | PocoSaveMode | get set | Determines how to discover any custom server side POCO save methods. |
and one useful method:
Method Signature | Description |
---|---|
public SaveOptions WithTag(Object tag) | Used to create a clone of this SaveOptions but with the "Tag" set to the specified value. |
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:
Property | PropertyType | Access | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Ok | bool | get | Returns whether or not the save succeeded. This will be true, unless the Save was either canceled or had an exception that was 'handled'. | ||||||||||||
SaveStatus | SaveStatus | get | Returns the kind of operation performed during this save.
| ||||||||||||
SavedEntities | IList<Object> | get | List of entities that were saved. | ||||||||||||
WasCancelled | bool | get | Returns true if the save was canceled, either during the Saving event or from within the EntityServerSaveInterceptor. | ||||||||||||
WasExceptionHandled | bool | get | Returns true if an exception was thrown but handled by an EntityServerError handler; otherwise false. | ||||||||||||
HandledException | EntityManagerSaveException | get | Returns the handled exception or null (Nothing in VB) if no exception or unhandled. | ||||||||||||
TemporaryIdMap | UniqueIdMap | get | Gets 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".
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.