public EntityMetaDetailsResponse GetEntityMetaDetails(ServiceStack.ServiceInterface.Service service)
        {
            // The entity meta details don't change per entity type, so cache these for performance
            var cacheKey    = string.Format("{0}-meta-details", EntityType.ToString().ToLower());
            var metaDetails = CacheClient.Get <EntityMetaDetailsResponse>(cacheKey);

            if (metaDetails != null)
            {
                return(metaDetails);
            }

            var request        = service.RequestContext.Get <IHttpRequest>();
            var appUri         = request.GetApplicationUrl().TrimEnd('/');
            var baseServiceUri = appUri + request.PathInfo.Replace("/meta", "");
            var queryString    = request.QueryString["format"] != null ? "&format=" + request.QueryString["format"] : "";
            var pkCount        = FieldMap.Count(pk => pk.Value.IsPrimaryKey);
            var fields         = new List <Link>();

            foreach (var f in FieldMap)
            {
                var isUnique = false;
                var ucs      = UniqueConstraintMap.Where(uc => uc.Value.Any(x => x.Name.Equals(f.Key, StringComparison.InvariantCultureIgnoreCase))).ToArray();
                var link     = Link.Create(
                    f.Key.ToCamelCase(), f.Value.DataType.Name, "field",
                    string.Format("{0}?select={1}{2}", baseServiceUri, f.Key.ToLowerInvariant(), queryString),
                    string.Format("The {0} field for the {1} resource.", f.Value.Name, typeof(TDto).Name),
                    new Dictionary <string, string>()
                    );
                var props = new SortedDictionary <string, string>();
                props.Add("index", f.Value.FieldIndex.ToString(CultureInfo.InvariantCulture));
                if (f.Value.IsPrimaryKey)
                {
                    props.Add("isPrimaryKey", f.Value.IsPrimaryKey.ToString().ToLower());
                    if (pkCount == 1)
                    {
                        isUnique = true;
                    }
                }
                if (f.Value.IsForeignKey)
                {
                    props.Add("isForeignKey", "true");
                }

                var ucNames = new List <string>();
                foreach (var uc in ucs)
                {
                    if (uc.Value.Count() == 1)
                    {
                        isUnique = true;
                    }
                    ucNames.Add(uc.Key.ToLower());
                }
                if (ucNames.Any())
                {
                    props.Add("partOfUniqueConstraints", string.Join(",", ucNames.ToArray()));
                }
                if (isUnique)
                {
                    props.Add("isUnique", "true");
                }
                if (f.Value.IsOfEnumDataType)
                {
                    props.Add("isOfEnumDataType", "true");
                }
                if (f.Value.IsReadOnly)
                {
                    props.Add("isReadOnly", "true");
                }
                if (f.Value.IsNullable)
                {
                    props.Add("isNullable", "true");
                }
                if (f.Value.IsOfEnumDataType)
                {
                    props.Add("isEnumType", "true");
                }
                if (f.Value.MaxLength > 0)
                {
                    props.Add("maxLength", f.Value.MaxLength.ToString(CultureInfo.InvariantCulture));
                }
                if (f.Value.Precision > 0)
                {
                    props.Add("precision", f.Value.Precision.ToString(CultureInfo.InvariantCulture));
                }
                if (f.Value.Scale > 0)
                {
                    props.Add("scale", f.Value.Scale.ToString(CultureInfo.InvariantCulture));
                }
                link.Properties = new Dictionary <string, string>(props);
                fields.Add(link);
            }

            var includes = new List <Link>();

            foreach (var f in IncludeMap)
            {
                var relationType = "";
                switch (f.Value.TypeOfRelation)
                {
                case RelationType.ManyToMany:
                    relationType = "n:n";
                    break;

                case RelationType.ManyToOne:
                    relationType = "n:1";
                    break;

                case RelationType.OneToMany:
                    relationType = "1:n";
                    break;

                case RelationType.OneToOne:
                    relationType = "1:1";
                    break;
                }
                var relatedDtoContainerName =
                    (Enum.GetName(typeof(EntityType), f.Value.ToFetchEntityType) ?? "").Replace("Entity", "");
                var link = Link.Create(
                    f.Key.ToCamelCase(),
                    (relationType.EndsWith("n") ? relatedDtoContainerName + "Collection" : relatedDtoContainerName),
                    "include",
                    string.Format("{0}?include={1}{2}", baseServiceUri, f.Key.ToLowerInvariant(), queryString),
                    string.Format(
                        "The {0} field for the {1} resource to include in the results returned by a query.",
                        f.Value.PropertyName,
                        typeof(TDto).Name),
                    new Dictionary <string, string>
                {
                    { "field", f.Value.PropertyName.ToCamelCase() },
                    {
                        "relatedType",
                        (Enum.GetName(typeof(EntityType), f.Value.ToFetchEntityType) ?? "").Replace("Entity", "")
                    },
                    { "relationType", relationType }
                });
                includes.Add(link);
            }

            var relations = new List <Link>();

            foreach (var f in RelationMap)
            {
                var isPkSide    = f.Value.StartEntityIsPkSide;
                var isFkSide    = !f.Value.StartEntityIsPkSide;
                var pkFieldCore = f.Value.GetAllPKEntityFieldCoreObjects().FirstOrDefault();
                var fkFieldCore = f.Value.GetAllFKEntityFieldCoreObjects().FirstOrDefault();
                var thisField   = isPkSide
                                    ? (pkFieldCore == null ? "" : pkFieldCore.Name)
                                    : (fkFieldCore == null ? "" : fkFieldCore.Name);
                var relatedField = isFkSide
                                    ? (pkFieldCore == null ? "" : pkFieldCore.Name)
                                    : (fkFieldCore == null ? "" : fkFieldCore.Name);
                var thisEntityAlias = isPkSide
                                    ? (pkFieldCore == null ? "": pkFieldCore.ActualContainingObjectName.Replace("Entity", ""))
                                    : (fkFieldCore == null ? "": fkFieldCore.ActualContainingObjectName.Replace("Entity", ""));
                var relatedEntityAlias = isFkSide
                                    ? (pkFieldCore == null ? "": pkFieldCore.ActualContainingObjectName.Replace("Entity", ""))
                                    : (fkFieldCore == null ? "": fkFieldCore.ActualContainingObjectName.Replace("Entity", ""));
                var relationType = "";
                switch (f.Value.TypeOfRelation)
                {
                case RelationType.ManyToMany:
                    relationType = "n:n";
                    break;

                case RelationType.ManyToOne:
                    relationType = "n:1";
                    break;

                case RelationType.OneToMany:
                    relationType = "1:n";
                    break;

                case RelationType.OneToOne:
                    relationType = "1:1";
                    break;
                }
                var link = Link.Create(
                    f.Key.ToCamelCase(),
                    relationType.EndsWith("n") ? relatedEntityAlias + "Collection" : relatedEntityAlias, "relation",
                    string.Format("{0}?relations={1}{2}", baseServiceUri, f.Key.ToLowerInvariant(), queryString),
                    string.Format(
                        "The relation '{0}' for the {1} resource between a {2} (PK) and a {3} (FK) resource.",
                        f.Value.MappedFieldName,
                        typeof(TDto).Name, f.Value.AliasStartEntity.Replace("Entity", ""),
                        f.Value.AliasEndEntity.Replace("Entity", "")),
                    new Dictionary <string, string>
                {
                    { "field", f.Value.MappedFieldName.ToCamelCase() },
                    { "joinHint", f.Value.JoinType.ToString().ToLower() },
                    { "relationType", relationType },
                    { "isPkSide", isPkSide.ToString().ToLower() },
                    { "isFkSide", isFkSide.ToString().ToLower() },
                    { "isWeakRelation", f.Value.IsWeak.ToString().ToLower() },
                    { "pkTypeName", isPkSide ? thisEntityAlias : relatedEntityAlias },
                    {
                        "pkTypeField",
                        isPkSide ? thisField.ToCamelCase() : relatedField.ToCamelCase()
                    },
                    { "fkTypeName", isFkSide ? thisEntityAlias : relatedEntityAlias },
                    {
                        "fkTypeField",
                        isFkSide ? thisField.ToCamelCase() : relatedField.ToCamelCase()
                    },
                });
                relations.Add(link);
                // add relation to fields list as well
                fields.Add(Link.Create(
                               f.Value.MappedFieldName.ToCamelCase(),
                               relationType.EndsWith("n") ? relatedEntityAlias + "Collection": relatedEntityAlias,
                               "field", null,
                               string.Format("The {0} field for the {1} resource.", f.Value.MappedFieldName,
                                             typeof(TDto).Name), new Dictionary <string, string>
                {
                    { "relation", f.Value.MappedFieldName.ToCamelCase() },
                    { "relationType", relationType },
                    { "isPkSide", isPkSide.ToString().ToLower() },
                    { "isFkSide", isFkSide.ToString().ToLower() },
                }));
            }

            metaDetails = new EntityMetaDetailsResponse()
            {
                Fields    = fields.ToArray(),
                Includes  = includes.ToArray(),
                Relations = relations.ToArray()
            };
            CacheClient.Set(cacheKey, metaDetails);
            return(metaDetails);
        }