// PRIVATE METHODS //////////////////////////////////////////////////
        #region Methods
        private void AddResourceLinkage(ResourceLinkageKey resourceLinkageKey, ResourceLinkage resourceLinkageNew)
        {
            Contract.Requires(resourceLinkageKey != null);
            Contract.Requires(resourceLinkageNew != null);

            ResourceLinkage resourceLinkageExisting;

            if (this.ResourceLinkageDictionary.TryGetValue(resourceLinkageKey, out resourceLinkageExisting) == false)
            {
                // Add initial new resource linkage
                this.ResourceLinkageDictionary.Add(resourceLinkageKey, resourceLinkageNew);
                return;
            }

            // Merge existing and new resource linkage
            var resourceLinkageExistingType = resourceLinkageExisting.Type;

            switch (resourceLinkageExistingType)
            {
            case ResourceLinkageType.ToOneResourceLinkage:
            {
                this.ResourceLinkageDictionary.Remove(resourceLinkageKey);
                this.ResourceLinkageDictionary.Add(resourceLinkageKey, resourceLinkageNew);
            }
            break;

            case ResourceLinkageType.ToManyResourceLinkage:
            {
                var toManyResourceLinkage = resourceLinkageExisting.ToManyResourceLinkage
                                            .SafeToList();
                toManyResourceLinkage.AddRange(resourceLinkageNew.ToManyResourceLinkage);

                toManyResourceLinkage = toManyResourceLinkage.Distinct()
                                        .SafeToList();

                var resourceLinkageMerged = new ResourceLinkage(toManyResourceLinkage);

                this.ResourceLinkageDictionary.Remove(resourceLinkageKey);
                this.ResourceLinkageDictionary.Add(resourceLinkageKey, resourceLinkageMerged);
            }
            break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        public void AddResourceLinkage <TFromResource, TToResource>(IServiceModel serviceModel, IToManyResourceLinkage <TFromResource, TToResource> toManyResourceLinkage)
            where TFromResource : class, IResource
            where TToResource : class, IResource
        {
            Contract.Requires(serviceModel != null);
            Contract.Requires(toManyResourceLinkage != null);

            // Create ResourceLinkageKey from ToManyResourceLinkage.
            var fromClrResourceType = typeof(TFromResource);
            var fromResourceType    = serviceModel.GetResourceType(fromClrResourceType);

            var fromClrResource           = toManyResourceLinkage.FromResource;
            var fromApiResourceIdentifier = fromResourceType.GetApiResourceIdentifier(fromClrResource);

            var fromApiRel = toManyResourceLinkage.FromRel;

            var resourceLinkageKey = new ResourceLinkageKey(fromApiResourceIdentifier, fromApiRel);

            // Create ResourceLinkage from ToManyResourceLinkage.
            var toApiResourceIdentifierCollection = Enumerable.Empty <ResourceIdentifier>()
                                                    .ToList();

            var toClrResourceCollection = toManyResourceLinkage.ToResourceCollection;

            if (toClrResourceCollection != null)
            {
                var toClrResourceType = typeof(TToResource);
                var toResourceType    = serviceModel.GetResourceType(toClrResourceType);

                toApiResourceIdentifierCollection = toClrResourceCollection.Select(toResourceType.GetApiResourceIdentifier)
                                                    .ToList();
            }

            var resourceLinkage = new ResourceLinkage(toApiResourceIdentifierCollection);

            // Add ResourceLinkage to this DocumentBuilderContext
            this.AddResourceLinkage(resourceLinkageKey, resourceLinkage);
        }
        public void AddResourceLinkage <TFromResource, TToResource>(IServiceModel serviceModel, IToOneResourceLinkage <TFromResource, TToResource> toOneResourceLinkage)
            where TFromResource : class, IResource
            where TToResource : class, IResource
        {
            Contract.Requires(serviceModel != null);
            Contract.Requires(toOneResourceLinkage != null);

            // Create ResourceLinkageKey from ToOneResourceLinkage.
            var fromClrResourceType = typeof(TFromResource);
            var fromResourceType    = serviceModel.GetResourceType(fromClrResourceType);

            var fromClrResource           = toOneResourceLinkage.FromResource;
            var fromApiResourceIdentifier = fromResourceType.GetApiResourceIdentifier(fromClrResource);

            var fromApiRel = toOneResourceLinkage.FromRel;

            var resourceLinkageKey = new ResourceLinkageKey(fromApiResourceIdentifier, fromApiRel);

            // Create ResourceLinkage from ToOneResourceLinkage
            var toApiResourceIdentifier = default(ResourceIdentifier);

            var toClrResource = toOneResourceLinkage.ToResource;

            if (toClrResource != null)
            {
                var toClrResourceType = typeof(TToResource);
                var toResourceType    = serviceModel.GetResourceType(toClrResourceType);

                toApiResourceIdentifier = toResourceType.GetApiResourceIdentifier(toClrResource);
            }

            var resourceLinkage = new ResourceLinkage(toApiResourceIdentifier);

            // Add ResourceLinkage to this DocumentBuilderContext
            this.AddResourceLinkage(resourceLinkageKey, resourceLinkage);
        }
        private void ResolveResourceRelationship(IDomRelationship domRelationship,
                                                 IHypermediaContext hypermediaContext,
                                                 IHypermediaAssembler hypermediaAssembler,
                                                 IResourcePathContext resourcePathContext,
                                                 Type clrResourceType,
                                                 object clrResource,
                                                 DomReadWriteRelationships domReadWriteRelationships)
        {
            if (domRelationship.IsReadOnly)
            {
                return;
            }

            // Resolve read-write relationship
            var domReadWriteRelationship = (DomReadWriteRelationship)domRelationship;

            // .. Rel
            var apiRelationshipRel = domReadWriteRelationship.Rel;

            // .. Links
            var linkContexts         = new List <ILinkContext>();
            var domRelationshipLinks = (IDomLinks)domReadWriteRelationship.GetNode(DomNodeType.Links);

            if (domRelationshipLinks != null)
            {
                if (domRelationshipLinks.IsReadOnly)
                {
                    // A read-write relationship contains unexpected read-only relationship links.
                    var detail = ServerErrorStrings.DomExceptionDetailReadWriteNodeHasUnexpectedReadOnlyChildNode
                                 .FormatWith(DomNodeType.Relationship, DomNodeType.Links);
                    throw new DomException(detail);
                }

                var domReadWriteRelationshipLinks = (DomReadWriteLinks)domRelationshipLinks;
                foreach (var domLink in domReadWriteRelationshipLinks.Nodes().Cast <IDomLink>())
                {
                    if (domLink.IsReadOnly)
                    {
                        // A read-write relationship contains unexpected read-only relationship link.
                        var detail = ServerErrorStrings.DomExceptionDetailReadWriteNodeHasUnexpectedReadOnlyChildNode
                                     .FormatWith(DomNodeType.Relationship, DomNodeType.Link);
                        throw new DomException(detail);
                    }

                    // Resolve read-write relationship link
                    var domReadWriteLink = (DomReadWriteLink)domLink;
                    var apiLinkRel       = domReadWriteLink.Rel;

                    var apiLinkMeta = default(Meta);
                    var domMeta     = (IDomMeta)domReadWriteLink.GetNode(DomNodeType.Meta);
                    if (domMeta != null)
                    {
                        apiLinkMeta = domMeta.Meta;
                    }

                    var linkContext = new LinkContext(apiLinkRel, apiLinkMeta);
                    linkContexts.Add(linkContext);
                }
            }

            // .. Data
            var resourceType = this.ServiceModel.GetResourceType(clrResourceType);
            var fromApiResourceIdentifier = resourceType.GetApiResourceIdentifier(clrResource);

            var             resourceLinkageKey = new ResourceLinkageKey(fromApiResourceIdentifier, apiRelationshipRel);
            ResourceLinkage resourceLinkage;
            var             hasResourceLinkage = this.DocumentBuilderContext.TryGetResourceLinkage(resourceLinkageKey, out resourceLinkage);

            // .. Meta
            var apiRelationshipMeta = default(Meta);
            var domRelationshipMeta = (IDomMeta)domReadWriteRelationship.GetNode(DomNodeType.Meta);

            if (domRelationshipMeta != null)
            {
                apiRelationshipMeta = domRelationshipMeta.Meta;
            }

            // Create the correct relationship context based on resource linkage (if any).
            RelationshipContext relationshipContext;

            if (hasResourceLinkage)
            {
                var resourceLinkageType = resourceLinkage.Type;
                switch (resourceLinkageType)
                {
                case ResourceLinkageType.ToOneResourceLinkage:
                {
                    var toOneResourceLinkage = resourceLinkage.ToOneResourceLinkage;
                    relationshipContext = new ToOneRelationshipContext(apiRelationshipRel, linkContexts, toOneResourceLinkage, apiRelationshipMeta);
                }
                break;

                case ResourceLinkageType.ToManyResourceLinkage:
                {
                    var toManyResourceLinkage = resourceLinkage.ToManyResourceLinkage;
                    relationshipContext = new ToManyRelationshipContext(apiRelationshipRel, linkContexts, toManyResourceLinkage, apiRelationshipMeta);
                }
                break;

                default:
                {
                    var detail = InfrastructureErrorStrings.InternalErrorExceptionDetailUnknownEnumerationValue
                                 .FormatWith(typeof(ResourceLinkageType).Name, resourceLinkageType);
                    throw new InternalErrorException(detail);
                }
                }
            }
            else
            {
                relationshipContext = new RelationshipContext(apiRelationshipRel, linkContexts, apiRelationshipMeta);
            }

            // Create relationship.
            var apiResourceRelationship = hypermediaAssembler.CreateResourceRelationship(hypermediaContext, resourcePathContext, clrResourceType, clrResource, relationshipContext);

            // Replace the old DOM read-write relationship node with a new DOM read-only relationship created by the framework.
            var domReadOnlyRelationship = DomReadOnlyRelationship.Create(apiRelationshipRel, apiResourceRelationship);

            domReadWriteRelationships.ReplaceNode(domReadWriteRelationship, domReadOnlyRelationship);
        }
 public bool TryGetResourceLinkage(ResourceLinkageKey resourceLinkageKey, out ResourceLinkage resourceLinkage)
 {
     return(this.ResourceLinkageDictionary.TryGetValue(resourceLinkageKey, out resourceLinkage));
 }