Example #1
0
        /// <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);
        }
Example #2
0
        /// <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);
        }