public static object GetPropertyValue(this PropertyInfo prop, object obj, TableOptionsAttribute attr)
        {
            if (obj == null)
            {
                return(null);
            }

            var value = prop.GetValue(obj, null);

            if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
            {
                return(prop.PropertyType.GetProperty("Item")?.GetValue(value, new object[] { attr.Key }));
            }

            return(value);
        }
        public static List <Tuple <TableOptionsAttribute, Type, PropertyInfo> > GetPropertiesTableData <T>(IEnumerable <T> list, string firstLevelName = null, string secondLevelName = null)
            where T : class
        {
            var type = typeof(T);

            if (TableDataCache.ContainsKey(type))
            {
                return(TableDataCache[type]);
            }

            var hasDynamicColumns = false;
            var result            = new List <Tuple <TableOptionsAttribute, Type, PropertyInfo> >();

            foreach (var propertyInfo in type.GetProperties())
            {
                if (propertyInfo.Name.IsNullOrEmpty())
                {
                    continue;
                }

                TableOptionsAttribute tableOptionsAttribute = null;
                var tableAttribute = propertyInfo.GetCustomAttributes(typeof(TableOptionsAttribute), false);
                if (tableAttribute.Length > 0)
                {
                    tableOptionsAttribute = tableAttribute[0] as TableOptionsAttribute;
                }

                var displayNameAttributes = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false);
                if (displayNameAttributes.Length > 0)
                {
                    tableOptionsAttribute       = tableOptionsAttribute ?? new TableOptionsAttribute();
                    tableOptionsAttribute.Title = ((DisplayNameAttribute)displayNameAttributes[0]).DisplayName;
                }

                var displayAttributes = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), false);
                if (displayAttributes.Length > 0)
                {
                    tableOptionsAttribute = tableOptionsAttribute ?? new TableOptionsAttribute();

                    // Get display attribute and check if it equals to "FirstLevel" or "Second" level. If so - replace title with its value
                    var displayAttribute          = (DisplayAttribute)displayAttributes[0];
                    var displayAttributeName      = displayAttribute.Name;
                    var displayAttributeNameValue = displayAttribute.GetName();

                    if (displayAttributeName == "FirstLevel" && firstLevelName.IsNotNullOrEmpty())
                    {
                        tableOptionsAttribute.Title = firstLevelName;
                    }
                    else if (displayAttributeName == "SecondLevel" && secondLevelName.IsNotNullOrEmpty())
                    {
                        tableOptionsAttribute.Title = secondLevelName;
                    }
                    else
                    {
                        tableOptionsAttribute.Title = displayAttributeNameValue;
                    }
                }

                if (tableOptionsAttribute != null)
                {
                    if (tableOptionsAttribute.IsOnlyForSignedUsers && HttpContext.Current.User == null)
                    {
                        continue;
                    }

                    if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
                    {
                        hasDynamicColumns = true;
                        var data       = list?.FirstOrDefault();
                        var dictionary = data != null
                            ? propertyInfo.GetValue(data) as IDictionary
                            : null;

                        if (dictionary != null)
                        {
                            var dictionaryType = dictionary.GetType();
                            var keyType        = dictionaryType.GetGenericArguments()[0];
                            var valueType      = dictionaryType.GetGenericArguments()[1];
                            foreach (DictionaryEntry entry in dictionary)
                            {
                                tableOptionsAttribute = new TableOptionsAttribute
                                {
                                    Key  = entry.Key.ToString(),
                                    Name = keyType == typeof(string)
                                        ? $"{propertyInfo.Name}['{entry.Key}']"
                                        : $"{propertyInfo.Name}[{entry.Key}]",
                                    Title    = entry.Key.ToString(),
                                    Format   = tableOptionsAttribute?.Format,
                                    CssClass = tableOptionsAttribute?.CssClass
                                };

                                result.Add(new Tuple <TableOptionsAttribute, Type, PropertyInfo>(tableOptionsAttribute, valueType, propertyInfo));
                            }
                        }
                    }
                    else
                    {
                        tableOptionsAttribute.Name = propertyInfo.Name;
                        result.Add(new Tuple <TableOptionsAttribute, Type, PropertyInfo>(tableOptionsAttribute, propertyInfo.PropertyType, propertyInfo));
                    }
                }
            }

            if (result.IsNotNullOrEmpty())
            {
                result = result.OrderBy(item => item.Item1.Order).ToList();
            }

            if (!hasDynamicColumns)
            {
                TableDataCache[type] = result;
            }

            return(result);
        }