Esempio n. 1
0
			internal void Add(MetaAssociation assoc, TrackedObject from, TrackedObject to)
			{
				Dictionary<TrackedObject, TrackedObject> pairs;
				if(!associations.TryGetValue(assoc, out pairs))
				{
					pairs = new Dictionary<TrackedObject, TrackedObject>();
					associations.Add(assoc, pairs);
				}
				pairs.Add(from, to);
			}
Esempio n. 2
0
			internal void Add(TrackedObject from, TrackedObject to)
			{
				List<TrackedObject> refs;
				if(!references.TryGetValue(from, out refs))
				{
					refs = new List<TrackedObject>();
					references.Add(from, refs);
				}
				if(!refs.Contains(to))
					refs.Add(to);
			}
Esempio n. 3
0
			internal TrackedObject this[MetaAssociation assoc, TrackedObject from]
			{
				get
				{
					Dictionary<TrackedObject, TrackedObject> pairs;
					if(associations.TryGetValue(assoc, out pairs))
					{
						TrackedObject to;
						if(pairs.TryGetValue(from, out to))
						{
							return to;
						}
					}
					return null;
				}
			}
 internal override int Insert(TrackedObject item) {
     if (item.Type.Table.InsertMethod != null) {
         try {
             item.Type.Table.InsertMethod.Invoke(this.context, new object[] { item.Current });
         }
         catch (TargetInvocationException tie) {
             if (tie.InnerException != null) {
                 throw tie.InnerException;
             }
             throw;
         }
         return 1;
     }
     else {
         return DynamicInsert(item);
     }
 }
 internal override int DynamicInsert(TrackedObject item) {
     Expression cmd = this.GetInsertCommand(item);
     if (cmd.Type == typeof(int)) {
         return (int)this.context.Provider.Execute(cmd).ReturnValue;
     }
     else {
         IEnumerable<object> facts = (IEnumerable<object>)this.context.Provider.Execute(cmd).ReturnValue;
         object[] syncResults = (object[])facts.FirstOrDefault();
         if (syncResults != null) {
             // [....] any auto gen or computed members
             AutoSyncMembers(syncResults, item, UpdateType.Insert, AutoSyncBehavior.ApplyNewAutoSync);
             return 1;
         }
         else {
             throw Error.InsertAutoSyncFailure();
         }
     }
 }
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="session">The session in which the conflicts occurred.</param>
		/// <param name="trackedObject">The tracked item in conflict.</param>
		/// <param name="isDeleted">True if the item in conflict no longer exists in the database.</param>
		internal ObjectChangeConflict(ChangeConflictSession session, TrackedObject trackedObject, bool isDeleted)
			: this(session, trackedObject)
		{
			this.isDeleted = isDeleted;
		}
Esempio n. 7
0
 internal abstract int DynamicDelete(TrackedObject item);
Esempio n. 8
0
 internal abstract int Update(TrackedObject item);
Esempio n. 9
0
 internal abstract int Insert(TrackedObject item);
        private void BuildDependencyOrderedList(TrackedObject item, List<TrackedObject> list, Dictionary<TrackedObject, VisitState> visited) {
            VisitState state;
            if (visited.TryGetValue(item, out state)) {
                if (state == VisitState.Before) {
                    throw Error.CycleDetected();
                }
                return;
            }

            visited[item] = VisitState.Before;

            if (item.IsInteresting) {
                if (item.IsDeleted) {
                    // if 'item' is deleted
                    //    all objects that used to refer to 'item' must be ordered before item
                    foreach (TrackedObject other in this.originalChildReferences[item]) {
                        if (other != item) {
                            this.BuildDependencyOrderedList(other, list, visited);
                        }
                    }
                }
                else {
                    // if 'item' is new or changed
                    //   for all objects 'other' that 'item' refers to along association 'assoc'
                    //      if 'other' is new then 'other' must be ordered before 'item'
                    //      if 'assoc' is pure one-to-one and some other item 'prevItem' used to refer to 'other'
                    //         then 'prevItem' must be ordered before 'item'
                    foreach (MetaAssociation assoc in item.Type.Associations) {
                        if (assoc.IsForeignKey) {
                            TrackedObject other = this.currentParentEdges[assoc, item];
                            if (other != null) {
                                if (other.IsNew) {
                                    // if other is new, visit other first (since item's FK depends on it)
                                    if (other != item || item.Type.DBGeneratedIdentityMember != null) {
                                        this.BuildDependencyOrderedList(other, list, visited);
                                    }
                                }
                                else if ((assoc.IsUnique || assoc.ThisKeyIsPrimaryKey)) {
                                    TrackedObject prevItem = this.originalChildEdges[assoc, other];
                                    if (prevItem != null && other != item) {
                                        this.BuildDependencyOrderedList(prevItem, list, visited);
                                    }
                                }
                            }
                        }
                    }
                }

                list.Add(item);
            }

            visited[item] = VisitState.After;
        }
Esempio n. 11
0
 internal abstract int Update(TrackedObject item);
 private Expression GetDeleteVerificationCommand(TrackedObject tracked) {
     ITable table = this.context.GetTable(tracked.Type.InheritanceRoot.Type);
     System.Diagnostics.Debug.Assert(table != null);
     ParameterExpression p = Expression.Parameter(table.ElementType, "p");
     Expression pred = Expression.Lambda(Expression.Equal(p, Expression.Constant(tracked.Current)), p);
     Expression where = Expression.Call(typeof(Queryable), "Where", new Type[] { table.ElementType }, table.Expression, pred);
     Expression selector = Expression.Lambda(Expression.Constant(0, typeof(int?)), p);
     Expression select = Expression.Call(typeof(Queryable), "Select", new Type[] { table.ElementType, typeof(int?) }, where, selector);
     Expression singleOrDefault = Expression.Call(typeof(Queryable), "SingleOrDefault", new Type[] { typeof(int?) }, select);
     return singleOrDefault;
 }
Esempio n. 13
0
 internal abstract int Insert(TrackedObject item);
Esempio n. 14
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="session">The session in which the conflicts occurred.</param>
 /// <param name="trackedObject">The tracked item in conflict.</param>
 /// <param name="isDeleted">True if the item in conflict no longer exists in the database.</param>
 internal ObjectChangeConflict(ChangeConflictSession session, TrackedObject trackedObject, bool isDeleted)
     : this(session, trackedObject)
 {
     this.isDeleted = isDeleted;
 }
Esempio n. 15
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="session">The session in which the conflicts occurred.</param>
 /// <param name="trackedObject">The tracked item in conflict.</param>
 internal ObjectChangeConflict(ChangeConflictSession session, TrackedObject trackedObject)
 {
     this.session       = session;
     this.trackedObject = trackedObject;
     this.original      = trackedObject.CreateDataCopy(trackedObject.Original);
 }
Esempio n. 16
0
 internal abstract void AppendDeleteText(TrackedObject item, StringBuilder appendTo);
Esempio n. 17
0
 internal abstract int DynamicDelete(TrackedObject item);
Esempio n. 18
0
 internal abstract int DynamicUpdate(TrackedObject item);
 private Expression GetUpdateCheck(Expression serverItem, TrackedObject tracked) {
     MetaType mt = tracked.Type;
     if (mt.VersionMember != null) {
         return Expression.Equal(
             this.GetMemberExpression(serverItem, mt.VersionMember.Member),
             this.GetMemberExpression(Expression.Constant(tracked.Current), mt.VersionMember.Member)
             );
     }
     else {
         Expression expr = null;
         foreach (MetaDataMember mm in mt.PersistentDataMembers) {
             if (!mm.IsPrimaryKey) {
                 UpdateCheck check = mm.UpdateCheck;
                 if (check == UpdateCheck.Always ||
                     (check == UpdateCheck.WhenChanged && tracked.HasChangedValue(mm))) {
                     object memberValue = mm.MemberAccessor.GetBoxedValue(tracked.Original);
                     Expression eq =
                         Expression.Equal(
                             this.GetMemberExpression(serverItem, mm.Member),
                             Expression.Constant(memberValue, mm.Type)
                             );
                     expr = (expr != null) ? Expression.And(expr, eq) : eq;
                 }
             }
         }
         return expr;
     }
 }
 private Expression GetDeleteCommand(TrackedObject tracked) {
     MetaType rowType = tracked.Type;
     MetaType rowTypeRoot = rowType.InheritanceRoot;
     ParameterExpression p = Expression.Parameter(rowTypeRoot.Type, "p");
     Expression pv = p;
     if (rowType != rowTypeRoot) {
         pv = Expression.Convert(p, rowType.Type);
     }
     object original = tracked.CreateDataCopy(tracked.Original);
     Expression check = this.GetUpdateCheck(pv, tracked);
     if (check != null) {
         check = Expression.Lambda(check, p);
         return Expression.Call(typeof(DataManipulation), "Delete", new Type[] { rowTypeRoot.Type }, Expression.Constant(original), check);
     }
     else {
         return Expression.Call(typeof(DataManipulation), "Delete", new Type[] { rowTypeRoot.Type }, Expression.Constant(original));
     }
 }
        private static void SendOnValidate(MetaType type, TrackedObject item, ChangeAction changeAction) {
            if (type != null) {
                SendOnValidate(type.InheritanceBase, item, changeAction);

                if (type.OnValidateMethod != null) {
                    try {
                        type.OnValidateMethod.Invoke(item.Current, new object[] { changeAction });
                    } catch (TargetInvocationException tie) {
                        if (tie.InnerException != null) {
                            throw tie.InnerException;
                        }

                        throw;
                    }
                }
            }
        }
 private bool HasAssociationChanged(MetaAssociation assoc, TrackedObject item) {
     if (item.Original != null && item.Current != null) {
         if (assoc.ThisMember.StorageAccessor.HasAssignedValue(item.Current) ||
             assoc.ThisMember.StorageAccessor.HasLoadedValue(item.Current)
             ) {
             return this.GetOtherItem(assoc, item.Current) != this.GetOtherItem(assoc, item.Original);
         }
         else {
             object[] currentFKs = CommonDataServices.GetForeignKeyValues(assoc, item.Current);
             object[] originaFKs = CommonDataServices.GetForeignKeyValues(assoc, item.Original);
             for (int i = 0, n = currentFKs.Length; i < n; i++) {
                 if (!object.Equals(currentFKs[i], originaFKs[i]))
                     return true;
             }
         }
     }
     return false;
 }
Esempio n. 23
0
 internal abstract int DynamicInsert(TrackedObject item);
        /// <summary>
        /// Clears out the foreign key values and parent object references for deleted objects on the child side of a relationship.
        /// For bi-directional relationships, also performs the following fixup:
        ///   - for 1:N we remove the deleted entity from the opposite EntitySet or collection
        ///   - for 1:1 we null out the back reference
        /// </summary>
        private void ClearForeignKeyReferences(TrackedObject to) {
            Debug.Assert(to.IsDeleted, "Foreign key reference cleanup should only happen on Deleted objects.");
            foreach (MetaAssociation assoc in to.Type.Associations) {
                if (assoc.IsForeignKey) {
                    // If there is a member on the other side referring back to us (i.e. this is a bi-directional relationship),
                    // we want to do a cache lookup to find the other side, then will remove ourselves from that collection.
                    // This cache lookup is only possible if the other key is the primary key, since that is the only way items can be found in the cache.
                    if (assoc.OtherMember != null && assoc.OtherKeyIsPrimaryKey) {
                        Debug.Assert(assoc.OtherMember.IsAssociation, "OtherMember of the association is expected to also be an association.");
                        // Search the cache for the target of the association, since
                        // it might not be loaded on the object being deleted, and we
                        // don't want to force a load.
                        object[] keyValues = CommonDataServices.GetForeignKeyValues(assoc, to.Current);
                        object cached = this.services.IdentityManager.Find(assoc.OtherType, keyValues);

                        if (cached != null) {
                            if (assoc.OtherMember.Association.IsMany) {
                                // Note that going through the IList interface handles 
                                // EntitySet as well as POCO collections that implement IList 
                                // and are not FixedSize.
                                System.Collections.IList collection = assoc.OtherMember.MemberAccessor.GetBoxedValue(cached) as System.Collections.IList;
                                if (collection != null && !collection.IsFixedSize) {
                                    collection.Remove(to.Current);
                                    // Explicitly clear the foreign key values and parent object reference
                                    ClearForeignKeysHelper(assoc, to.Current);
                                }
                            }
                            else {
                                // Null out the other association.  Since this is a 1:1 association,
                                // we're not concerned here with causing a deferred load, since the
                                // target is already cached (since we're deleting it).
                                assoc.OtherMember.MemberAccessor.SetBoxedValue(ref cached, null);
                                // Explicitly clear the foreign key values and parent object reference
                                ClearForeignKeysHelper(assoc, to.Current);
                            }
                        }
                        // else the item was not found in the cache, so there is no fixup that has to be done
                        // We are explicitly not calling ClearForeignKeysHelper because it breaks existing shipped behavior and we want to maintain backward compatibility
                    }
                    else {
                        // This is a unidirectional relationship or we have no way to look up the other side in the cache, so just clear our own side
                        ClearForeignKeysHelper(assoc, to.Current);
                    }                    
                }
                // else this is not the 1-side (foreign key) of the relationship, so there is nothing for us to do
            }
        }
 /// <summary>
 /// Update the item, returning 0 if the update fails, 1 if it succeeds.
 /// </summary>        
 internal override int Update(TrackedObject item) {
     if (item.Type.Table.UpdateMethod != null) {
         // create a copy - don't allow the override to modify our
         // internal original values
         try {
             item.Type.Table.UpdateMethod.Invoke(this.context, new object[] { item.Current });
         }
         catch (TargetInvocationException tie) {
             if (tie.InnerException != null) {
                 throw tie.InnerException;
             }
             throw;
         }
         return 1;
     }
     else {
         return DynamicUpdate(item);
     }
 }
 // verify that primary key and db-generated values have not changed
 private static void CheckForInvalidChanges(TrackedObject tracked) {
     foreach (MetaDataMember mem in tracked.Type.PersistentDataMembers) {
         if (mem.IsPrimaryKey || mem.IsDbGenerated || mem.IsVersion) {
             if (tracked.HasChangedValue(mem)) {
                 if (mem.IsPrimaryKey) {
                     throw Error.IdentityChangeNotAllowed(mem.Name, tracked.Type.Name);
                 }
                 else {
                     throw Error.DbGeneratedChangeNotAllowed(mem.Name, tracked.Type.Name);
                 }
             }
         }
     }
 }
 internal override int DynamicDelete(TrackedObject item) {
     Expression cmd = this.GetDeleteCommand(item);
     int ret = (int)this.context.Provider.Execute(cmd).ReturnValue;
     if (ret == 0) {
         // we don't yet know if the delete failed because the check constaint did not match
         // or item was already deleted.  Verify the item exists
         cmd = this.GetDeleteVerificationCommand(item);
         ret = ((int?)this.context.Provider.Execute(cmd).ReturnValue) ?? -1;
     }
     return ret;
 }
 private static int Compare(TrackedObject x, int xOrdinal, TrackedObject y, int yOrdinal) {
     // deal with possible nulls
     if (x == y) {
         return 0;
     }
     if (x == null) {
         return -1;
     }
     else if (y == null) {
         return 1;
     }
     // first order by action: Inserts first, Updates, Deletes last
     int xAction = x.IsNew ? 0 : x.IsDeleted ? 2 : 1;
     int yAction = y.IsNew ? 0 : y.IsDeleted ? 2 : 1;
     if (xAction < yAction) {
         return -1;
     }
     else if (xAction > yAction) {
         return 1;
     }
     // no need to order inserts (PK's may not even exist)
     if (x.IsNew) {
         // keep original order
         return xOrdinal.CompareTo(yOrdinal);
     }
     // second order by type
     if (x.Type != y.Type) {
         return string.CompareOrdinal(x.Type.Type.FullName, y.Type.Type.FullName);
     }
     // lastly, order by PK values
     int result = 0;
     foreach (MetaDataMember mm in x.Type.IdentityMembers) {
         object xValue = mm.StorageAccessor.GetBoxedValue(x.Current);
         object yValue = mm.StorageAccessor.GetBoxedValue(y.Current);
         if (xValue == null) {
             if (yValue != null) {
                 return -1;
             }
         }
         else {
             IComparable xc = xValue as IComparable;
             if (xc != null) {
                 result = xc.CompareTo(yValue);
                 if (result != 0) {
                     return result;
                 }
             }
         }
     }
     // they are the same? leave in original order
     return xOrdinal.CompareTo(yOrdinal);
 }
 internal override void AppendDeleteText(TrackedObject item, StringBuilder appendTo) {
     if (item.Type.Table.DeleteMethod != null) {
         appendTo.Append(Strings.DeleteCallbackComment);
     }
     else {
         Expression cmd = this.GetDeleteCommand(item);
         appendTo.Append(this.context.Provider.GetQueryText(cmd));
         appendTo.AppendLine();
     }
 }
 internal IEnumerable<TrackedObject> this[TrackedObject from] {
     get {
         List<TrackedObject> refs;
         if (references.TryGetValue(from, out refs)) {
             return refs;
         }
         return Empty;
     }
 }
            private Expression GetInsertCommand(TrackedObject item) {
                MetaType mt = item.Type;

                // bind to InsertFacts if there are any members to syncronize
                List<MetaDataMember> membersToSync = GetAutoSyncMembers(mt, UpdateType.Insert);
                ParameterExpression p = Expression.Parameter(item.Type.Table.RowType.Type, "p");
                if (membersToSync.Count > 0) {
                    Expression autoSync = this.CreateAutoSync(membersToSync, p);
                    LambdaExpression resultSelector = Expression.Lambda(autoSync, p);
                    return Expression.Call(typeof(DataManipulation), "Insert", new Type[] { item.Type.InheritanceRoot.Type, resultSelector.Body.Type }, Expression.Constant(item.Current), resultSelector);
                }
                else {
                    return Expression.Call(typeof(DataManipulation), "Insert", new Type[] { item.Type.InheritanceRoot.Type }, Expression.Constant(item.Current));
                }
            }
Esempio n. 32
0
 internal abstract int DynamicInsert(TrackedObject item);
            private void AutoSyncMembers(object[] syncResults, TrackedObject item, UpdateType updateType, AutoSyncBehavior autoSyncBehavior) {
                System.Diagnostics.Debug.Assert(item != null);
                System.Diagnostics.Debug.Assert(item.IsNew || item.IsPossiblyModified, "AutoSyncMembers should only be called for new and modified objects.");
                object[] syncRollbackValues = null;
                if (syncResults != null) {
                    int idx = 0;
                    List<MetaDataMember> membersToSync = GetAutoSyncMembers(item.Type, updateType);
                    System.Diagnostics.Debug.Assert(syncResults.Length == membersToSync.Count);                    
                    if (autoSyncBehavior == AutoSyncBehavior.ApplyNewAutoSync) {
                        syncRollbackValues = new object[syncResults.Length];
                    }
                    foreach (MetaDataMember mm in membersToSync) {
                        object value = syncResults[idx];
                        object current = item.Current;
                        MetaAccessor accessor =
                            (mm.Member is PropertyInfo && ((PropertyInfo)mm.Member).CanWrite)
                                ? mm.MemberAccessor
                                : mm.StorageAccessor;

                        if (syncRollbackValues != null) {
                            syncRollbackValues[idx] = accessor.GetBoxedValue(current);
                        }
                        accessor.SetBoxedValue(ref current, DBConvert.ChangeType(value, mm.Type));
                        idx++;
                    }                    
                }
                if (syncRollbackValues != null) {
                    this.SyncRollbackItems.Add(new KeyValuePair<TrackedObject, object[]>(item, syncRollbackValues));
                }
            }
Esempio n. 34
0
 internal abstract int DynamicUpdate(TrackedObject item);
            private Expression GetUpdateCommand(TrackedObject tracked) {
                object database = tracked.Original;
                MetaType rowType = tracked.Type.GetInheritanceType(database.GetType());
                MetaType rowTypeRoot = rowType.InheritanceRoot;

                ParameterExpression p = Expression.Parameter(rowTypeRoot.Type, "p");
                Expression pv = p;
                if (rowType != rowTypeRoot) {
                    pv = Expression.Convert(p, rowType.Type);
                }

                Expression check = this.GetUpdateCheck(pv, tracked);
                if (check != null) {
                    check = Expression.Lambda(check, p);
                }

                // bind to out array if there are any members to synchronize
                List<MetaDataMember> membersToSync = GetAutoSyncMembers(rowType, UpdateType.Update);
                if (membersToSync.Count > 0) {
                    Expression autoSync = this.CreateAutoSync(membersToSync, pv);
                    LambdaExpression resultSelector = Expression.Lambda(autoSync, p);
                    if (check != null) {
                        return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type, resultSelector.Body.Type }, Expression.Constant(tracked.Current), check, resultSelector);
                    }
                    else {
                        return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type, resultSelector.Body.Type }, Expression.Constant(tracked.Current), resultSelector);
                    }
                }
                else if (check != null) {
                    return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type }, Expression.Constant(tracked.Current), check);
                }
                else {
                    return Expression.Call(typeof(DataManipulation), "Update", new Type[] { rowTypeRoot.Type }, Expression.Constant(tracked.Current));
                }
            }
Esempio n. 36
0
 internal abstract void AppendDeleteText(TrackedObject item, StringBuilder appendTo);
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="session">The session in which the conflicts occurred.</param>
		/// <param name="trackedObject">The tracked item in conflict.</param>
		internal ObjectChangeConflict(ChangeConflictSession session, TrackedObject trackedObject)
		{
			this.session = session;
			this.trackedObject = trackedObject;
			this.original = trackedObject.CreateDataCopy(trackedObject.Original);
		}