Ejemplo n.º 1
0
        /// <summary>
        /// Calculate count of principal self navigation properties for
        /// ordering to be able to successfully define order.
        /// </summary>
        /// <remarks>
        /// If Category has principal navigation to itself named ParentCategory, we need to
        /// find count of them to be able to define order appropriately. We need to define state of
        /// them in ascending order.
        /// </remarks>
        /// <example>
        /// firstCategory which has null value on ParentCategory property has 0 principal self
        /// navigation count. If ParentCategory of secondCategory is firstCategory, then secondCategory
        /// has 1 principal self navigation count, if ParentCategory of thirdCateogry is secondCategory,
        /// then it has 2 principal self navigaiton count.
        /// </example>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="entity">Entity to find principla self navigaiton count.</param>
        /// <param name="store">Store to add calculated count and check first.</param>
        /// <returns>Count of principal self navigation count.</returns>
        private int CalculatePrincipalSelfNavigationCount <TEntity>(
            TEntity entity,
            Dictionary <object, int> store)
            where TEntity : class
        {
            if (store.ContainsKey(entity))
            {
                return(store[entity]);
            }

            string typeName = entity.GetType().Name;

            // Get navigation details and find properties which is refers to itslef.
            IGraphEntityManager <TEntity> entityManager          = GetEntityManager <TEntity>();
            NavigationDetail navigationDetail                    = entityManager.GetNavigationDetail();
            List <string>    selfReferringPrincipalPropertyNames = navigationDetail
                                                                   .Relations
                                                                   .Where(m => m.Direction == NavigationDirection.From &&
                                                                          m.PropertyTypeName == typeName)
                                                                   .Select(m => m.PropertyName)
                                                                   .ToList();

            // Loop through principal navigation properties and add their count.
            int principalSelfCount = 0;

            foreach (string propertyName in selfReferringPrincipalPropertyNames)
            {
                TEntity principalEntity = entity.GetPropertyValue(propertyName) as TEntity;

                // If principla entity is null then continue
                if (principalEntity == null)
                {
                    continue;
                }

                // Othewise calculate principal count as 1 + count for principal entity
                principalSelfCount++;
                principalSelfCount += CalculatePrincipalSelfNavigationCount(principalEntity, store);
            }

            store.Add(entity, principalSelfCount);
            return(principalSelfCount);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Define state of entity. If entity already exists in the source
        /// set and values has not been altered set the state to Unchanged,
        /// else if values has been changed set the state of changed properties
        /// to Modified, otherwise set the state to Added.
        /// </summary>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="entity">Entity to define state of.</param>
        private void DefineState <TEntity>(TEntity entity)
            where TEntity : class
        {
            // If entity has been detached, then do not try to define its state
            if (Context.Entry(entity).State == EntityState.Detached)
            {
                return;
            }

            IGraphEntityManager <TEntity> entityManager = GetEntityManager <TEntity>();
            // Get matching entity.
            TEntity matchingEntity = entityManager.GetMatchingEntity(entity);

            if (matchingEntity != null)
            {
                /*
                 * If entity is already in the context, for example if
                 * entity has been retrieved in program layer using .FirstOrDefault()
                 * without calling .AsNoTracking()
                 * then this entity is tracked by entity framework. If entity retrieved
                 * using this method then, some properties have been altered, setting its
                 * state to Unchanged will undo all changes. It means that made alterations
                 * will be lost, and all current values will be replaced by original values.
                 * And keys will not be altered in child entities by settting state to Unchanged
                 * which has been done below in this code ( after dealing with duplicates ).
                 * As a workaround, I detach and readd entity to context to clear original values.
                 */
                if (Context.Entry(entity).State != EntityState.Added)
                {
                    Context.Entry(entity).State = EntityState.Detached;
                    Context.Entry(entity).State = EntityState.Added;
                }

                entityManager.SynchronizeKeys(entity, matchingEntity);
            }

            // Deal with duplicates before proceeding
            DealWithDuplicates(entity);

            if (matchingEntity != null)
            {
                Context.Entry(entity).State = EntityState.Unchanged;
                entityManager.DetectPropertyChanges(entity, matchingEntity);
            }
            else
            {
                // When priamry keys of entity is not store generated
                // and state of entity is added, value of primary
                // keys will not reflected at child entities.
                // If primary keys of  entity has values different
                // than default values then set its state to
                // unchanged to fixup keys to solve this issue
                // and after that set state to Added
                var primaryKeys = entityManager.GetPrimaryKeys();
                if (!entity.HasDefaultValues(entityManager
                                             .GetPrimaryKeys()))
                {
                    Context.Entry(entity).State = EntityState.Unchanged;
                }

                Context.Entry(entity).State = EntityState.Added;
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Add all child or parents entities related to given entity
        /// and entity itself to the relatedEntityList.
        /// </summary>
        /// <exception cref="ArgumentNullException">
        /// When entity or relatedEntityList is null.
        /// </exception>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="entity">Entity to get all related entities.</param>
        /// <param name="relatedEntityList">List of entities to add.</param>
        public void GetAllEntities <TEntity>(
            TEntity entity,
            List <object> relatedEntityList)
            where TEntity : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            if (relatedEntityList == null)
            {
                throw new ArgumentNullException(nameof(relatedEntityList));
            }

            if (!relatedEntityList.Contains(entity))
            {
                relatedEntityList.Add(entity);
            }

            IGraphEntityManager <TEntity> graphEntityManager = GetEntityManager <TEntity>();
            NavigationDetail navigationDetail = graphEntityManager
                                                .GetNavigationDetail();

            List <PropertyInfo> navigationPropeties = navigationDetail
                                                      .Relations
                                                      .Select(n => entity.GetProperty(n.PropertyName))
                                                      .ToList();

            if (navigationPropeties != null &&
                navigationPropeties.Count > 0)
            {
                foreach (PropertyInfo childEntityProperty in navigationPropeties)
                {
                    if (childEntityProperty.PropertyType.IsCollectionType())
                    {
                        // If child entity is collection get all entities inside this collection.
                        IEnumerable <object> enumerableChildEntity =
                            entity.GetPropertyValue(childEntityProperty.Name) as IEnumerable <object>;

                        if (enumerableChildEntity != null)
                        {
                            for (int i = 0; i < enumerableChildEntity.Count(); i++)
                            {
                                dynamic childEntity = enumerableChildEntity.ElementAt(i);
                                if (childEntity != null &&
                                    !relatedEntityList.Contains(childEntity))
                                {
                                    GetAllEntities(
                                        childEntity,
                                        relatedEntityList);
                                }
                            }
                        }
                    }
                    else
                    {
                        // If child entity is not collection get its own.
                        dynamic childEntity = entity.GetPropertyValue(childEntityProperty.Name);

                        if (childEntity != null &&
                            !relatedEntityList.Contains(childEntity))
                        {
                            GetAllEntities(
                                childEntity,
                                relatedEntityList);
                        }
                    }
                }
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Set property of parent entity to targetValue
        /// which has property value equals to currentValue.
        /// </summary>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="currentValue">Current value of property to replace.</param>
        /// <param name="targetValue">Target value to set value of property of parent.</param>
        private void ReplaceEntitiesInParents <TEntity>(
            TEntity currentValue,
            TEntity targetValue)
            where TEntity : class
        {
            IGraphEntityManager <TEntity> graphEntityManager = GetEntityManager <TEntity>();
            NavigationDetail navigationDetailOfCurrent       = graphEntityManager
                                                               .GetNavigationDetail();

            // Get parent properties of entity.
            // Properties which have navigation property type of which
            // is type of entity. Ignore navigation properties
            // which are mutual navigation properties with entity itself.
            string typeName = currentValue.GetType().Name;
            List <NavigationDetail> parentNavigationDetails = GetNavigationDetails()
                                                              .Select(n => new NavigationDetail()
            {
                SourceTypeName = n.SourceTypeName,
                Relations      = n.Relations
                                 .Where(r => r.PropertyTypeName.Equals(typeName) &&
                                        r.SourceMultiplicity == RelationshipMultiplicity.Many &&
                                        !navigationDetailOfCurrent
                                        .Relations
                                        .Any(c => c.PropertyTypeName.Equals(n.SourceTypeName) &&
                                             c.TargetMultiplicity == r.SourceMultiplicity &&
                                             c.FromKeyNames.SequenceEqual(r.FromKeyNames) &&
                                             c.ToKeyNames.SequenceEqual(r.ToKeyNames)))
                                 .ToList()
            })
                                                              .Where(n => n.Relations != null &&
                                                                     n.Relations.Count() > 0)
                                                              .ToList();

            // Get assembly to be able to get types according to type name
            Assembly entityAssembly = currentValue.GetType().Assembly;

            if (parentNavigationDetails != null && parentNavigationDetails.Count() > 0)
            {
                foreach (NavigationDetail parentNavigation in parentNavigationDetails)
                {
                    Type parentType = entityAssembly.GetTypes()
                                      .FirstOrDefault(t => t.Name.Equals(parentNavigation.SourceTypeName));

                    // Get local set of parent
                    IEnumerable <object> localParentSet = Context
                                                          .Set(parentType)
                                                          .Local
                                                          .CastToGeneric();

                    foreach (NavigationRelation navigationRelation in parentNavigation.Relations)
                    {
                        PropertyInfo childProperty =
                            parentType.GetProperty(navigationRelation.PropertyName);

                        if (!childProperty.PropertyType.IsCollectionType())
                        {
                            // Get all parent entities which have current entity inside
                            var containerParentCollection = localParentSet
                                                            .Where(m => m.GetPropertyValue(navigationRelation.PropertyName) != null &&
                                                                   m.GetPropertyValue(navigationRelation.PropertyName).Equals(currentValue))
                                                            .ToList();

                            // If collection is empty then skip.
                            if (containerParentCollection == null ||
                                containerParentCollection.Count == 0)
                            {
                                continue;
                            }

                            foreach (var containerParent in containerParentCollection)
                            {
                                // If parent is null then skip.
                                if (containerParent == null)
                                {
                                    continue;
                                }

                                // If parent with property value of currentValue found replace values

                                /*
                                 * If duplicate entity is in the entity, state of which has already
                                 * been defined and if state of this parent entity is Unchanged or
                                 * Modified, trying to change navigation property of this parent entity
                                 * will throw InvalidOperationException with following message:
                                 * "A referential integrity constraint violation occurred:
                                 *  A primary key property that is a part of referential integrity constraint
                                 *  cannot be changed when the dependent object is Unchanged unless it is being
                                 *  set to the association's principal object. The principal object must
                                 *  be tracked and not marked for deletion."
                                 * As a workaround I am storing current state of parent entity, changing the
                                 * state to Added, then replacing duplicate entity and in the end
                                 * I set state of parent entity to stored current state.
                                 */

                                // Store current state
                                var currentState = Context.Entry(containerParent).State;
                                // Change state to added
                                Context.Entry(containerParent).State = EntityState.Added;

                                // Replace value through
                                // context.Entry(containerParent).Member(propertyName).CurrentValue
                                // because otherwise EntityFramework will not be able to track entities
                                Context.Entry(containerParent)
                                .Member(navigationRelation.PropertyName).CurrentValue = targetValue;

                                // Restore state to current state
                                Context.Entry(containerParent).State = currentState;
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Find duplicate entities in the local context, perform needed operations
        /// to be able insert or update value appropriately and detach duplicates.
        /// </summary>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="entity">Entity to find duplicates of.</param>
        private void DealWithDuplicates <TEntity>(
            TEntity entity)
            where TEntity : class
        {
            IGraphEntityManager <TEntity>      graphEntityManager = GetEntityManager <TEntity>();
            Expression <Func <TEntity, bool> > filterExpression   =
                graphEntityManager.ConstructFilterExpression(entity, FilterType.IdOrUnique);


            bool duplicatesFoundAndEliminatedFlag = false;

            if (filterExpression != null)
            {
                /// This is used instead of Context.Set<TEntity>().Local
                /// to improve perfermance.
                var duplicateEntityFromLocal = Context
                                               .ChangeTracker
                                               .Entries <TEntity>()
                                               .Select(m => m.Entity)
                                               .Where(filterExpression.Compile())
                                               .ToList();

                if (duplicateEntityFromLocal.Any())
                {
                    dynamic uppermostPrincipalParentEntity =
                        GetUppermostPrincipalParent(entity);

                    foreach (TEntity duplicate in duplicateEntityFromLocal.Where(d => d != entity))
                    {
                        dynamic uppermostPrincipalParentDuplicate =
                            GetUppermostPrincipalParent(duplicate);

                        // If duplicate value is not in a collection
                        // we need to replace its duplicate value with
                        // original one in parent entity. Becasue as we detach the
                        // duplicate entry afterwards, not setting its value with original
                        // one will not send it to database where it has to be sent
                        ReplaceEntitiesInParents(
                            uppermostPrincipalParentDuplicate,
                            uppermostPrincipalParentEntity);

                        /*
                         * ******************************************************
                         * TO DO: ALSO CONSIDER WHEN ONE DUPLICATE IS IN A
                         * NON-COLLECTION ENTITY AND ANOTHER IS IN A COLLECTION
                         * ******************************************************
                         */

                        DetachWithDependants(uppermostPrincipalParentDuplicate, true);
                    }

                    duplicatesFoundAndEliminatedFlag = true;
                }
            }

            // If no duplicaes has been found, get uppermost principal
            // parent entity and if it is not entity itself, try to find
            // and eliminate duplicates of parent
            if (!duplicatesFoundAndEliminatedFlag)
            {
                dynamic uppermostPrincipalParentEntity =
                    GetUppermostPrincipalParent(entity);

                if (!uppermostPrincipalParentEntity.Equals(entity))
                {
                    DealWithDuplicates(uppermostPrincipalParentEntity);
                }
            }
        }