/// <summary> /// Reconciles the stored entity graph extending as far as described by the given extent with the one given by entity. /// It makes a number of load requests from the store, but all modifications are merely scheduled in the context. /// This overload takes a prefetched attached entity that can allow the skipping of the load request in case of a /// trivial extent. /// </summary> /// <param name="db">The context.</param> /// <param name="attachedEntity">The prefetched graph to reconcile.</param> /// <param name="templateEntity">The detached graph to reconcile with.</param> /// <param name="extent">The extent of the subgraph to reconcile.</param> /// <returns>The attached entity.</returns> internal static async Task <E> ReconcileAsync <E>(this DbContext db, E attachedEntity, E templateEntity, Action <ExtentBuilder <E> > extent) where E : class { // FIXME: We want to be able to deal with unset foreign keys, perhaps this helps: // https://stackoverflow.com/questions/4384081/read-foreign-key-metadata-programatically-with-entity-framework-4 var builder = new ExtentBuilder <E>(); extent?.Invoke(builder); if (attachedEntity == null || builder.reconcilers.Count != 0) { // In case of removals, the templateEntity is null, and otherwise // attachedEntity is often null as it's not always preloaded by // the caller. var entityToTakeTheKeyFrom = templateEntity ?? attachedEntity; attachedEntity = builder.reconcilers .Aggregate(db.GetEntity(entityToTakeTheKeyFrom), (q, r) => r.AugmentInclude(q)) .FirstOrDefault(); } var isNewEntity = attachedEntity == null || (db.Entry(attachedEntity).State == EntityState.Deleted && templateEntity != null); if (isNewEntity) { attachedEntity = db.AddEntity(templateEntity); } foreach (var reconciler in builder.reconcilers) { await reconciler.ReconcileAsync(db, attachedEntity, templateEntity); } if (!isNewEntity && templateEntity != null) { db.UpdateEntity(attachedEntity, templateEntity); } foreach (var property in builder.properties) { property.Modify(db, attachedEntity, templateEntity); } return(attachedEntity); }
/// <summary> /// Loads the entity given by the given entity's key to the given extent. /// </summary> /// <param name="db">The context.</param> /// <param name="entityToLoad">The detached entity the key of which defines what entity to load.</param> /// <param name="extent">The extent to which to load the entity.</param> /// <returns>The attached entity.</returns> public static async Task <E> LoadExtentAsync <E>(this DbContext db, E entityToLoad, Action <ExtentBuilder <E> > extent) where E : class { var builder = new ExtentBuilder <E>(); extent(builder); var attachedEntity = await builder.reconcilers .Aggregate(db.GetEntity(entityToLoad), (q, r) => r.AugmentInclude(q)) .FirstOrDefaultAsync(); foreach (var reconciler in builder.reconcilers) { await reconciler.LoadAsync(db, attachedEntity); } foreach (var property in builder.properties) { property.Modify(db, attachedEntity, null); } return(attachedEntity); }