/// <summary>
 /// Get cache datas core
 /// </summary>
 /// <param name="query">Query condition</param>
 /// <param name="size">Return data size</param>
 /// <returns></returns>
 QueryDataResult <T> GetCacheDatas <T>(IQuery query, int size)
 {
     //complex query force query database
     if (query?.IsComplexQuery ?? false)
     {
         return(QueryDataResult <T> .Default("IQuery is a complex query object,not query cache data"));
     }
     //query all data
     if (query?.NoneCondition ?? true)
     {
         List <T> datas         = null;
         bool     queryDatabase = true;
         if (size > 0)
         {
             datas         = GetCacheDatasByType <T>(query, query?.QuerySize ?? 1);
             queryDatabase = datas?.Count < size;
         }
         return(new QueryDataResult <T>()
         {
             Datas = datas,
             QueryDatabase = queryDatabase
         });
     }
     return(GetCacheDatasByCondition <T>(query, size));
 }
        /// <summary>
        /// Called before a query command execute
        /// </summary>
        /// <typeparam name="T">Data type</typeparam>
        /// <param name="queryDataContext">Query data context</param>
        /// <returns>Return query result</returns>
        public virtual QueryDataResult <T> OnQueryStarting <T>(QueryDataContext <T> queryDataContext) where T : BaseEntity <T>, new()
        {
            if (queryDataContext == null)
            {
                return(QueryDataResult <T> .Default($"Parameter:{nameof(queryDataContext)} is null"));
            }
            IQuery query = queryDataContext.Query;
            int    size  = query?.QuerySize ?? 0;

            try
            {
                return(GetCacheDatas <T>(query, size));
            }
            catch (Exception ex)
            {
                return(DataCacheBehavior.GetQueryResult <T>(ex));
            }
        }
        /// <summary>
        /// Get cache datas by condition
        /// </summary>
        /// <typeparam name="T">Data type</typeparam>
        /// <param name="query">Query condition</param>
        /// <param name="size">Return data size</param>
        /// <returns></returns>
        protected virtual QueryDataResult <T> GetCacheDatasByCondition <T>(IQuery query, int size)
        {
            Type entityType          = typeof(T);
            var  entityConfiguration = EntityManager.GetEntityConfiguration(entityType);

            if (entityConfiguration == null)
            {
                LogManager.LogError <DefaultDataCachePolicy>($"Entity :{entityType.FullName} configuration is null");
                return(QueryDataResult <T> .Default());
            }
            var primaryKeys = entityConfiguration.PrimaryKeys;

            if (primaryKeys.IsNullOrEmpty())
            {
                LogManager.LogError <T>($"Type:{entityType.FullName} no primary key is set,unable to get cache data");
                return(new QueryDataResult <T>()
                {
                    Datas = new List <T>(0),
                    QueryDatabase = true
                });
            }
            var otherKeys   = query.AllConditionFieldNames;
            var cacheObject = new CacheObject()
            {
                ObjectName = entityType.Name
            };

            #region cache prefix keys

            List <CacheKey> prefixDataKeys  = new List <CacheKey>();
            var             cachePrefixKeys = entityConfiguration.CachePrefixKeys ?? new List <string>(0);
            var             prefixKeyValues = query.GetKeysEqualValue(cachePrefixKeys);
            if (prefixKeyValues.Count != cachePrefixKeys.Count)
            {
                LogManager.LogError <T>($"Type:{entityType.FullName} miss cache prefix key values in IQuery");
                return(new QueryDataResult <T>()
                {
                    Datas = new List <T>(0),
                    QueryDatabase = true
                });
            }
            int preIndex = 0;
            foreach (var valItem in prefixKeyValues)
            {
                foreach (var prefixVal in valItem.Value)
                {
                    if (preIndex == 0)
                    {
                        var prefixDataKey = new CacheKey();
                        prefixDataKey.AddName(valItem.Key, prefixVal?.ToString() ?? string.Empty);
                        prefixDataKeys.Add(prefixDataKey);
                    }
                    else
                    {
                        foreach (var pdk in prefixDataKeys)
                        {
                            pdk.AddName(valItem.Key, prefixVal?.ToString() ?? string.Empty);
                        }
                    }
                }
                preIndex++;
            }
            otherKeys = otherKeys.Except(cachePrefixKeys);

            #endregion

            List <CacheKey> dataCacheKeys = new List <CacheKey>(otherKeys.Count());

            #region cache ignore keys

            var cacheIgnoreKeys = entityConfiguration.CacheIgnoreKeys ?? new List <string>(0);
            otherKeys = otherKeys.Except(cacheIgnoreKeys).ToList();

            #endregion

            #region primary keys

            var  primaryKeyValues = query.GetKeysEqualValue(primaryKeys);
            bool fullPrimaryKey   = primaryKeyValues.Count == primaryKeys.Count;
            if (fullPrimaryKey)
            {
                otherKeys = otherKeys.Except(primaryKeys).ToList();
                List <CacheKey> primaryCacheKeys = new List <CacheKey>();
                int             pindex           = 0;
                foreach (var valueItem in primaryKeyValues)
                {
                    if (valueItem.Value.IsNullOrEmpty())
                    {
                        continue;
                    }
                    foreach (var primaryVal in valueItem.Value)
                    {
                        if (pindex == 0)
                        {
                            var primaryCacheKey = new CacheKey(cacheObject, prefixDataKeys);
                            primaryCacheKey.AddName(valueItem.Key, primaryVal?.ToString() ?? string.Empty);
                            primaryCacheKeys.Add(primaryCacheKey);
                        }
                        else
                        {
                            foreach (var cacheKey in primaryCacheKeys)
                            {
                                cacheKey.AddName(valueItem.Key, primaryVal?.ToString() ?? string.Empty);
                            }
                        }
                    }
                    pindex++;
                }
                dataCacheKeys.AddRange(primaryCacheKeys);
            }

            #endregion

            #region cache fields

            var             cacheFields         = entityConfiguration.CacheKeys ?? new List <string>(0);
            List <CacheKey> cacheFieldCacheKeys = null;
            if (!cacheFields.IsNullOrEmpty())
            {
                var dataCacheFieldValues = query.GetKeysEqualValue(cacheFields);
                if (!dataCacheFieldValues.IsNullOrEmpty())
                {
                    otherKeys           = otherKeys.Except(dataCacheFieldValues.Keys).ToList();
                    cacheFieldCacheKeys = new List <CacheKey>();
                    foreach (var valueItem in dataCacheFieldValues)
                    {
                        if (valueItem.Value.IsNullOrEmpty())
                        {
                            continue;
                        }
                        foreach (var val in valueItem.Value)
                        {
                            var cacheKey = new CacheKey(cacheObject, prefixDataKeys);
                            cacheKey.AddName(valueItem.Key, val?.ToString() ?? string.Empty);
                            cacheFieldCacheKeys.Add(cacheKey);
                        }
                    }
                    dataCacheKeys.AddRange(CacheManager.String.Get(cacheFieldCacheKeys, cacheObject).Select(c => ConstantCacheKey.Create(c)));
                }
            }

            #endregion

            bool needSort = query != null && !query.Orders.IsNullOrEmpty();
            if (dataCacheKeys.IsNullOrEmpty() || (!otherKeys.IsNullOrEmpty() && needSort))
            {
                LogManager.LogInformation <DefaultDataCachePolicy>($"Type:{entityType.FullName},IQuery has any other criterias without cache keys and need sort");
                return(new QueryDataResult <T>()
                {
                    QueryDatabase = true,
                    Datas = new List <T>(0)
                });
            }
            dataCacheKeys = dataCacheKeys.Distinct().ToList();
            int      removeCount        = 0;
            var      queryConditionFunc = query.GetQueryExpression <T>(); //query condition
            List <T> dataList           = new List <T>();
            size = size < 0 ? 0 : size;                                   //return value count
            bool notQueryDb = false;
            CacheManager.GetDataList <T>(dataCacheKeys, cacheObject).ForEach(data =>
            {
                if (data != null && (queryConditionFunc?.Invoke(data) ?? true))
                {
                    dataList.Add(data);
                }
                else
                {
                    removeCount += data != null || DataCacheManager.Configuration.EnableCacheNullData ? 1 : 0;
                }
            });
            if (otherKeys.IsNullOrEmpty())
            {
                size       = size <= 0 ? dataCacheKeys.Count : (size > dataCacheKeys.Count ? dataCacheKeys.Count : size);
                notQueryDb = dataList.Count >= size - removeCount;
                if (notQueryDb && needSort)
                {
                    dataList = query.Sort(dataList).ToList();
                }
            }
            else
            {
                notQueryDb = size > 0 && dataList.Count >= size;
            }
            return(new QueryDataResult <T>()
            {
                QueryDatabase = !notQueryDb,
                QuriedCache = true,
                PrimaryCacheKeys = dataCacheKeys,
                OtherCacheKeys = cacheFieldCacheKeys,
                Datas = dataList
            });
        }