<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Reflection;

namespace AweSamNet.Data
{
    /// <summary>
    /// Enhances existing IQueriables with all required .Inclue(...) invocations.  
    /// </summary>
    /// <remarks>
    /// Profiles can be used to map an entity that may or may not already be mapped to a different class property altogether.  
    /// An example of this might be:
    /// 
    ///     1. YourModels.Invoice --> Db.Invoice
    ///     2. YourModels.Invoice --> Db.InvoiceArchived
    ///     
    /// In this case, the default profile would handle the mapping of case 1 while a seperate profile (Ex: "Archives") might handle the mapping of case 2.
    /// </remarks>
    public static class EFEagerLoad
    {
        const string PROPERTY_ALREADY_MAPPED_MESSAGE = "Property: {0}.{1} is already mapped to Property: {2}.{3}";
        const string DEFAULT_PROFILE_NAME = "Default";

        static object syncKey = new object();

        private static int _MaxRecursionLevel = 3;
        /// <summary>
        /// Gets/Sets the maximum number of recursion levels to eager load.
        /// </summary>
        public static int MaxRecursionLevel
        {
            get
            {
                return _MaxRecursionLevel;
            }
            set
            {
                _MaxRecursionLevel = value;
            }
        }

        /// <summary>
        /// Represents a mapping from one Type's property to another Type's property.
        /// </summary>
        private class Mapping
        {
            public Mapping(PropertyInfo entity1, PropertyInfo entity2)
            {
                this.Entity1 = entity1;
                this.Entity2 = entity2;
            }

            public PropertyInfo Entity1 { get; private set; }
            public PropertyInfo Entity2 { get; private set; }

            public bool Contains(PropertyInfo property)
            {
                return (this.Entity1.DeclaringType.Equals(property.DeclaringType) && this.Entity1.Name.Equals(property.Name))
                    || (this.Entity2.DeclaringType.Equals(property.DeclaringType) && this.Entity2.Name.Equals(property.Name));
            }
        }

        /// <summary>
        /// Profile property mappings.
        /// </summary>
        private static Dictionary<string, List<Mapping>> Mappings = new Dictionary<string,List<Mapping>>();

        /// <summary>
        /// Adds a new mapping between one class property to another class property.
        /// </summary>
        /// <typeparam name="TEntity1">Type of Entity 1.</typeparam>
        /// <typeparam name="TEntity2">Type of Entity 2.</typeparam>
        /// <param name="entity1Selector">Selector for Entity 1.</param>
        /// <param name="entity2Selector">Selector for Entity 2.</param>
        /// <param name="profile">The profile to add the mapping to (Empty: Default profile).</param>
        /// <exception cref="ArgumentException">ArgumentException: Thrown when a passed selector has already been mapped for the given profile.</exception>
        public static void AddEFMapping<TEntity1, TEntity2>(Expression<Func<TEntity1, object>> entity1Selector, Expression<Func<TEntity2, object>> entity2Selector, string profile = DEFAULT_PROFILE_NAME)
        {
            lock (syncKey)
            {
                //see if the mapping profile exists
                if (!Mappings.Keys.Contains(profile))
                {
                    Mappings[profile] = new List<Mapping>();
                }

                var profileMappings = Mappings[profile];

                var entity1 = GetSelectorPropertyInfo(entity1Selector);
                var entity2 = GetSelectorPropertyInfo(entity2Selector);

                bool mappingAlreadyExists = false;

                //verify that it does not already exist
                foreach (var item in profileMappings)
                {
                    if (item.Contains(entity1) || item.Contains(entity2))
                    {
                        throw new ArgumentException(String.Format(PROPERTY_ALREADY_MAPPED_MESSAGE
                            , item.Entity1.DeclaringType.Name, item.Entity1.Name
                            , item.Entity2.DeclaringType.Name, item.Entity2.Name));
                    }
                }

                if (!mappingAlreadyExists) //if it already exists then ignore this mapping.
                {
                    profileMappings.Add(new Mapping(entity1, entity2));
                }
            }
        }

        /// <summary>
        /// Removes all mappings for the given selector in the profile specified.
        /// </summary>
        /// <typeparam name="TEntity">Type of Entity in mapping.</typeparam>
        /// <param name="entitySelector">Selector of entity in mapping.</param>
        /// <param name="profile">The profile to add the mapping to (Empty: Default profile).</param>
        public static void RemoveMappingsFor<TEntity>(Expression<Func<TEntity, object>> entitySelector, string profile = DEFAULT_PROFILE_NAME)
        {
            lock (syncKey)
            {
                var entity = GetSelectorPropertyInfo(entitySelector);

                List<Mapping> mappingsToRemove = new List<Mapping>();
                if (!Mappings.Keys.Contains(profile))
                {
                    return;
                }

                var profileMappings = Mappings[profile];
<commit_msg>- Added optional exception pass-through to AddEFMapping(...) (default to false)<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Reflection;

namespace AweSamNet.Data
{
    /// <summary>
    /// Enhances existing IQueriables with all required .Inclue(...) invocations.  
    /// </summary>
    /// <remarks>
    /// Profiles can be used to map an entity that may or may not already be mapped to a different class property altogether.  
    /// An example of this might be:
    /// 
    ///     1. YourModels.Invoice --> Db.Invoice
    ///     2. YourModels.Invoice --> Db.InvoiceArchived
    ///     
    /// In this case, the default profile would handle the mapping of case 1 while a seperate profile (Ex: "Archives") might handle the mapping of case 2.
    /// </remarks>
    public static class EFEagerLoad
    {
        const string PROPERTY_ALREADY_MAPPED_MESSAGE = "Property: {0}.{1} is already mapped to Property: {2}.{3}";
        const string DEFAULT_PROFILE_NAME = "Default";

        static object syncKey = new object();

        private static int _MaxRecursionLevel = 3;
        /// <summary>
        /// Gets/Sets the maximum number of recursion levels to eager load.
        /// </summary>
        public static int MaxRecursionLevel
        {
            get
            {
                return _MaxRecursionLevel;
            }
            set
            {
                _MaxRecursionLevel = value;
            }
        }

        /// <summary>
        /// Represents a mapping from one Type's property to another Type's property.
        /// </summary>
        private class Mapping
        {
            public Mapping(PropertyInfo entity1, PropertyInfo entity2)
            {
                this.Entity1 = entity1;
                this.Entity2 = entity2;
            }

            public PropertyInfo Entity1 { get; private set; }
            public PropertyInfo Entity2 { get; private set; }

            public bool Contains(PropertyInfo property)
            {
                return (this.Entity1.DeclaringType.Equals(property.DeclaringType) && this.Entity1.Name.Equals(property.Name))
                    || (this.Entity2.DeclaringType.Equals(property.DeclaringType) && this.Entity2.Name.Equals(property.Name));
            }
        }

        /// <summary>
        /// Profile property mappings.
        /// </summary>
        private static Dictionary<string, List<Mapping>> Mappings = new Dictionary<string,List<Mapping>>();

        /// <summary>
        /// Adds a new mapping between one class property to another class property.  Returns false if unsuccessful.
        /// </summary>
        /// <typeparam name="TEntity1">Type of Entity 1.</typeparam>
        /// <typeparam name="TEntity2">Type of Entity 2.</typeparam>
        /// <param name="entity1Selector">Selector for Entity 1.</param>
        /// <param name="entity2Selector">Selector for Entity 2.</param>
        /// <param name="exceptionPassthrough">If true, throws an exception with detailed message when a property is already mapped.</param>
        /// <exception cref="ArgumentException">ArgumentException: Thrown when a passed selector has already been mapped for the given profile.</exception>
        /// <returns>Returns false if unsuccessful.</returns>
        public static bool AddEFMapping<TEntity1, TEntity2>(Expression<Func<TEntity1, object>> entity1Selector, Expression<Func<TEntity2, object>> entity2Selector, bool exceptionPassthrough = false)
        {
            return AddEFMapping(entity1Selector, entity2Selector, DEFAULT_PROFILE_NAME, exceptionPassthrough);
        }

        /// <summary>
        /// Adds a new mapping between one class property to another class property.  Returns false if unsuccessful.
        /// </summary>
        /// <typeparam name="TEntity1">Type of Entity 1.</typeparam>
        /// <typeparam name="TEntity2">Type of Entity 2.</typeparam>
        /// <param name="entity1Selector">Selector for Entity 1.</param>
        /// <param name="entity2Selector">Selector for Entity 2.</param>
        /// <param name="profile">The profile to add the mapping to (Empty: Default profile).</param>
        /// <param name="exceptionPassthrough">If true, throws an exception with detailed message when a property is already mapped.</param>
        /// <exception cref="ArgumentException">ArgumentException: Thrown when a passed selector has already been mapped for the given profile.</exception>
        /// <returns>Returns false if unsuccessful.</returns>
        public static bool AddEFMapping<TEntity1, TEntity2>(Expression<Func<TEntity1, object>> entity1Selector, Expression<Func<TEntity2, object>> entity2Selector, string profile = DEFAULT_PROFILE_NAME, bool exceptionPassthrough = false)
        {
            lock (syncKey)
            {
                //see if the mapping profile exists
                if (!Mappings.Keys.Contains(profile))
                {
                    Mappings[profile] = new List<Mapping>();
                }

                var profileMappings = Mappings[profile];

                var entity1 = GetSelectorPropertyInfo(entity1Selector);
                var entity2 = GetSelectorPropertyInfo(entity2Selector);

                bool mappingAlreadyExists = false;

                //verify that it does not already exist
                foreach (var item in profileMappings)
                {
                    if (item.Contains(entity1) || item.Contains(entity2))
                    {
                        //if exceptionPassthrough is enabled then throw an exception.
                        if (exceptionPassthrough)
                        {
                            throw new ArgumentException(String.Format(PROPERTY_ALREADY_MAPPED_MESSAGE
                                , item.Entity1.DeclaringType.Name, item.Entity1.Name
                                , item.Entity2.DeclaringType.Name, item.Entity2.Name));
                        }
                        return false;
                    }
                }

                if (!mappingAlreadyExists) //if it already exists then ignore this mapping.
                {
                    profileMappings.Add(new Mapping(entity1, entity2));
                }
            }

            return true;
        }

        /// <summary>
        /// Removes all mappings for the given selector in the profile specified.
        /// </summary>
        /// <typeparam name="TEntity">Type of Entity in mapping.</typeparam>
        /// <param name="entitySelector">Selector of entity in mapping.</param>
        /// <param name="profile">The profile to add the mapping to (Empty: Default profile).</param>
        public static void RemoveMappingsFor<TEntity>(Expression<Func<TEntity, object>> entitySelector, string profile = DEFAULT_PROFILE_NAME)
        {
            lock (syncKey)
            {
                var entity = GetSelectorPropertyInfo(entitySelector);

                List<Mapping> mappingsToRemove = new List<Mapping>();
                if (!Mappings.Keys.Contains(profile))
                {
                    return;
                }

                var profileMappings = Mappings[profile];

{'fraction_non_alphanumeric': 0.09866168689698102, 'fraction_numerical': 0.008558979147214442, 'mean_word_length': 1.8909131803868646, 'pattern_counts': {'":': 0, '<': 122, '<?xml version=': 0, '>': 126, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6021551', 'n_tokens_mistral': 3242, 'n_tokens_neox': 3077, 'n_words': 919}