<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Reflection;
using System.Threading;
using TrackableEntities.Common;
#if EF_6
using System.Threading.Tasks;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Core.Metadata.Edm;
#else
using System.Data;
using System.Data.Objects;
using System.Data.Metadata.Edm;
#endif

#if EF_6
namespace TrackableEntities.EF6
#else
namespace TrackableEntities.EF5
#endif
{
    /// <summary>
    /// Extension methods for DbContext to persist trackable entities.
    /// </summary>
    public static class DbContextExtensions
    {
        /// <summary>
        /// Update entity state on DbContext for an object graph.
        /// </summary>
        /// <param name="context">Used to query and save changes to a database</param>
        /// <param name="item">Object that implements ITrackable</param>
        public static void ApplyChanges(this DbContext context, ITrackable item)
        {
            // Recursively set entity state for DbContext entry
            ApplyChanges(context, item, null, new ObjectVisitationHelper(), null);
        }

        /// <summary>
        /// Update entity state on DbContext for more than one object graph.
        /// </summary>
        /// <param name="context">Used to query and save changes to a database</param>
        /// <param name="items">Objects that implement ITrackable</param>
        public static void ApplyChanges(this DbContext context, IEnumerable<ITrackable> items)
        {
            // Apply changes to collection of items
            foreach (var item in items)
                ApplyChanges(context, item, null, new ObjectVisitationHelper(), null);
        }

        private static void ApplyChanges(this DbContext context,
            ITrackable item, ITrackable parent, ObjectVisitationHelper visitationHelper,
            string propertyName, TrackingState? state = null)
        {
            // Prevent endless recursion
            if (!visitationHelper.TryVisit(item)) return;

            // Check for null args
            if (context == null)
                throw new ArgumentNullException("context");
            if (item == null)
                throw new ArgumentNullException("item");

            // If M-M child, set relationship for added or deleted items
            if (parent != null && propertyName != null
                && (context.IsRelatedProperty(parent.GetType(), 
                propertyName, RelationshipType.ManyToMany)))
            {
                // If parent is added set tracking state to match
                var trackingState = item.TrackingState;
                if (parent.TrackingState == TrackingState.Added)
                    trackingState = parent.TrackingState;

                // If tracking state is added set entity to unchanged,
                // then add or delete relation.
                if (trackingState == TrackingState.Added
                    || trackingState == TrackingState.Deleted)
                {
                    context.Entry(item).State = item.TrackingState == TrackingState.Modified
                        ? EntityState.Modified
                        : EntityState.Unchanged;                    
                    context.SetRelationshipState(item, parent, propertyName,
                        trackingState.ToEntityState());
                }
                else
                {
                    // Set entity state for modified or unchanged
                    if (item.TrackingState == TrackingState.Modified)
                        context.Entry(item).State = EntityState.Modified;
                    else if (item.TrackingState == TrackingState.Unchanged)
                        context.Entry(item).State = EntityState.Unchanged;
                }

                // Set state for child collections
                context.ApplyChangesOnProperties(item, visitationHelper);
                return;
            }   

            // Exit if parent is added or deleted,
            // and it's not a M-1 relation
            if (parent != null
                && (parent.TrackingState == TrackingState.Added
                    || parent.TrackingState == TrackingState.Deleted)
                && !context.IsRelatedProperty(parent.GetType(),
                    propertyName, RelationshipType.ManyToOne))
                return;

            // If it is a M-1 relation and item state is deleted,
            // set to unchanged and exit
            if (parent != null
                && (context.IsRelatedProperty(parent.GetType(),
                    propertyName, RelationshipType.ManyToOne)
                    && item.TrackingState == TrackingState.Deleted))
            {
                context.Entry(item).State = EntityState.Unchanged;
                return;
            }

            // Set state to Added on parent only
            if (item.TrackingState == TrackingState.Added
                && (state == null || state == TrackingState.Added))
            {
                context.Entry(item).State = EntityState.Added;
                context.ApplyChangesOnProperties(item, visitationHelper);
                return;
            }

            // Set state to Deleted on children and parent
            if (item.TrackingState == TrackingState.Deleted
                && (state == null || state == TrackingState.Deleted))
            {
                context.SetChanges(item, EntityState.Unchanged, visitationHelper.Clone()); // Clone to avoid interference
                context.SetChanges(item, EntityState.Deleted, visitationHelper);
                return;
            }

            // Set entity state
            if (state == null
                || state == TrackingState.Unchanged 
                || state == TrackingState.Modified)
            {
                // Set added state for reference or child properties
                context.ApplyChangesOnProperties(item, visitationHelper.Clone(), TrackingState.Added); // Clone to avoid interference

                // Set modified properties
                if (item.TrackingState == TrackingState.Modified
                    && (state == null || state == TrackingState.Modified)
                    && item.ModifiedProperties != null
                    && item.ModifiedProperties.Count > 0)
                {
                    // Mark modified properties
                    context.Entry(item).State = EntityState.Unchanged;
                    foreach (var property in item.ModifiedProperties)
                        context.Entry(item).Property(property).IsModified = true;
                }
                else<commit_msg>Fixed: Allowed recursion into a section of code by adding line 144 to DbContextExtensions.ApplyChanges.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Reflection;
using System.Threading;
using TrackableEntities.Common;
#if EF_6
using System.Threading.Tasks;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Core.Metadata.Edm;
#else
using System.Data;
using System.Data.Objects;
using System.Data.Metadata.Edm;
#endif

#if EF_6
namespace TrackableEntities.EF6
#else
namespace TrackableEntities.EF5
#endif
{
    /// <summary>
    /// Extension methods for DbContext to persist trackable entities.
    /// </summary>
    public static class DbContextExtensions
    {
        /// <summary>
        /// Update entity state on DbContext for an object graph.
        /// </summary>
        /// <param name="context">Used to query and save changes to a database</param>
        /// <param name="item">Object that implements ITrackable</param>
        public static void ApplyChanges(this DbContext context, ITrackable item)
        {
            // Recursively set entity state for DbContext entry
            ApplyChanges(context, item, null, new ObjectVisitationHelper(), null);
        }

        /// <summary>
        /// Update entity state on DbContext for more than one object graph.
        /// </summary>
        /// <param name="context">Used to query and save changes to a database</param>
        /// <param name="items">Objects that implement ITrackable</param>
        public static void ApplyChanges(this DbContext context, IEnumerable<ITrackable> items)
        {
            // Apply changes to collection of items
            foreach (var item in items)
                ApplyChanges(context, item, null, new ObjectVisitationHelper(), null);
        }

        private static void ApplyChanges(this DbContext context,
            ITrackable item, ITrackable parent, ObjectVisitationHelper visitationHelper,
            string propertyName, TrackingState? state = null)
        {
            // Prevent endless recursion
            if (!visitationHelper.TryVisit(item)) return;

            // Check for null args
            if (context == null)
                throw new ArgumentNullException("context");
            if (item == null)
                throw new ArgumentNullException("item");

            // If M-M child, set relationship for added or deleted items
            if (parent != null && propertyName != null
                && (context.IsRelatedProperty(parent.GetType(), 
                propertyName, RelationshipType.ManyToMany)))
            {
                // If parent is added set tracking state to match
                var trackingState = item.TrackingState;
                if (parent.TrackingState == TrackingState.Added)
                    trackingState = parent.TrackingState;

                // If tracking state is added set entity to unchanged,
                // then add or delete relation.
                if (trackingState == TrackingState.Added
                    || trackingState == TrackingState.Deleted)
                {
                    context.Entry(item).State = item.TrackingState == TrackingState.Modified
                        ? EntityState.Modified
                        : EntityState.Unchanged;                    
                    context.SetRelationshipState(item, parent, propertyName,
                        trackingState.ToEntityState());
                }
                else
                {
                    // Set entity state for modified or unchanged
                    if (item.TrackingState == TrackingState.Modified)
                        context.Entry(item).State = EntityState.Modified;
                    else if (item.TrackingState == TrackingState.Unchanged)
                        context.Entry(item).State = EntityState.Unchanged;
                }

                // Set state for child collections
                context.ApplyChangesOnProperties(item, visitationHelper);
                return;
            }   

            // Exit if parent is added or deleted,
            // and it's not a M-1 relation
            if (parent != null
                && (parent.TrackingState == TrackingState.Added
                    || parent.TrackingState == TrackingState.Deleted)
                && !context.IsRelatedProperty(parent.GetType(),
                    propertyName, RelationshipType.ManyToOne))
                return;

            // If it is a M-1 relation and item state is deleted,
            // set to unchanged and exit
            if (parent != null
                && (context.IsRelatedProperty(parent.GetType(),
                    propertyName, RelationshipType.ManyToOne)
                    && item.TrackingState == TrackingState.Deleted))
            {
                context.Entry(item).State = EntityState.Unchanged;
                return;
            }

            // Set state to Added on parent only
            if (item.TrackingState == TrackingState.Added
                && (state == null || state == TrackingState.Added))
            {
                context.Entry(item).State = EntityState.Added;
                context.ApplyChangesOnProperties(item, visitationHelper);
                return;
            }

            // Set state to Deleted on children and parent
            if (item.TrackingState == TrackingState.Deleted
                && (state == null || state == TrackingState.Deleted))
            {
                context.SetChanges(item, EntityState.Unchanged, visitationHelper.Clone()); // Clone to avoid interference
                context.SetChanges(item, EntityState.Deleted, visitationHelper);
                return;
            }

            // Set entity state
            if (state == null
                || state == TrackingState.Unchanged 
                || state == TrackingState.Modified
                || (state == TrackingState.Added && item.TrackingState != TrackingState.Deleted))
            {
                // Set added state for reference or child properties
                context.ApplyChangesOnProperties(item, visitationHelper.Clone(), TrackingState.Added); // Clone to avoid interference

                // Set modified properties
                if (item.TrackingState == TrackingState.Modified
                    && (state == null || state == TrackingState.Modified)
                    && item.ModifiedProperties != null
                    && item.ModifiedProperties.Count > 0)
                {
                    // Mark modified properties
                    context.Entry(item).State = EntityState.Unchanged;
                    foreach (var property in item.ModifiedProperties)
                        context.Entry(item).Property(property).IsModified = true;
                }
                else
{'fraction_non_alphanumeric': 0.08541651483128052, 'fraction_numerical': 0.001238976750965673, 'mean_word_length': 1.7006494784491242, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4224628', 'n_tokens_mistral': 3234, 'n_tokens_neox': 3026, 'n_words': 882}