Exemplo n.º 1
0
        private Expression CreateIncludeExpression(UpdateMember previousMember, UpdateMember currentMember)
        {
            // Add First() for Include expression if previous was a collection
            if (previousMember.IsCollection)
            {
                var previousType = ((MemberExpression)previousMember.IncludeExpression).Type;
                var currentType  = currentMember.Accessor.PropertyType;

                if (previousType.IsGenericType && previousType.GetGenericArguments().Length == 1)
                {
                    var innerParam = Expression.Parameter(previousType.GetGenericArguments()[0]);
                    return(Expression.Call(
                               typeof(Enumerable),
                               "Select",
                               new[] { previousType.GetGenericArguments()[0], currentType },
                               previousMember.IncludeExpression,
                               Expression.Lambda(
                                   Expression.Property(innerParam, currentMember.Accessor)
                                   , innerParam)));
                }
                else
                {
                    throw new NotSupportedException("Only supports generic typed collections of IEnumerable<T>");
                }
            }
            else
            {
                return(Expression.Property(previousMember.IncludeExpression, (PropertyInfo)currentMember.Accessor));
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Translates the Expression tree to a tree of UpdateMembers
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns></returns>
        public UpdateMember GetUpdateMembers(Expression <Func <IUpdateConfiguration <T>, object> > expression)
        {
            var initialNode = new UpdateMember()
            {
                IncludeExpression = Expression.Parameter(typeof(T), "p")
            };

            currentMember = initialNode;
            Visit(expression);
            return(initialNode);
        }
Exemplo n.º 3
0
 private static void GetIncludeExpressions <T>(UpdateMember member, ParameterExpression parameter, List <Expression <Func <T, object> > > expressions)
 {
     if (!member.HasMembers())
     {
         expressions.Add(Expression.Lambda <Func <T, object> >(member.IncludeExpression, parameter));
     }
     else
     {
         foreach (var iMember in member.Members)
         {
             GetIncludeExpressions <T>(iMember, parameter, expressions);
         }
     }
 }
Exemplo n.º 4
0
        protected override Expression VisitMethodCall(MethodCallExpression expression)
        {
            currentMethod = expression.Method.Name;

            // go left to right in the subtree (ignore first argument for now)
            for (int i = 1; i < expression.Arguments.Count; i++)
            {
                Visit(expression.Arguments[i]);
            }

            // go back up the tree and continue
            currentMember = currentMember.Parent;
            return(Visit(expression.Arguments[0]));
        }
Exemplo n.º 5
0
        protected override Expression VisitMember(MemberExpression expression)
        {
            // Create new node for this item
            var newMember = new UpdateMember
            {
                Members  = new Stack <UpdateMember>(),
                Parent   = currentMember,
                Accessor = (PropertyInfo)expression.Member
            };

            currentMember.Members.Push(newMember);
            previousMember = currentMember;
            currentMember  = newMember;
            currentMember.IncludeExpression = CreateIncludeExpression(previousMember, currentMember);

            // Chose if entity update or reference update and create expression
            switch (currentMethod)
            {
            case "OwnedEntity":
                currentMember.IsOwned = true;
                break;

            case "AssociatedEntity":
                currentMember.IsOwned = false;
                break;

            case "OwnedCollection":
                currentMember.IsOwned      = true;
                currentMember.IsCollection = true;
                break;

            case "AssociatedCollection":
                currentMember.IsOwned      = false;
                currentMember.IsCollection = true;
                break;

            default:
                throw new NotSupportedException("The method used in the update mapping is not supported");
            }
            return(base.VisitMember(expression));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Updates a detached graph of entities by performing a diff comparison of object keys.
        /// Author: Brent McKendrick
        /// </summary>
        /// <param name="context">The database context to attach / detach.</param>
        /// <param name="dataStoreEntity">The entity (sub)graph as retrieved from the data store.</param>
        /// <param name="updatingEntity">The entity (sub)graph after it has been updated</param>
        private static void RecursiveGraphUpdate(DbContext context, object dataStoreEntity, object updatingEntity, UpdateMember member)
        {
            if (member.IsCollection)
            {
                // We are dealing with a collection
                var updateValues = (IEnumerable)member.Accessor.GetValue(updatingEntity, null);
                var dbCollection = (IEnumerable)member.Accessor.GetValue(dataStoreEntity, null);
                var keyFields    = context.GetKeysFor(updateValues.GetType().GetGenericArguments()[0]);
                var dbHash       = MapCollectionToDictionary(keyFields, dbCollection);

                // Iterate through the elements from the updated graph and try to match them against the db graph.
                List <object> additions = new List <object>();
                foreach (object updateItem in updateValues)
                {
                    try
                    {
                        var key = CreateHash(keyFields, updateItem);
                        // try to find in db collection
                        object dbItem;
                        if (dbHash.TryGetValue(key, out dbItem))
                        {
                            // If we own the collection
                            if ((member.IsOwned) && (context.Entry(dbItem).State != System.Data.Entity.EntityState.Detached))
                            {
                                context.Entry(dbItem).CurrentValues.SetValues(updateItem); // match means we are updating

                                foreach (var childMember in member.Members)
                                {
                                    RecursiveGraphUpdate(context, dbHash[key], updateItem, childMember);
                                }
                            }

                            dbHash.Remove(key); // remove to leave only db removals in the collection
                        }
                        else
                        {
                            additions.Add(updateItem);
                        }
                    }
                    catch
                    {
                    }
                }

                // Removal of dbItem's left in the collection
                foreach (var dbItem in dbHash.Values)
                {
                    // Own the collection so remove it completely.
                    if (member.IsOwned)
                    {
                        context.Set(dbItem.GetType()).Remove(dbItem);
                    }

                    dbCollection.GetType().GetMethod("Remove").Invoke(dbCollection, new[] { dbItem });
                }

                // Add elements marked for addition
                foreach (object newItem in additions)
                {
                    if (!member.IsOwned)
                    {
                        context.Set(newItem.GetType()).Attach(newItem);
                    }

                    // Otherwise we will add to object
                    dbCollection.GetType().GetMethod("Add").Invoke(dbCollection, new[] { newItem });
                }
            }
            else // not collection
            {
                var dbvalue  = member.Accessor.GetValue(dataStoreEntity, null);
                var newvalue = member.Accessor.GetValue(updatingEntity, null);
                if (dbvalue == null && newvalue == null) // No value
                {
                    return;
                }

                // If we own the collection then we need to update the entities otherwise simple relationship update
                if (!member.IsOwned)
                {
                    if (dbvalue != null && newvalue != null)
                    {
                        var keyFields = context.GetKeysFor(newvalue.GetType());
                        var newKey    = CreateHash(keyFields, newvalue);
                        var updateKey = CreateHash(keyFields, dbvalue);
                        if (newKey == updateKey)
                        {
                            return; // do nothing if the same key
                        }
                    }

                    if (context.Entry(newvalue).State == System.Data.Entity.EntityState.Detached)
                    {
                        context.Set(newvalue.GetType()).Attach(newvalue);
                    }

                    member.Accessor.SetValue(dataStoreEntity, newvalue, null);
                    context.Entry(newvalue).Reload();
                    // TODO would like to do this: context.Entry(newvalue).State = EntityState.Unchanged;
                    // However it seems even though we are in an unchanged state EF will still update the database if the original values are different.
                }
                else
                {
                    if (dbvalue != null && newvalue != null)
                    {
                        // Check if the same key, if so then update values on the entity
                        var keyFields = context.GetKeysFor(newvalue.GetType());
                        var newKey    = CreateHash(keyFields, newvalue);
                        var updateKey = CreateHash(keyFields, dbvalue);

                        // perform update if the same
                        if (updateKey == newKey)
                        {
                            context.Entry(dbvalue).CurrentValues.SetValues(newvalue);
                            context.Entry(dbvalue).State = System.Data.Entity.EntityState.Modified;
                        }
                        else
                        {
                            member.Accessor.SetValue(dataStoreEntity, newvalue, null);
                        }
                    }
                    else
                    {
                        member.Accessor.SetValue(dataStoreEntity, newvalue, null);
                    }

                    // TODO
                    foreach (var childMember in member.Members)
                    {
                        RecursiveGraphUpdate(context, dbvalue, newvalue, childMember);
                    }
                }
            }
        }
Exemplo n.º 7
0
        public static List <Expression <Func <T, object> > > GetIncludeExpressions <T>(UpdateMember member)
        {
            var expressions = new List <Expression <Func <T, object> > >();

            GetIncludeExpressions <T>(member, Expression.Parameter(typeof(T), "p"), expressions);
            return(expressions);
        }