/// <summary>
        /// Builds a strongly typed Entity Model based on a given DXA R2 Data Model.
        /// </summary>
        /// <param name="entityModel">The strongly typed Entity Model to build. Is <c>null</c> for the first Entity Model Builder in the pipeline.</param>
        /// <param name="entityModelData">The DXA R2 Data Model.</param>
        /// <param name="baseModelType">The base type for the Entity Model to build.</param>
        /// <param name="localization">The context <see cref="ILocalization"/>.</param>
        public virtual void BuildEntityModel(ref EntityModel entityModel, EntityModelData entityModelData, Type baseModelType, ILocalization localization)
        {
            using (new Tracer(entityModel, entityModelData, baseModelType, localization))
            {
                MvcData        mvcData        = CreateMvcData(entityModelData.MvcData, "Entity");
                SemanticSchema semanticSchema = SemanticMapping.GetSchema(entityModelData.SchemaId, localization);

                Type modelType = (baseModelType == null) ?
                                 ModelTypeRegistry.GetViewModelType(mvcData) :
                                 semanticSchema.GetModelTypeFromSemanticMapping(baseModelType);

                MappingData mappingData = new MappingData
                {
                    SourceViewModel    = entityModelData,
                    ModelType          = modelType,
                    PropertyValidation = new Validation
                    {
                        MainSchema       = semanticSchema,
                        InheritedSchemas = GetInheritedSemanticSchemas(entityModelData, localization)
                    },
                    Fields         = entityModelData.Content,
                    MetadataFields = entityModelData.Metadata,
                    Localization   = localization
                };

                entityModel = (EntityModel)CreateViewModel(mappingData);

                entityModel.Id      = entityModelData.Id;
                entityModel.MvcData = mvcData ?? entityModel.GetDefaultView(localization);
            }
        }
        protected virtual object MapKeyword(KeywordModelData keywordModelData, Type targetType, ILocalization localization)
        {
            if (typeof(KeywordModel).IsAssignableFrom(targetType))
            {
                KeywordModel result;
                if (keywordModelData.SchemaId == null)
                {
                    result = new KeywordModel
                    {
                        ExtensionData = keywordModelData.ExtensionData
                    };
                }
                else
                {
                    MappingData keywordMappingData = new MappingData
                    {
                        SourceViewModel    = keywordModelData,
                        ModelType          = targetType,
                        PropertyValidation = new Validation
                        {
                            MainSchema       = SemanticMapping.GetSchema(keywordModelData.SchemaId, localization),
                            InheritedSchemas = GetInheritedSemanticSchemas(keywordModelData as ViewModelData, localization)
                        },
                        MetadataFields = keywordModelData.Metadata,
                        Localization   = localization
                    };

                    result = (KeywordModel)CreateViewModel(keywordMappingData);
                }

                result.Id          = keywordModelData.Id;
                result.Title       = keywordModelData.Title;
                result.Description = keywordModelData.Description ?? string.Empty;
                result.Key         = keywordModelData.Key ?? string.Empty;
                result.TaxonomyId  = keywordModelData.TaxonomyId;

                return(result);
            }

            if (targetType == typeof(Tag))
            {
                return(new Tag
                {
                    DisplayText = GetKeywordDisplayText(keywordModelData),
                    Key = keywordModelData.Key,
                    TagCategory = localization.GetCmUri(keywordModelData.TaxonomyId, (int)ItemType.Category)
                });
            }

            if (targetType == typeof(bool))
            {
                string key = string.IsNullOrEmpty(keywordModelData.Key) ? keywordModelData.Title : keywordModelData.Key;
                return(Convert.ToBoolean(key));
            }

            return(GetKeywordDisplayText(keywordModelData));
        }
        /// <summary>
        /// Builds a strongly typed Page Model from a given DXA R2 Data Model.
        /// </summary>
        /// <param name="pageModel">The strongly typed Page Model to build. Is <c>null</c> for the first Page Model Builder in the pipeline.</param>
        /// <param name="pageModelData">The DXA R2 Data Model.</param>
        /// <param name="includePageRegions">Indicates whether Include Page Regions should be included.</param>
        /// <param name="localization">The context <see cref="ILocalization"/>.</param>
        public virtual void BuildPageModel(ref PageModel pageModel, PageModelData pageModelData, bool includePageRegions, ILocalization localization)
        {
            using (new Tracer(pageModel, pageModelData, includePageRegions, localization))
            {
                MvcData mvcData   = CreateMvcData(pageModelData.MvcData, "Page");
                Type    modelType = ModelTypeRegistry.GetViewModelType(mvcData);

                if (modelType == typeof(PageModel))
                {
                    // Standard Page Model.
                    pageModel = new PageModel(pageModelData.Id)
                    {
                        ExtensionData = pageModelData.ExtensionData,
                        HtmlClasses   = pageModelData.HtmlClasses,
                        XpmMetadata   = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null,
                    };
                }
                else if (pageModelData.SchemaId == null)
                {
                    // Custom Page Model, but no custom metadata.
                    pageModel = (PageModel)modelType.CreateInstance(pageModelData.Id);
                    pageModel.ExtensionData = pageModelData.ExtensionData;
                    pageModel.HtmlClasses   = pageModelData.HtmlClasses;
                    pageModel.XpmMetadata   = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null;
                }
                else
                {
                    // Custom Page Model with custom metadata; do full-blown model mapping.
                    MappingData mappingData = new MappingData
                    {
                        SourceViewModel    = pageModelData,
                        ModelId            = pageModelData.Id,
                        ModelType          = modelType,
                        PropertyValidation = new Validation
                        {
                            MainSchema       = SemanticMapping.GetSchema(pageModelData.SchemaId, localization),
                            InheritedSchemas = GetInheritedSemanticSchemas(pageModelData, localization)
                        },
                        MetadataFields = pageModelData.Metadata,
                        Localization   = localization
                    };
                    pageModel = (PageModel)CreateViewModel(mappingData);
                }

                pageModel.MvcData = mvcData;
                pageModel.Meta    = pageModelData.Meta ?? new Dictionary <string, string>();
                pageModel.Title   = PostProcessPageTitle(pageModelData, localization); // TODO TSI-2210: This should eventually be done in Model Service.
                pageModel.Url     = pageModelData.UrlPath;
                if (pageModelData.Regions != null)
                {
                    IEnumerable <RegionModelData> regions = includePageRegions ? pageModelData.Regions : pageModelData.Regions.Where(r => r.IncludePageId == null);
                    pageModel.Regions.UnionWith(regions.Select(data => CreateRegionModel(data, localization)));
                    pageModel.IsVolatile |= pageModel.Regions.Any(region => region.IsVolatile);
                }
            }
        }
        protected virtual List <SemanticSchema> GetInheritedSemanticSchemas(ViewModelData pageModelData, ILocalization localization)
        {
            List <SemanticSchema> schemas = new List <SemanticSchema>();

            if (pageModelData.ExtensionData != null && pageModelData.ExtensionData.ContainsKey("Schemas"))
            {
                string[] ids = pageModelData.ExtensionData["Schemas"] as string[];
                if (ids != null && ids.Length > 0)
                {
                    foreach (string inheritedSchemaId in ids)
                    {
                        SemanticSchema schema = SemanticMapping.GetSchema(inheritedSchemaId, localization);
                        if (schema == null)
                        {
                            continue;
                        }
                        schemas.Add(schema);
                    }
                }
            }

            return(schemas);
        }
Exemplo n.º 5
0
        private static SemanticInfo ExtractSemanticInfo(Type modelType)
        {
            SemanticInfo semanticInfo = new SemanticInfo();

            // Built-in semantic type mapping
            string bareTypeName = modelType.Name.Split('`')[0]; // Type name without generic type parameters (if any)

            semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(bareTypeName));

            // Extract semantic info from SemanticEntity attributes on the Model Type.
            foreach (SemanticEntityAttribute attribute in modelType.GetCustomAttributes(true).Where(a => a is SemanticEntityAttribute))
            {
                semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(attribute.EntityName, attribute.Vocab));

                if (!attribute.Public || string.IsNullOrEmpty(attribute.Prefix))
                {
                    continue;
                }

                string prefix = attribute.Prefix;
                string registeredVocab;
                if (semanticInfo.PrefixMappings.TryGetValue(prefix, out registeredVocab))
                {
                    // Prefix mapping already exists; must match.
                    if (attribute.Vocab != registeredVocab)
                    {
                        throw new DxaException(
                                  string.Format("Attempt to use semantic prefix '{0}' for vocabulary '{1}', but is is already used for vocabulary '{2}",
                                                prefix, attribute.Vocab, registeredVocab)
                                  );
                    }
                }
                else
                {
                    semanticInfo.PrefixMappings.Add(prefix, attribute.Vocab);
                }

                semanticInfo.PublicSemanticTypes.Add(String.Format("{0}:{1}", prefix, attribute.EntityName));
            }

            // Extract semantic info from SemanticEntity attributes on the Model Type's properties
            foreach (MemberInfo memberInfo in modelType.GetMembers(BindingFlags.Public | BindingFlags.Instance))
            {
                foreach (SemanticPropertyAttribute attribute in memberInfo.GetCustomAttributes(true).Where(a => a is SemanticPropertyAttribute))
                {
                    if (string.IsNullOrEmpty(attribute.PropertyName))
                    {
                        // Skip properties without name.
                        continue;
                    }
                    string[] semanticPropertyNameParts = attribute.PropertyName.Split(':');
                    if (semanticPropertyNameParts.Length < 2)
                    {
                        // Skip property names without prefix.
                        continue;
                    }
                    string prefix = semanticPropertyNameParts[0];
                    if (!semanticInfo.PrefixMappings.ContainsKey(prefix))
                    {
                        // Skip property names with prefix which is not declared as public prefix on the type.
                        continue;
                    }

                    IList <string> semanticPropertyNames;
                    if (!semanticInfo.SemanticProperties.TryGetValue(memberInfo.Name, out semanticPropertyNames))
                    {
                        semanticPropertyNames = new List <string>();
                        semanticInfo.SemanticProperties.Add(memberInfo.Name, semanticPropertyNames);
                    }
                    semanticPropertyNames.Add(attribute.PropertyName);
                }
            }

            return(semanticInfo);
        }
Exemplo n.º 6
0
        private static SemanticInfo ExtractSemanticInfo(Type modelType)
        {
            SemanticInfo semanticInfo = new SemanticInfo();

            // Get model type name
            string modelTypeName = modelType.BareTypeName();

            // Built-in semantic type mapping
            semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(modelTypeName));

            // Extract semantic info from SemanticEntity attributes on the Model Type.
            foreach (SemanticEntityAttribute attribute in modelType.GetCustomAttributes <SemanticEntityAttribute>(inherit: true))
            {
                semanticInfo.MappedSemanticTypes.Add(SemanticMapping.GetQualifiedTypeName(attribute.EntityName, attribute.Vocab));

                string prefix = attribute.Prefix ?? string.Empty;
                if (attribute.Public && !String.IsNullOrEmpty(attribute.Prefix))
                {
                    string registeredVocab;
                    if (semanticInfo.PrefixMappings.TryGetValue(prefix, out registeredVocab))
                    {
                        // Prefix mapping already exists; must match.
                        if (attribute.Vocab != registeredVocab)
                        {
                            throw new DxaException(
                                      String.Format("Attempt to use semantic prefix '{0}' for vocabulary '{1}', but is is already used for vocabulary '{2}",
                                                    prefix, attribute.Vocab, registeredVocab)
                                      );
                        }
                    }
                    else
                    {
                        semanticInfo.PrefixMappings.Add(prefix, attribute.Vocab);
                    }
                    semanticInfo.PublicSemanticTypes.Add(String.Format("{0}:{1}", prefix, attribute.EntityName));
                }

                // There may be multiple Semantic Entity attributes for the same prefix. The first one will be used.
                if (semanticInfo.PrefixToSemanticTypeMap.ContainsKey(prefix))
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.Debug($"Type '{modelType.FullName}' has multiple SemanticEntity attributes for prefix '{prefix}'. Ignoring '{attribute.EntityName}'.");
                    }
                }
                else
                {
                    semanticInfo.PrefixToSemanticTypeMap.Add(prefix, new SemanticType(attribute.EntityName, attribute.Vocab));
                }
            }

            if (!semanticInfo.PrefixToSemanticTypeMap.ContainsKey(string.Empty))
            {
                // If there is no SemanticEntity attribute without prefix, we add an implicit one:
                semanticInfo.PrefixToSemanticTypeMap.Add(string.Empty, new SemanticType(modelTypeName, SemanticMapping.DefaultVocabulary));
            }

            string defaultPrefix;
            bool   mapAllProperties;
            SemanticDefaultsAttribute semanticDefaultsAttr = modelType.GetCustomAttribute <SemanticDefaultsAttribute>();

            if (semanticDefaultsAttr == null)
            {
                defaultPrefix    = string.Empty;
                mapAllProperties = true;
            }
            else
            {
                defaultPrefix    = semanticDefaultsAttr.Prefix;
                mapAllProperties = semanticDefaultsAttr.MapAllProperties;
            }

            foreach (PropertyInfo propertyInfo in modelType.GetProperties())
            {
                // Extract semantic info from SemanticEntity attributes on the Model Type's properties
                bool useImplicitMapping = mapAllProperties;

                List <SemanticPropertyAttribute> attributes = propertyInfo.GetCustomAttributes <SemanticPropertyAttribute>(inherit: true).ToList();

                // check if we should be ignoring this mapping completely
                if (attributes.Where(x => x.IgnoreMapping).Select(x => x).FirstOrDefault() != null)
                {
                    continue;
                }

                List <SemanticProperty> semanticProperties = new List <SemanticProperty>();
                foreach (SemanticPropertyAttribute attribute in attributes)
                {
                    if (string.IsNullOrEmpty(attribute.PropertyName))
                    {
                        continue;
                    }

                    // check for known property names
                    switch (attribute.PropertyName)
                    {
                    // To do : we need to make this more generic to collect all fields of a given type (e.g. [SemtanticProperty("_all", typeof(Keyword)])
                    case SemanticProperty.AllFields:
                        if (!typeof(IDictionary <string, string>).IsAssignableFrom(propertyInfo.PropertyType) &&
                            !typeof(IDictionary <string, KeywordModel>).IsAssignableFrom(propertyInfo.PropertyType))
                        {
                            throw new DxaException(
                                      $"Invalid semantics for property {modelType.Name}.{propertyInfo.Name}. Properties with [SemanticProperty(\"_all\")] annotation must be of type Dictionary<string, string> or Dictionary<string, KeywordModel>."
                                      );
                        }
                        break;

                    case SemanticProperty.Self:
                        Type elementType = GetElementType(propertyInfo.PropertyType);
                        if (!typeof(MediaItem).IsAssignableFrom(elementType) && !typeof(Link).IsAssignableFrom(elementType) && (elementType != typeof(string)) && (elementType != typeof(RichText)))
                        {
                            throw new DxaException(
                                      $"Invalid semantics for property {modelType.Name}.{propertyInfo.Name}. Properties with [SemanticProperty(\"_self\")] annotation must be of type MediaItem, Link, String or RichText.");
                        }
                        break;
                    }

                    string prefix = attribute.Prefix;
                    string name   = attribute.PropertyName;
                    if (prefix != null)
                    {
                        if (semanticInfo.PrefixMappings.ContainsKey(prefix))
                        {
                            IList <string> semanticPropertyNames;
                            if (!semanticInfo.SemanticProperties.TryGetValue(propertyInfo.Name, out semanticPropertyNames))
                            {
                                semanticPropertyNames = new List <string>();
                                semanticInfo.SemanticProperties.Add(propertyInfo.Name, semanticPropertyNames);
                            }
                            semanticPropertyNames.Add(attribute.PropertyName);
                        }
                    }
                    else
                    {
                        // Skip property names without prefix.
                        prefix             = defaultPrefix;
                        useImplicitMapping = false;
                    }

                    SemanticType semanticType;
                    if (!semanticInfo.PrefixToSemanticTypeMap.TryGetValue(prefix, out semanticType))
                    {
                        throw new DxaException($"Use of undeclared prefix '{prefix}' in property '{propertyInfo.Name}' in type '{modelType.FullName}'.");
                    }
                    semanticProperties.Add(new SemanticProperty(prefix, name, semanticType));
                }

                if (useImplicitMapping)
                {
                    SemanticType semanticType;
                    if (!semanticInfo.PrefixToSemanticTypeMap.TryGetValue(defaultPrefix, out semanticType))
                    {
                        throw new DxaException($"Use of undeclared prefix '{defaultPrefix}' in property '{propertyInfo.Name}' in type '{modelType.FullName}'.");
                    }
                    SemanticProperty implicitSemanticProperty = new SemanticProperty(
                        String.Empty, GetDefaultSemanticPropertyName(propertyInfo),
                        semanticType
                        );

                    semanticProperties.Add(implicitSemanticProperty);
                }

                if (semanticProperties.Count > 0)
                {
                    if (semanticInfo.PropertySemantics.ContainsKey(propertyInfo.Name))
                    {
                        // Properties with same name can exist is a property is reintroduced with a different signature in a subclass.
                        if (Log.IsDebugEnabled)
                        {
                            Log.Debug("Property with name '{0}' is declared multiple times in type {1}.", propertyInfo.Name, modelType.FullName);
                        }
                    }
                    else
                    {
                        semanticInfo.PropertySemantics.Add(propertyInfo.Name, semanticProperties);
                    }
                }
            }

            return(semanticInfo);
        }
Exemplo n.º 7
0
        private RichText ResolveRichText(XmlDocument doc, Localization localization)
        {
            const string xlinkNamespaceUri = "http://www.w3.org/1999/xlink";

            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);

            nsmgr.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
            nsmgr.AddNamespace("xlink", xlinkNamespaceUri);

            // Process/resolve hyperlinks with XLink attributes
            ILinkResolver linkResolver = SiteConfiguration.LinkResolver;

            foreach (XmlElement linkElement in doc.SelectNodes("//a[@xlink:href]", nsmgr))
            {
                // DD4T BinaryPublisher may have resolved a href attribute already (for links to MM Components)
                string linkUrl = linkElement.GetAttribute("href");
                if (string.IsNullOrEmpty(linkUrl))
                {
                    // No href attribute found. Apparently the XLink refers to a regular Component; we resolve it to a URL here.
                    string tcmUri = linkElement.GetAttribute("href", xlinkNamespaceUri);
                    if (!string.IsNullOrEmpty(tcmUri))
                    {
                        // Try to resolve directly to Binary content of MM Component.
                        linkUrl = linkResolver.ResolveLink(tcmUri, resolveToBinary: true);
                    }
                }
                if (!string.IsNullOrEmpty(linkUrl))
                {
                    // The link was resolved; set HTML href attribute
                    linkElement.SetAttribute("href", linkUrl);
                    ApplyHashIfApplicable(linkElement, localization);

                    // Remove all XLink and data- attributes
                    IEnumerable <XmlAttribute> attributesToRemove = linkElement.Attributes.Cast <XmlAttribute>()
                                                                    .Where(a => a.NamespaceURI == xlinkNamespaceUri || a.LocalName == "xlink" || a.LocalName.StartsWith("data-")).ToArray();
                    foreach (XmlAttribute attr in attributesToRemove)
                    {
                        linkElement.Attributes.Remove(attr);
                    }
                }
                else
                {
                    // The link was not resolved; remove the hyperlink.
                    XmlNode parentNode = linkElement.ParentNode;
                    foreach (XmlNode childNode in linkElement.ChildNodes)
                    {
                        parentNode.InsertBefore(childNode, linkElement);
                    }
                    parentNode.RemoveChild(linkElement);
                }
            }

            // Resolve embedded media items
            List <EntityModel> embeddedEntities = new List <EntityModel>();

            foreach (XmlElement imgElement in doc.SelectNodes("//img[@data-schemaUri]", nsmgr))
            {
                string[]       schemaTcmUriParts = imgElement.GetAttribute("data-schemaUri").Split('-');
                SemanticSchema semanticSchema    = SemanticMapping.GetSchema(schemaTcmUriParts[1], localization);

                // The semantic mapping may resolve to a more specific model type than specified here (e.g. YouTubeVideo instead of just MediaItem)
                Type      modelType = semanticSchema.GetModelTypeFromSemanticMapping(typeof(MediaItem));
                MediaItem mediaItem = (MediaItem)modelType.CreateInstance();
                mediaItem.ReadFromXhtmlElement(imgElement);
                if (mediaItem.MvcData == null)
                {
                    // In DXA 1.1 MediaItem.ReadFromXhtmlElement was expected to set MvcData.
                    // In DXA 1.2 this should be done in a GetDefaultView override (which is also used for other embedded Entities)
                    mediaItem.MvcData = mediaItem.GetDefaultView(localization);
                }
                embeddedEntities.Add(mediaItem);

                // Replace img element with marker XML processing instruction
                imgElement.ParentNode.ReplaceChild(
                    doc.CreateProcessingInstruction(EmbeddedEntityProcessingInstructionName, String.Empty),
                    imgElement
                    );
            }

            // Split the XHTML into fragments based on marker XML processing instructions.
            string xhtml = doc.DocumentElement.InnerXml;
            IList <IRichTextFragment> richTextFragments = new List <IRichTextFragment>();
            int lastFragmentIndex = 0;
            int i = 0;

            foreach (Match embeddedEntityMatch in EmbeddedEntityProcessingInstructionRegex.Matches(xhtml))
            {
                int embeddedEntityIndex = embeddedEntityMatch.Index;
                if (embeddedEntityIndex > lastFragmentIndex)
                {
                    richTextFragments.Add(new RichTextFragment(xhtml.Substring(lastFragmentIndex, embeddedEntityIndex - lastFragmentIndex)));
                }
                richTextFragments.Add(embeddedEntities[i++]);
                lastFragmentIndex = embeddedEntityIndex + embeddedEntityMatch.Length;
            }
            if (lastFragmentIndex < xhtml.Length)
            {
                // Final text fragment
                richTextFragments.Add(new RichTextFragment(xhtml.Substring(lastFragmentIndex)));
            }

            return(new RichText(richTextFragments));
        }
        protected virtual RegionModel CreateRegionModel(RegionModelData regionModelData, ILocalization localization)
        {
            MvcData mvcData         = CreateMvcData(regionModelData.MvcData, "Region");
            Type    regionModelType = ModelTypeRegistry.GetViewModelType(mvcData);

            RegionModel result = (RegionModel)regionModelType.CreateInstance(regionModelData.Name);

            result.ExtensionData = regionModelData.ExtensionData;
            result.HtmlClasses   = regionModelData.HtmlClasses;
            result.MvcData       = mvcData;
            result.XpmMetadata   = localization.IsXpmEnabled ? regionModelData.XpmMetadata : null;
            result.SchemaId      = regionModelData.SchemaId;

            if (!string.IsNullOrEmpty(regionModelData.SchemaId))
            {
                SemanticSchema semanticSchema = SemanticMapping.GetSchema(regionModelData.SchemaId, localization);

                Type modelType = ModelTypeRegistry.GetViewModelType(mvcData);

                MappingData mappingData = new MappingData
                {
                    SourceViewModel    = regionModelData,
                    ModelType          = modelType,
                    PropertyValidation = new Validation
                    {
                        MainSchema       = semanticSchema,
                        InheritedSchemas = GetInheritedSemanticSchemas(regionModelData, localization)
                    },
                    Fields         = null,
                    MetadataFields = regionModelData.Metadata,
                    Localization   = localization
                };
                MapSemanticProperties(result, mappingData);
            }

            if (regionModelData.Regions != null)
            {
                IEnumerable <RegionModel> nestedRegionModels = regionModelData.Regions.Select(data => CreateRegionModel(data, localization));
                result.Regions.UnionWith(nestedRegionModels);
                result.IsVolatile |= result.Regions.Any(region => region.IsVolatile);
            }

            if (regionModelData.Entities != null)
            {
                foreach (EntityModelData entityModelData in regionModelData.Entities)
                {
                    EntityModel entityModel;
                    try
                    {
                        entityModel = ModelBuilderPipeline.CreateEntityModel(entityModelData, null, localization);
                        // indicate to region model that this region is potentially volatile if it contains a volatile entity
                        result.IsVolatile |= entityModel.IsVolatile;
                        entityModel.MvcData.RegionName = regionModelData.Name;
                    }
                    catch (Exception ex)
                    {
                        // If there is a problem mapping an Entity, we replace it with an ExceptionEntity which holds the error details and carry on.
                        Log.Error(ex);
                        entityModel = new ExceptionEntity(ex);
                    }
                    result.Entities.Add(entityModel);
                }
            }

            return(result);
        }