Exemplo n.º 1
0
        /// <summary>
        /// Order entities according to principal self navigation count ascendingly
        /// for defining state appropriately.
        /// </summary>
        /// <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. In this case, state of enitites must be defined
        /// in ascending order. In our example, the order is like below:
        /// 1. firstCategory, 2. secondCategory, 3. thirdCateogry.
        /// </example>
        /// <exception cref="ArgumentNullException">
        /// When entityCollection is null.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// When entities in collection do not have same type.
        /// </exception>
        /// <param name="entityCollection">EntityCollection to define state define order.</param>
        /// <returns>Orderer entity collection suiting for defining state.</returns>
        private IEnumerable <object> DefineStateDefineOrder(IEnumerable <object> entityCollection)
        {
            if (entityCollection == null)
            {
                throw new ArgumentNullException(nameof(entityCollection));
            }

            if (!entityCollection.Any())
            {
                return(entityCollection);
            }

            // Get type of entity
            string entityTypeName = entityCollection.First().GetType().Name;

            // All entities must have same type in collection.
            if (entityCollection
                .Any(m => m != null && m.GetType().Name != entityTypeName))
            {
                throw new InvalidOperationException(string.Format(
                                                        "All entities must have same type in collection to define state define order."
                                                        + " Entity type: {0}.",
                                                        entityTypeName));
            }

            // Check if entity has navigation propert to itself. If not, then
            // there is no need to order them, otherwise order.
            IGraphEntityTypeManager entityTypeManager = GetEntityTypeManager(entityTypeName);
            NavigationDetail        navigationDetail  = entityTypeManager.GetNavigationDetail();
            bool hasPrinciplaSelfNavigationProperty   = navigationDetail
                                                        .Relations
                                                        .Any(m => m.Direction == NavigationDirection.From &&
                                                             m.PropertyTypeName == entityTypeName);

            if (!hasPrinciplaSelfNavigationProperty)
            {
                return(entityCollection);
            }

            // Initialize store for ordered entities.
            Dictionary <object, int> principalCountStore =
                new Dictionary <object, int>();

            for (int i = 0; i < entityCollection.Count(); i++)
            {
                dynamic entity             = entityCollection.ElementAt(i);
                int     principalSelfCount = CalculatePrincipalSelfNavigationCount(
                    entity, principalCountStore);
            }

            // Order according to principal slef navigation properties ascendingly.
            return(principalCountStore.OrderBy(m => m.Value).Select(m => m.Key));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Get parents of entity which contains this entity
        /// </summary>
        /// <exception cref="ArgumentNullException">
        /// When entity is null
        /// </exception>
        /// <typeparam name="TEntity">Type of entity.</typeparam>
        /// <param name="entity">Entity to get parent.</param>
        /// <param name="onlyPrincipal">Get only one-to-one parent of entity</param>
        /// <returns>Principal parent of entity.</returns>
        public IEnumerable <object> GetParents <TEntity>(
            TEntity entity,
            bool onlyPrincipal)
            where TEntity : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            string typeName = entity.GetType().Name;
            List <RelationshipMultiplicity> principalRelationshipMultiplicity =
                new List <RelationshipMultiplicity>()
            {
                RelationshipMultiplicity.One,
                RelationshipMultiplicity.ZeroOrOne
            };

            IGraphEntityTypeManager graphEntityTypeMangeer    = GetEntityTypeManager(typeName);
            NavigationDetail        navigationDetailOfCurrent = graphEntityTypeMangeer
                                                                .GetNavigationDetail();

            // Get only those parent property navigation details
            // which has navigation property to this entity
            var parentNavigationDetails = navigationDetailOfCurrent
                                          .Relations
                                          .Where(r => r.Direction == NavigationDirection.From)
                                          .Select(r =>
            {
                IGraphEntityTypeManager typeManager =
                    GetEntityTypeManager(r.PropertyTypeName);
                return(new
                {
                    SourceTypeName = r.PropertyTypeName,
                    Relation = typeManager
                               .GetNavigationDetail()
                               .Relations
                               .FirstOrDefault(pr =>
                                               pr.PropertyTypeName.Equals(typeName) &&
                                               pr.SourceMultiplicity == r.TargetMultiplicity &&
                                               pr.TargetMultiplicity == r.SourceMultiplicity &&
                                               pr.ToKeyNames.SequenceEqual(r.ToKeyNames))
                });
            })
                                          .Where(r => r.Relation != null);

            if (onlyPrincipal)
            {
                parentNavigationDetails = parentNavigationDetails
                                          .Where(r => principalRelationshipMultiplicity
                                                 .Contains(r.Relation.TargetMultiplicity));
            }

            List <string> parentPropertyNames = navigationDetailOfCurrent
                                                .Relations
                                                .Where(r => parentNavigationDetails.Any(p =>
                                                                                        p.SourceTypeName == r.PropertyTypeName &&
                                                                                        p.Relation.SourceMultiplicity == r.TargetMultiplicity &&
                                                                                        p.Relation.TargetMultiplicity == r.SourceMultiplicity &&
                                                                                        p.Relation.ToKeyNames.SequenceEqual(r.ToKeyNames)))
                                                .Select(r => r.PropertyName)
                                                .ToList();

            if (parentPropertyNames != null &&
                parentPropertyNames.Count > 0)
            {
                foreach (string propertyName in parentPropertyNames)
                {
                    object parent = entity.GetPropertyValue(propertyName);
                    if (parent != null)
                    {
                        yield return(parent);
                    }
                }
            }
        }
        /// <summary>
        /// Synchronize keys of entity with matching entity.
        /// <remarks>
        /// In EntityFramework at one-to-one relationships, setting primary key
        /// of parent entity and setting state of entities to Unchanged or to Modified
        /// will also change primary key value of child entity.
        /// But vice-versa is not correct. This means that setting primary key value
        /// of child entity and setting state of entities to Unchanged or to Modified
        /// will not change primary key value of parent key, instead, it resets primary
        /// key value of child entity to its default value, (or to value of primary key
        /// of parent).
        /// This method is for synchronizing PKs of entity and PKs of matching entity and
        /// setting key of parent to key of child entities at one to relationships.
        /// </remarks>
        /// </summary>
        /// <param name="entity">Entity to set pk and parent key values.</param>
        /// <param name="matchingEntity">Found matching entity from underlying source.</param>
        public void SynchronizeKeys(
            TEntity entity,
            TEntity matchingEntity)
        {
            /*
             *
             *
             * Set keys of parent entity to value of child entity at one to one relations.
             *
             *
             */
            IEnumerable <RelationshipDetail> associatedRealtionships = GetForeignKeyDetails()
                                                                       .Where(m => m.ToDetails.ContainerClass.Equals(entity.GetType().Name) &&
                                                                              m.ToDetails.RelationshipMultiplicity == RelationshipMultiplicity.One);

            foreach (RelationshipDetail relationshipDetail in associatedRealtionships)
            {
                // Get parent property name from navigation details using information from foreign keys
                IGraphEntityTypeManager entityTypeManager = ContextFactory
                                                            .GetEntityTypeManager(relationshipDetail.ToDetails.ContainerClass);
                string parentPropertyName = entityTypeManager
                                            .GetNavigationDetail()
                                            .Relations
                                            .Where(n => n.PropertyTypeName.Equals(relationshipDetail.FromDetails.ContainerClass) &&
                                                   n.SourceMultiplicity == relationshipDetail.ToDetails.RelationshipMultiplicity &&
                                                   n.TargetMultiplicity == relationshipDetail.FromDetails.RelationshipMultiplicity)
                                            .Select(n => n.PropertyName)
                                            .FirstOrDefault();

                dynamic parent = entity.GetPropertyValue(parentPropertyName);

                if (parent != null)
                {
                    for (int i = 0; i < relationshipDetail.FromDetails.Keys.Count; i++)
                    {
                        // Get matching from and to key names
                        string fromKeyName = relationshipDetail.FromDetails.Keys[i];
                        string toKeyName   = relationshipDetail.ToDetails.Keys[i];

                        ReflectionExtensions.SetPropertyValue(parent,
                                                              fromKeyName,
                                                              matchingEntity.GetPropertyValue(toKeyName));
                    }
                }
            }

            /*
             *   Description:
             *     PK value shuold be changed by using
             *     context.Entry(entity).Property(pkName).CurrentValue = pkValue;
             *     becasue setting value by entity.pkName = pkValue will not synchronize
             *     it with dependent navigation properties automatically but prior method
             *     will do it.
             *     Primary key values of entity itself must be changed after
             *     principal parent keys has been synchronized. Because changing
             *     primary key value of entity using
             *     context.Entry(entity).Property(pkName).CurrentValue = pkValue
             *     set principal parent navigation property to null.
             */
            IEnumerable <string> primaryKeyNames = GetPrimaryKeys();

            DbEntityEntry current = Context.Entry(entity);

            foreach (string primaryKey in primaryKeyNames)
            {
                current.Property(primaryKey).CurrentValue =
                    matchingEntity.GetPropertyValue(primaryKey);
            }
        }