public static DbSyncScopeDescription GetDescriptionFromUri(ArgsParser parser, string uriString, out string serviceUri) { // Issue the request. var request = new WebClient(); SyncSvcUtil.Log("Trying to connect to Uri '{0}' to check for SyncScopeSchema document", uriString); var content = request.DownloadString(uriString); // Check to see if its a SyncScopes <services> document or an SyncScopeSchema <edmx> document. if (parser.UseVerbose) { SyncSvcUtil.Log( "Download succeeded. Checking to see if its a SyncScope <Service> document or an SyncScopeSchema <edmx> document."); SyncSvcUtil.Log("Downloaded document content:\n {0}", content); } SyncSvcUtil.Log("Parsing downloaded document.", uriString); var document = XDocument.Parse(content); if (document == null) { throw new CsdlException("Downloaded content is not a valid XML document."); } if (document.Root.Name.Equals(Constants.SyncScopeServicesElement)) { if (parser.UseVerbose) { SyncSvcUtil.Log("Found a <service> document. Checking for SyncScopes workspace."); } var workspaceElement = document.Root.Element(Constants.SyncScopeWorkspaceElement); if (workspaceElement == null) { throw new CsdlException("Remote SyncScope services document did not contain a <workspace> element."); } // Look for <collection> element var collectionElements = workspaceElement.Elements(Constants.SyncScopeWorkspaceCollectionElement).ToArray(); if (collectionElements.Length == 0) { throw new CsdlException("Remote SyncScope services document did not contain a <collection> element."); } XAttribute hrefAttr; if (collectionElements.Length > 1) { SyncSvcUtil.Log( "Multiple SyncScopes were found in the <service> document. Please specify the correct Url."); foreach (var elem in collectionElements) { hrefAttr = elem.Attribute(Constants.SyncScopeWorkspaceCollectionHrefAttribute); SyncSvcUtil.Log("\t\t{0} - Uri: {1}{2}{0}/$metadata", hrefAttr.Value, parser.CSDLUrl, parser.CSDLUrl.EndsWith("/") ? string.Empty : "/"); } throw new CsdlException("Multiple SyncScopes found."); } // We have exactly one SyncScope. Download the schema for that hrefAttr = collectionElements[0].Attribute(Constants.SyncScopeWorkspaceCollectionHrefAttribute); if (hrefAttr == null) { throw new CsdlException("No Href attribute was found in the <collection> element."); } // Ensure the href param is not empty as this is the scopeName if (string.IsNullOrEmpty(hrefAttr.Value)) { throw new CsdlException( string.Format("Href attribute in <collection> must have a non empty string.\n Content: {0}", collectionElements[0])); } // Look for and remove $syncScopes var origUrl = parser.CSDLUrl; if (origUrl.EndsWith("$syncscopes", StringComparison.InvariantCultureIgnoreCase)) { origUrl = origUrl.Substring(0, origUrl.LastIndexOf("/")); } uriString = string.Format("{0}{1}{2}/$metadata", origUrl, origUrl.EndsWith("/") ? string.Empty : "/", hrefAttr.Value); return(GetDescriptionFromUri(parser, uriString, out serviceUri)); } if (document.Root.Name.Equals(Constants.SyncScopeEdmxElement)) { // Set the service URI and remove $metadata token from it. //Remove the / at the end if present. serviceUri = (uriString.EndsWith("/")) ? uriString.Substring(0, uriString.Length - 1) : uriString; //The service will render the schema only if there is a $metadata at the end in the Uri. serviceUri = serviceUri.Substring(0, serviceUri.Length - "/$metadata".Length); //Remove the scope name serviceUri = serviceUri.Substring(0, serviceUri.LastIndexOf("/") + 1); return(ParseCSDLDocument(parser, uriString, document)); } throw new CsdlException( string.Format("Downloaded XML content is not a valid <service> document. \nDocument Content: {0}", content)); }
private static DbSyncScopeDescription ParseCSDLDocument(ArgsParser parser, string uriString, XDocument document) { DbSyncScopeDescription scopeDescription = null; var uri = new Uri(uriString); // Assumption is that for OData Sync metadata document, the URI is of format http://foo/snc.svc/scopename/$metadata. // In this case we are looking for the last but one segment. var scopeName = uri.Segments[uri.Segments.Length - 2]; if (scopeName.EndsWith("/")) { scopeName = scopeName.Substring(0, scopeName.Length - 1); } if (parser.UseVerbose) { SyncSvcUtil.Log("Parsed ScopeName as {0}", scopeName); } // Its an CSDL document var dataServicesElem = document.Root.Element(Constants.SyncScopeDataServicesElement); if (dataServicesElem == null) { throw new CsdlException("No <DataServices> element found in the <edmx> document."); } var schemaElement = dataServicesElem.Element(Constants.SyncScopeSchemaElement); if (schemaElement == null) { throw new CsdlException("No <Schema> element found in the <DataServices> document."); } scopeDescription = new DbSyncScopeDescription(scopeName); // Loop over each <EntityType> element and add it as a DbSyncTableDescription foreach (var entity in schemaElement.Elements(Constants.SyncScopeEntityTypeElement)) { var nameAttr = entity.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (nameAttr == null) { throw new CsdlException("<EntityType> has no Name attribute. \n" + entity); } // Parse each entity and create a DbSyncTableDescription var table = new DbSyncTableDescription(nameAttr.Value); // Look for <Key> element var keyElem = entity.Element(Constants.SyncScopeEntityTypeKeyElement); if (keyElem == null) { throw new CsdlException("<EntityType> has no <Key> elements defined. \n" + entity); } var keyNames = new List <string>(); // Loop over each <PropertyRef> element and add it to the list for lookup foreach (var prop in keyElem.Elements(Constants.SyncScopeEntityTypeKeyRefElement)) { var keyName = prop.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (keyName != null) { keyNames.Add(keyName.Value); } } // Loop over each <Property> element and add it as a DbSyncColumnDescription foreach (var field in entity.Elements(Constants.SyncScopeEntityTypePropertyElement)) { // Read Property name var fieldName = field.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (fieldName == null) { throw new CsdlException("<Property> has no Name attribute. \n" + field); } // Read Property Edm type var fieldType = field.Attribute(Constants.SyncScopeEntityTypeTypeAttribute); if (fieldType == null) { throw new CsdlException("<Property> has no Type attribute. \n" + field); } // Read Property Nullable attribute var fieldNullable = field.Attribute(Constants.SyncScopeEntityTypeNullableAttribute); var column = new DbSyncColumnDescription(fieldName.Value, GetSqlTypeForEdm(fieldType.Value)); if (fieldNullable != null && bool.Parse(fieldNullable.Value)) { column.IsNullable = true; } column.IsPrimaryKey = keyNames.Contains(fieldName.Value); table.Columns.Add(column); } scopeDescription.Tables.Add(table); } return(scopeDescription); }