//===================================================================== /// <summary> /// This is used to get the MSDN URL for the given .NET Framework member ID /// </summary> /// <param name="id">The member ID to look up</param> /// <returns>The MSDN URL for the member ID or null if not found</returns> public string GetMsdnUrl(string id) { string endPoint = null; bool success = false; if(msdnService != null && !cachedMsdnIds.TryGetValue(id, out endPoint)) { getContentRequest msdnRequest = new getContentRequest(); msdnRequest.contentIdentifier = "AssetId:" + id; msdnRequest.locale = this.Locale; try { getContentResponse msdnResponse = msdnService.GetContent(msdnRequest); endPoint = msdnResponse.contentId; success = true; } catch(WebException ex) { // Ignore failures, just turn off the service and note the last error for the caller msdnService.Dispose(); msdnService = null; this.DisabledReason = ex.Message; Exception innerEx = ex.InnerException; while(innerEx != null) { this.DisabledReason += "\r\n" + innerEx.Message; innerEx = innerEx.InnerException; } // Don't save changes to the cache this.CacheItemsAdded = false; } catch(SoapException ex) { if(ex.Message.IndexOf("content identifier not found", StringComparison.OrdinalIgnoreCase) != -1 || ex.Detail.OuterXml.IndexOf("mtpsContentIdentifierNotFound", StringComparison.Ordinal) != -1) { // Lookup failed (ID not found). Cache the result though since it isn't there. success = true; // For certain inherited explicitly implemented interface members, Microsoft is using // incorrect member IDs in the XML comments files and on the content service. If the // failed member ID looks like one of them, try using the broken ID for the look up. string brokenId = id; if(reDictionaryEII.IsMatch(brokenId)) brokenId = reGenericEII.Replace(brokenId, "#I$1{TKey@TValue}#"); else if(reGenericEII.IsMatch(brokenId)) brokenId = reGenericEII.Replace(brokenId, "#I$1{T}#"); // Don't bother if they are the same if(brokenId != id) { endPoint = this.GetMsdnUrl(brokenId); if(endPoint != null) endPoint = endPoint.Substring(endPoint.LastIndexOf('/') + 1); } } else { // Ignore failures, just turn off the service and note the last error for the caller msdnService.Dispose(); msdnService = null; this.DisabledReason = ex.Message; Exception innerEx = ex.InnerException; while(innerEx != null) { this.DisabledReason += "\r\n" + innerEx.Message; innerEx = innerEx.InnerException; } // Don't save changes to the cache this.CacheItemsAdded = false; } } // We'll cache the result but will only mark the cache as changed if successful so as not to // save null results from failures caused by issues other than not being found. cachedMsdnIds[id] = endPoint; if(success) this.CacheItemsAdded = true; } if(String.IsNullOrEmpty(endPoint)) return null; return String.Format(CultureInfo.InvariantCulture, "http://msdn2.microsoft.com/{0}/library/{1}", this.Locale, endPoint); }
//===================================================================== /// <summary> /// Default constructor /// </summary> /// <remarks>The default constructor creates a simple dictionary to hold the cached MSDN content IDs</remarks> public MsdnResolver() { cachedMsdnIds = new Dictionary<string, string>(); this.Locale = "en-us"; msdnService = new ContentService(); msdnService.appIdValue = new appId(); msdnService.appIdValue.value = "Sandcastle"; msdnService.SoapVersion = SoapProtocolVersion.Soap11; }