Пример #1
0
        /// <summary>
        /// Make a shallow copy of column values without copying references of the source entity
        /// </summary>
        /// <param name="source">the source entity that will have it's values copied</param>
        /// <returns></returns>
        public static LINQEntityBase ShallowCopy(LINQEntityBase source)
        {
            PropertyInfo[] sourcePropInfos      = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            PropertyInfo[] destinationPropInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            // create an object to copy values into
            Type           entityType = source.GetType();
            LINQEntityBase destination;

            destination = Activator.CreateInstance(entityType) as LINQEntityBase;

            foreach (PropertyInfo sourcePropInfo in sourcePropInfos)
            {
                if (Attribute.GetCustomAttribute(sourcePropInfo, typeof(ColumnAttribute), false) != null)
                {
                    PropertyInfo destPropInfo = destinationPropInfos.Where(pi => pi.Name == sourcePropInfo.Name).First();
                    destPropInfo.SetValue(destination, sourcePropInfo.GetValue(source, null), null);
                }
            }

            destination.LINQEntityState = EntityState.Original;
            destination.LINQEntityGUID  = source.LINQEntityGUID;

            return(destination);
        }
Пример #2
0
        /// <summary>
        /// Returns true if two entities have the same property values (does not traverse releationships).
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool ShallowCompare(LINQEntityBase entity1, LINQEntityBase entity2)
        {
            if (!object.ReferenceEquals(entity1.GetType(), entity2.GetType()))
            {
                return(false);
            }

            PropertyInfo[] entity1PropInfos = entity1.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            PropertyInfo[] entity2PropInfos = entity2.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            // Find if there are any properties that do not match that are custom attributes
            var compareResults = from pi1 in entity1PropInfos.Where(p1 => Attribute.GetCustomAttribute(p1, typeof(ColumnAttribute), false) != null)
                                 join pi2 in entity2PropInfos.Where(p2 => Attribute.GetCustomAttribute(p2, typeof(ColumnAttribute), false) != null)
                                 on pi1.Name equals pi2.Name into pij
                                 from pi2 in pij.DefaultIfEmpty()
                                 select new { Match = object.Equals(pi1.GetValue(entity1, null), pi2.GetValue(entity2, null)) };

            return(compareResults.Where(cr => cr.Match == false).Count() == 0);
        }
Пример #3
0
        /// <summary>
        /// Handles the property changing event sent from child object.
        /// </summary>
        /// <param name="sender">child object</param>
        /// <param name="e">Property Changing arguements</param>
        private void PropertyChanging(object sender, PropertyChangingEventArgs e)
        {
            // Ignore events if syncronising with DB
            if (_isSyncronisingWithDB == true)
            {
                return;
            }

            // Do a check here to make sure that the entity is not change if it is supposed to be deleted
            if (this.LINQEntityState == EntityState.Deleted || this.LINQEntityState == EntityState.CancelNew)
            {
                throw new ApplicationException("You cannot modify a deleted entity");
            }

            // If it's a change tracked object thats in "Original" state
            // grab a copy of the object incase it's going to be modified
            if (this.LINQEntityState == EntityState.Original && LINQEntityKeepOriginal == true && LINQEntityOriginalValue == null)
            {
                _originalEntityValueTemp = LINQEntityBase.ShallowCopy(this);
            }
        }
Пример #4
0
        /// <summary>
        /// Indicates that the entity will Update the database.
        /// </summary>
        /// <param name="OriginalValue">
        /// Sets/Overrides the original value of the entity.
        /// The entity value passed in should be an earlier shallow copy of the entity.
        /// This value can be set to null to indicate if the original entity value should be removed if it exists from a previous modification.
        /// </param>
        public void SetAsUpdateOnSubmit(LINQEntityBase OriginalValue)
        {
            if (this.LINQEntityState == EntityState.Detached)
            {
                throw new ApplicationException("You cannot change the Entity State from 'Detached' to 'Modified'");
            }

            if (this.LINQEntityState == EntityState.NotTracked)
            {
                throw new ApplicationException("You cannot change the Entity State when the Entity is not change tracked");
            }

            if (OriginalValue != null)
            {
                this._originalEntityValue = LINQEntityBase.ShallowCopy(this);
            }
            else
            {
                this._originalEntityValue = null;
            }

            this.LINQEntityState = EntityState.Modified;
        }
Пример #5
0
        /// <summary>
        /// Syncronises this EntityBase and all sub objects
        /// with a data context.
        /// </summary>
        /// <param name="targetDataContext">The data context that will apply the changes</param>
        /// <param name="cascadeDelete">Whether or not casade deletes is allowed</param>
        public void SynchroniseWithDataContext(DataContext targetDataContext, bool cascadeDelete)
        {
            // Before doing anything, check to make sure that the new datacontext
            // doesn't try any deferred (lazy) loading
            if (targetDataContext.DeferredLoadingEnabled == true)
            {
                throw new ApplicationException("Syncronisation requires that the Deferred loading is disabled on the Target DataContext");
            }

            // Also Make sure this entity is the change tracking root
            if (this.IsRoot == false)
            {
                throw new ApplicationException("You cannot syncronise an entity that is not the change tracking root");
            }

            List <LINQEntityBase> entities        = this.ToEntityTree().Distinct().ToList();
            List <LINQEntityBase> entitiesDeleted = new List <LINQEntityBase>();

            try
            {
                // Tell each entity that syncronisation is occuring
                foreach (LINQEntityBase entity in entities)
                {
                    entity._isSyncronisingWithDB = true;
                }

                // For entities which have been Cancelled (Added as new then deleted before submission to DB)
                // detach these entities by removing it's references so that they can be garbage collected.

                foreach (LINQEntityBase entity in entities)
                {
                    if (entity.LINQEntityState == EntityState.CancelNew)
                    {
                        foreach (PropertyInfo propInfo in _cacheAssociationFKProperties[entity.GetType()].Values)
                        {
                            propInfo.SetValue(entity, null, null);
                        }
                    }
                }

                // Loop through all the entities, attaching as appropriate to the data context

                foreach (LINQEntityBase entity in entities)
                {
                    if (entity.LINQEntityState == EntityState.Original)
                    {
                        targetDataContext.GetTable(entity.GetEntityType()).Attach(entity, false);
                    }
                    else if (entity.LINQEntityState == EntityState.New)
                    {
                        // If the entity's state is new, LINQ to SQL Tries to attach an FK references as "New" as well.
                        // To avoid this, attach all FK references first as unmodified (unless they were intended to be new anyway)
                        // then attach the new record.
                        foreach (PropertyInfo fkPropInfo in _cacheAssociationFKProperties[entity.GetType()].Values)
                        {
                            LINQEntityBase fkProp = fkPropInfo.GetValue(entity, null) as LINQEntityBase;
                            if (fkProp != null && fkProp.LINQEntityState != EntityState.New)
                            {
                                try
                                {
                                    targetDataContext.GetTable(fkProp.GetType()).Attach(fkProp, false);
                                }
                                catch
                                {
                                    // do nothing as the entity was already attached.
                                }
                            }
                        }

                        targetDataContext.GetTable(entity.GetEntityType()).InsertOnSubmit(entity);
                    }
                    else if (entity.LINQEntityState == EntityState.Modified || entity.LINQEntityState == EntityState.Detached)
                    {
                        if (entity.LINQEntityOriginalValue != null)
                        {
                            targetDataContext.GetTable(entity.GetEntityType()).Attach(entity, entity.LINQEntityOriginalValue);
                        }
                        else
                        {
                            targetDataContext.GetTable(entity.GetEntityType()).Attach(entity, true);
                        }
                    }

                    if (entity.LINQEntityState == EntityState.Deleted && !entitiesDeleted.Contains(entity))
                    {
                        // Check to see if cascading deletes is allowed
                        if (cascadeDelete)
                        {
                            // Grab the entity tree and reverse it so that this entity is deleted last
                            List <LINQEntityBase> entityTreeReversed = entity.ToEntityTree();
                            entityTreeReversed.Reverse();

                            // Cascade delete children and then this object
                            foreach (LINQEntityBase toDelete in entityTreeReversed)
                            {
                                // Before we try and delete, make sure the entity hasn't been marked to be deleted already
                                // through another relationship linkng this entity in the same sub-tree that is being deleted.
                                if (!entitiesDeleted.Contains(toDelete))
                                {
                                    // Mark for deletion
                                    toDelete.SetAsDeleteOnSubmit();
                                    targetDataContext.GetTable(toDelete.GetEntityType()).Attach(toDelete);
                                    targetDataContext.GetTable(toDelete.GetEntityType()).DeleteOnSubmit(toDelete);

                                    //add deleted entity to a list to make sure we don't delete them twice.
                                    entitiesDeleted.Add(toDelete);
                                }
                            }
                        }
                        else
                        {
                            // Mark for deletion
                            targetDataContext.GetTable(entity.GetEntityType()).Attach(entity);
                            targetDataContext.GetTable(entity.GetEntityType()).DeleteOnSubmit(entity);

                            //add deleted entity to a list to make sure we don't delete them twice.
                            entitiesDeleted.Add(entity);
                        }

                        // if this is the root object, there's no need to do more processing
                        // so just quit the loop
                        if (this == entity)
                        {
                            break;
                        }
                    }
                }

                // Reset this entity as the change tracking root, getting a new copy of all objects
                this.SetAsChangeTrackingRoot(this.LINQEntityKeepOriginal);
            }
            finally
            {
                // Tell each entity that syncronisation is occuring
                foreach (LINQEntityBase entity in entities)
                {
                    entity._isSyncronisingWithDB = false;
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Handles the property changed event sent from child object.
        /// </summary>
        /// <param name="sender">child object</param>
        /// <param name="e">Property Changed arguements</param>
        private void PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Ignore events if syncronising with DB
            if (_isSyncronisingWithDB == true)
            {
                return;
            }

            PropertyInfo propInfo = null;

            // if this object isn't change tracked yet, but it's parent
            // is, this means it's a new object
            if (this.LINQEntityState == EntityState.NotTracked)
            {
                // Check to see if the parent object is change tracked
                // If there is, set the new flag, and tell this new object it's tracked
                if (_cacheAssociationFKProperties[this.GetType()].ContainsKey(e.PropertyName))
                {
                    if (_cacheAssociationFKProperties[this.GetType()].TryGetValue(e.PropertyName, out propInfo))
                    {
                        if (propInfo != null)
                        {
                            LINQEntityBase parentEntity = (LINQEntityBase)propInfo.GetValue(this, null);

                            if (parentEntity != null && parentEntity.LINQEntityState != EntityState.NotTracked)
                            {
                                // loop through this entity and child entities and track them aswell
                                foreach (LINQEntityBase entity in this.ToEntityTree())
                                {
                                    entity.LINQEntityState = EntityState.New;
                                }
                            }
                        }
                    }
                }
            }


            //if the object is not new....
            if (LINQEntityState != EntityState.New)
            {
                // only go into this section if it's change tracked
                if (this.LINQEntityState != EntityState.NotTracked)
                {
                    if (!_cacheAssociationProperties[this.GetType()].ContainsKey(e.PropertyName))
                    {
                        if (_cacheAssociationFKProperties[this.GetType()].ContainsKey(e.PropertyName))
                        {
                            if (_cacheAssociationFKProperties[this.GetType()].TryGetValue(e.PropertyName, out propInfo))
                            {
                                // Parent FK has been set to null, object is now detached.
                                if ((propInfo != null) && (propInfo.GetValue(this, null) == null))
                                {
                                    this._originalEntityValue = this._originalEntityValueTemp;
                                    LINQEntityState           = EntityState.Detached;
                                }
                                else if (LINQEntityState != EntityState.Modified && LINQEntityState != EntityState.Detached)
                                {
                                    this._originalEntityValue = this._originalEntityValueTemp;
                                    LINQEntityState           = EntityState.Modified;
                                }
                            }
                        }
                        else
                        {
                            // if a db generated column has been modified
                            // do nothing
                            bool isDbGenerated = _cacheDBGeneratedProperties[this.GetType()].TryGetValue(e.PropertyName, out propInfo);
                            if (isDbGenerated)
                            {
                                return;
                            }

                            // if the object is already modified and the property values have reverted back to their
                            // original values, set the state back to "Original"
                            if (LINQEntityState == EntityState.Modified && this._originalEntityValue != null)
                            {
                                if (ShallowCompare(this, this._originalEntityValue))
                                {
                                    LINQEntityState           = EntityState.Original;
                                    this._originalEntityValue = null;
                                }
                            }
                            // if the object isn't already modified or detached
                            // set it as modified
                            else if (LINQEntityState != EntityState.Modified && LINQEntityState != EntityState.Detached)
                            {
                                this._originalEntityValue = this._originalEntityValueTemp;
                                LINQEntityState           = EntityState.Modified;
                            }
                        }
                    }
                }
            }

            this._originalEntityValueTemp = null;
        }