Saving is the process of storing entities in the database, inserting them if they are new, or updating them if they are already there.
When an entity is saved for the first time, an Id is assigned to it.
Saving has full support for graphs of entities
, so you can have a graph of IdentifiableEntity with cycles and the saver should be able to deal with it.
Also, in the current implementation, saving is done at a granularity level of the IdentifiableGraph
, so it's not possible to save just a Modifiable (an EmbeddedEntity or a MList for example). In fact, when a Modifiable is SelfModified, the modification is propagated to the parent IdentifiableEntity before saving, so the whole IdentifiableEntity is saved. See more about this in Change Tracking
Before saving, every Modifiable is tested for integrity
using IntegrityCheck. If the test fails, an Exception is thrown and the transaction is roll-backed, so you never have invalid data in your database. You can avoid this behaviour for some particular entities and integrity rules using Corruption. See more about this in Validation
Finally, before saving, the method PreSaving is called
on every Modifiable in the graph. You can use it to calculate some redundant values before saving (ToStr is calculated this way).
public static T Save<T>(this T obj) where T : IdentifiableEntity
public static void SaveList<T>(this IEnumerable<T> entities) where T : IIdentifiable
public static void SaveParams(params IdentifiableEntity entities)
Preserving Object Identity
Once saved, entities are stored in the current ObjectCache
. This information is usually lost but you can keep it using a wider scope ObjectCache
How the Saver works (Advanced Topic)
In case you are interested in the details, this is what the saver is actually doing:
- Generating a DirectedGraph of all the reachable Modifiables.
- Testing IntegrityCheck and calling PreSaving on every Modifiable.
- Using an invert graph to propagate modifications from Modifiables to the closest Identifiable parent.
- Collapsing the original graph of Modifiables to a graph of Identifiables.
- Identifying all the edges that mean a dependency on saving order (the end of the edge goes to a new Identifiable).
- Identifying feedback edges, if any, in this dependency graph using this algorithm.
- Grouping Identifiables in the graph using topological short, so the entities are layered starting from the leaves and ending with the root.
- Saving each group (layer): The Schema class is the one that actually creates the SqlPreCommand for each entity avoiding to use references in the feedback edge set. Finally the SqlPreCommands are concatenated together and sent to the database using a single SqlCommand. If some group is too big for a single Sql Command (too many parameters), this is split using SqlPreCommand facilities.
- Final updates are sent to fix the problem made by the feedback edges.
Note: Maybe you have realised that, in order to resolve cycles, entities have to be saved inconsistently while some related entities have no Id jet (feedback edges). This is safely done inside of a transaction but could have problems if your FK columns have no support for null values. Try to avoid
[NotNullable] atributte on references, use
[NotNullValidator] on the property instead.