private static SelectedConfigSections FillDefaults(ArgsParser parser, SyncConfigurationSection syncConfig) { SelectedConfigSections sections = new SelectedConfigSections(); if (string.IsNullOrEmpty(parser.ScopeName) && syncConfig.SyncScopes.Count == 1) { sections.SelectedSyncScope = syncConfig.SyncScopes.Cast <SyncScopeConfigElement>().First(); parser.ScopeName = sections.SelectedSyncScope.Name; } else { sections.SelectedSyncScope = syncConfig.SyncScopes.Cast <SyncScopeConfigElement>().Single((e) => e.Name.Equals(parser.ScopeName, StringComparison.InvariantCultureIgnoreCase)); } if (string.IsNullOrEmpty(parser.TargetDatabaseName) && syncConfig.Databases.Count == 1) { sections.SelectedTargetDatabase = syncConfig.Databases.Cast <TargetDatabaseConfigElement>().First(); parser.TargetDatabaseName = sections.SelectedTargetDatabase.Name; } else { sections.SelectedTargetDatabase = syncConfig.Databases.Cast <TargetDatabaseConfigElement>().Single((e) => e.Name.Equals(parser.TargetDatabaseName, StringComparison.InvariantCultureIgnoreCase)); } return(sections); }
private static void ValidateCSDLMode(ArgsParser parser) { if (parser.OperationMode != OperationMode.Codegen) { throw new InvalidOperationException("Only /mode:codegen allowed when /url flag is used."); } if (parser.CodeGenMode == CodeGenTarget.Server) { throw new InvalidOperationException("/target:server is not a valid target when /url flag is used. Only isclient and client targets allowed."); } }
private static void ValidateConfigFile(ArgsParser parser, SyncConfigurationSection syncConfig) { if (syncConfig == null) { throw new InvalidOperationException("Unable to parse config file."); } if (syncConfig.SyncScopes.Count == 0) { throw new InvalidOperationException("Config file should contain atleast one <SyncScope> definition."); } if (syncConfig.Databases.Count == 0) { throw new InvalidOperationException( "Config file should contain atleast one <TargetDatabase> definition."); } if (syncConfig.SyncScopes.Count > 1 && string.IsNullOrEmpty(parser.ScopeName)) { throw new InvalidOperationException( "More than one <SyncScope> definitions found. Specify /scopename parameter."); } if (syncConfig.Databases.Count > 1 && string.IsNullOrEmpty(parser.TargetDatabaseName)) { throw new InvalidOperationException( "More than one <TargetDatabase> definitions found. Specify /database parameter."); } if (!string.IsNullOrEmpty(parser.ScopeName) && syncConfig.SyncScopes.Cast <SyncScopeConfigElement>() .FirstOrDefault(e => e.Name.Equals(parser.ScopeName, StringComparison.InvariantCultureIgnoreCase)) == null) { throw new ArgumentException("Scopename not found in SyncConfiguration/SyncScope definition.", parser.ScopeName); } if (!string.IsNullOrEmpty(parser.TargetDatabaseName) && syncConfig.Databases.Cast <TargetDatabaseConfigElement>() .FirstOrDefault( e => e.Name.Equals(parser.TargetDatabaseName, StringComparison.InvariantCultureIgnoreCase)) == null) { throw new ArgumentException("TargetDatabase not found in SyncConfiguration/Databases definition.", parser.ScopeName); } }
/// <summary> /// Entry point /// </summary> /// <param name="args"></param> public static void Main(string[] args) { try { ArgsParser parser = null; try { parser = ArgsParser.ParseArgs(args); } catch (Exception e) { LogArgsError(e.Message); return; } if (parser.HelpRequested) { return; } if (!parser.ModeSpecified) { LogArgsError("Required argument /mode is not specified."); return; } if (parser.UseCSDLUrl) { // This means user have specified a Config file. Read it and create the DbSyncDescription for it. ProcessCSDLUri(parser); } else { // This means user have specified a Config file. Read it and create the DbSyncDescription for it. ProcessConfigFile(parser); } Log("{0} completed with no errors.", Constants.AssemblyName); } catch (Exception e) { Log(e.ToString()); Log("{0} failed.", Constants.AssemblyName); } Console.ReadLine(); }
private static void ProcessCSDLUri(ArgsParser parser) { Dictionary <string, Dictionary <string, string> > tablesToColumnMappingsInfo = new Dictionary <string, Dictionary <string, string> >(); ValidateCSDLMode(parser); string serviceUri = null; DbSyncScopeDescription scopeDescription = CSDLParser.GetDescriptionFromUri(parser, parser.CSDLUrl, out serviceUri); Log("Generating files..."); EntityGenerator generator = EntityGeneratorFactory.Create(parser.CodeGenMode, null /* null syncSchema - not needed for client code generation */); generator.GenerateEntities(parser.GeneratedFilePrefix, string.IsNullOrEmpty(parser.Namespace) ? string.IsNullOrEmpty(parser.GeneratedFilePrefix) ? scopeDescription.ScopeName : parser.GeneratedFilePrefix : parser.Namespace, scopeDescription, tablesToColumnMappingsInfo, parser.WorkingDirectory, parser.Language, serviceUri); }
private static void LogArgsError(string errorMessage) { Log(errorMessage); Log(ArgsParser.GetHelpString()); }
private static DbSyncScopeDescription ParseCSDLDocument(ArgsParser parser, string uriString, XDocument document) { DbSyncScopeDescription scopeDescription = null; Uri 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. string 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 XElement dataServicesElem = document.Root.Element(Constants.SyncScopeDataServicesElement); if (dataServicesElem == null) { throw new CsdlException("No <DataServices> element found in the <edmx> document."); } XElement 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 (XElement entity in schemaElement.Elements(Constants.SyncScopeEntityTypeElement)) { XAttribute nameAttr = entity.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (nameAttr == null) { throw new CsdlException("<EntityType> has no Name attribute. \n" + entity.ToString()); } // Parse each entity and create a DbSyncTableDescription DbSyncTableDescription table = new DbSyncTableDescription(nameAttr.Value); // Look for <Key> element XElement keyElem = entity.Element(Constants.SyncScopeEntityTypeKeyElement); if (keyElem == null) { throw new CsdlException("<EntityType> has no <Key> elements defined. \n" + entity.ToString()); } List<string> keyNames = new List<string>(); // Loop over each <PropertyRef> element and add it to the list for lookup foreach (XElement prop in keyElem.Elements(Constants.SyncScopeEntityTypeKeyRefElement)) { XAttribute keyName = prop.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (keyName != null) { keyNames.Add(keyName.Value); } } // Loop over each <Property> element and add it as a DbSyncColumnDescription foreach (XElement field in entity.Elements(Constants.SyncScopeEntityTypePropertyElement)) { // Read Property name XAttribute fieldName = field.Attribute(Constants.SyncScopeEntityTypeNameAttribute); if (fieldName == null) { throw new CsdlException("<Property> has no Name attribute. \n" + field.ToString()); } // Read Property Edm type XAttribute fieldType = field.Attribute(Constants.SyncScopeEntityTypeTypeAttribute); if (fieldType == null) { throw new CsdlException("<Property> has no Type attribute. \n" + field.ToString()); } // Read Property Nullable attribute XAttribute fieldNullable = field.Attribute(Constants.SyncScopeEntityTypeNullableAttribute); DbSyncColumnDescription 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; }
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 void ValidateConfigFile(ArgsParser parser, SyncConfigurationSection syncConfig) { if (syncConfig == null) { throw new InvalidOperationException("Unable to parse config file."); } if (syncConfig.SyncScopes.Count == 0) { throw new InvalidOperationException("Config file should contain atleast one <SyncScope> definition."); } if (syncConfig.Databases.Count == 0) { throw new InvalidOperationException("Config file should contain atleast one <TargetDatabase> definition."); } if (syncConfig.SyncScopes.Count > 1 && string.IsNullOrEmpty(parser.ScopeName)) { throw new InvalidOperationException("More than one <SyncScope> definitions found. Specify /scopename parameter."); } if (syncConfig.Databases.Count > 1 && string.IsNullOrEmpty(parser.TargetDatabaseName)) { throw new InvalidOperationException("More than one <TargetDatabase> definitions found. Specify /database parameter."); } if (!string.IsNullOrEmpty(parser.ScopeName) && syncConfig.SyncScopes.Cast<SyncScopeConfigElement>().FirstOrDefault((e) => e.Name.Equals(parser.ScopeName, StringComparison.InvariantCultureIgnoreCase)) == null) { throw new ArgumentException("Scopename not found in SyncConfiguration/SyncScope definition.", parser.ScopeName); } if (!string.IsNullOrEmpty(parser.TargetDatabaseName) && syncConfig.Databases.Cast<TargetDatabaseConfigElement>().FirstOrDefault((e) => e.Name.Equals(parser.TargetDatabaseName, StringComparison.InvariantCultureIgnoreCase)) == null) { throw new ArgumentException("TargetDatabase not found in SyncConfiguration/Databases definition.", parser.ScopeName); } }
private static void ProcessConfigFile(ArgsParser parser) { DbSyncScopeDescription scopeDescription; Dictionary<string, Dictionary<string, string>> tablesToColumnMappingsInfo = new Dictionary<string, Dictionary<string, string>>(); if (string.IsNullOrEmpty(parser.ConfigFile)) { LogArgsError("Required argument /scopeconfig is not specified."); return; } if (!System.IO.File.Exists(parser.ConfigFile)) { LogArgsError("Unable to find scopeconfig file '" + parser.ConfigFile + "'"); return; } System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename = parser.ConfigFile }, ConfigurationUserLevel.None); Log("Reading specified config file..."); SyncConfigurationSection syncConfig = config.GetSection(Constants.SyncConfigurationSectionName) as SyncConfigurationSection; // ValidateConfigFile the config and the passed in input parameters ValidateConfigFile(parser, syncConfig); // Fill in the defaults value for the parser. SelectedConfigSections selectedConfig = FillDefaults(parser, syncConfig); Log("Generating DbSyncScopeDescription for scope {0}...", selectedConfig.SelectedSyncScope.Name); scopeDescription = GetDbSyncScopeDescription(selectedConfig); tablesToColumnMappingsInfo = BuildColumnMappingInfo(selectedConfig); switch (parser.OperationMode) { case OperationMode.Provision: try { SqlSyncScopeProvisioning prov = new SqlSyncScopeProvisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString()), scopeDescription, selectedConfig.SelectedSyncScope.IsTemplateScope ? SqlSyncScopeProvisioningType.Template : SqlSyncScopeProvisioningType.Scope); // Note: Deprovisioning does not work because of a bug in the provider when you set the ObjectSchema property to “dbo”. // The workaround is to not set the property (it internally assumes dbo in this case) so that things work on deprovisioning. if (!String.IsNullOrEmpty(selectedConfig.SelectedSyncScope.SchemaName)) { prov.ObjectSchema = selectedConfig.SelectedSyncScope.SchemaName; } foreach (SyncTableConfigElement tableElement in selectedConfig.SelectedSyncScope.SyncTables) { // Check and set the SchemaName for individual table if specified if (!string.IsNullOrEmpty(tableElement.SchemaName)) { prov.Tables[tableElement.GlobalName].ObjectSchema = tableElement.SchemaName; } prov.Tables[tableElement.GlobalName].FilterClause = tableElement.FilterClause; foreach (FilterColumnConfigElement filterCol in tableElement.FilterColumns) { prov.Tables[tableElement.GlobalName].FilterColumns.Add(scopeDescription.Tables[tableElement.GlobalName].Columns[filterCol.Name]); } foreach (FilterParameterConfigElement filterParam in tableElement.FilterParameters) { CheckFilterParamTypeAndSize(filterParam); prov.Tables[tableElement.GlobalName].FilterParameters.Add(new SqlParameter(filterParam.Name, (SqlDbType)Enum.Parse(typeof(SqlDbType), filterParam.SqlType, true))); prov.Tables[tableElement.GlobalName].FilterParameters[filterParam.Name].Size = filterParam.DataSize; } } // enable bulk procedures. prov.SetUseBulkProceduresDefault(selectedConfig.SelectedSyncScope.EnableBulkApplyProcedures); // Create a new set of enumeration stored procs per scope. // Without this multiple scopes share the same stored procedure which is not desirable. prov.SetCreateProceduresForAdditionalScopeDefault(DbSyncCreationOption.Create); if (selectedConfig.SelectedSyncScope.IsTemplateScope) { if (!prov.TemplateExists(selectedConfig.SelectedSyncScope.Name)) { Log("Provisioning Database {0} for template scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); prov.Apply(); } else { throw new InvalidOperationException(string.Format("Database {0} already contains a template scope {1}. Please deprovision the scope and retry.", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name)); } } else { if (!prov.ScopeExists(selectedConfig.SelectedSyncScope.Name)) { Log("Provisioning Database {0} for scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); prov.Apply(); } else { throw new InvalidOperationException(string.Format("Database {0} already contains a scope {1}. Please deprovision the scope and retry.", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name)); } } } catch (ConfigurationErrorsException) { throw; } catch (InvalidOperationException) { throw; } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Provisioning command. See inner exception for details.", e); } break; case OperationMode.Deprovision: try { SqlSyncScopeDeprovisioning deprov = new SqlSyncScopeDeprovisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString())); // Set the ObjectSchema property. if (!String.IsNullOrEmpty(selectedConfig.SelectedSyncScope.SchemaName)) { deprov.ObjectSchema = selectedConfig.SelectedSyncScope.SchemaName; } Log("Deprovisioning Database {0} for scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); if (selectedConfig.SelectedSyncScope.IsTemplateScope) { deprov.DeprovisionTemplate(selectedConfig.SelectedSyncScope.Name); } else { deprov.DeprovisionScope(selectedConfig.SelectedSyncScope.Name); } } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Deprovisioning command. See inner exception for details.", e); } break; case OperationMode.Deprovisionstore: try { SqlSyncScopeDeprovisioning deprov = new SqlSyncScopeDeprovisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString())); Log("Deprovisioning Store Database {0} ...", selectedConfig.SelectedTargetDatabase.Name); deprov.DeprovisionStore(); } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Deprovisioning command. See inner exception for details.", e); } break; case OperationMode.Codegen: Log("Generating files..."); EntityGenerator generator = EntityGeneratorFactory.Create(parser.CodeGenMode, selectedConfig.SelectedSyncScope.SchemaName); generator.GenerateEntities(parser.GeneratedFilePrefix, string.IsNullOrEmpty(parser.Namespace) ? string.IsNullOrEmpty(parser.GeneratedFilePrefix) ? scopeDescription.ScopeName : parser.GeneratedFilePrefix : parser.Namespace, scopeDescription, tablesToColumnMappingsInfo, parser.WorkingDirectory, parser.Language, null/*serviceUri*/); break; default: break; } }
public static ArgsParser ParseArgs(string[] args) { ArgsParser parser = new ArgsParser(); foreach (string param in args) { string[] tokens = param.Split(new char[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries); if (tokens.Count() != 2 && !tokens[0].Equals("/?", StringComparison.InvariantCultureIgnoreCase) && !tokens[0].Equals("/verbose", StringComparison.InvariantCultureIgnoreCase)) { throw new ArgumentException("Invalid parameter passed", param); } string token1 = tokens.Count() == 2 ? tokens[1] : string.Empty; switch (tokens[0].ToLowerInvariant()) { case "/mode": parser._modeSpecified = true; if (!EnumUtils.TryEnumParse <OperationMode>(token1, out parser._operationMode)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/target": if (!EnumUtils.TryEnumParse <CodeGenTarget>(token1, out parser._codeGenMode)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/language": if (!EnumUtils.TryEnumParse <CodeLanguage>(token1, out parser._language)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/scopeconfig": if (parser._csdlUri != null) { throw new InvalidOperationException("Cannot specify both /scopeconfig and /url option."); } parser._configFile = token1; break; case "/url": if (parser._configFile != null) { throw new InvalidOperationException("Cannot specify both /scopeconfig and /url option."); } parser._csdlUri = token1; if (!new Uri(parser._csdlUri).IsAbsoluteUri) { throw new ArgumentException(string.Format("Sync scope schema Uri cannot be relative."), param); } break; case "/scopename": parser._scopeName = token1; break; case "/database": parser._targetDatabaseName = token1; break; case "/outprefix": parser._generateFilePrefix = token1; break; case "/directory": parser._workingDirectory = new DirectoryInfo(token1); break; case "/namespace": parser._namespace = token1; break; case "/verbose": parser._verboseEnabled = true; break; case "/?": parser._helpRequested = true; Console.WriteLine(GetHelpString()); break; default: throw new ArgumentOutOfRangeException(param); } } return(parser); }
private static void ProcessConfigFile(ArgsParser parser) { DbSyncScopeDescription scopeDescription; Dictionary <string, Dictionary <string, string> > tablesToColumnMappingsInfo = new Dictionary <string, Dictionary <string, string> >(); if (string.IsNullOrEmpty(parser.ConfigFile)) { LogArgsError("Required argument /scopeconfig is not specified."); return; } if (!System.IO.File.Exists(parser.ConfigFile)) { LogArgsError("Unable to find scopeconfig file '" + parser.ConfigFile + "'"); return; } System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename = parser.ConfigFile }, ConfigurationUserLevel.None); Log("Reading specified config file..."); SyncConfigurationSection syncConfig = config.GetSection(Constants.SyncConfigurationSectionName) as SyncConfigurationSection; // ValidateConfigFile the config and the passed in input parameters ValidateConfigFile(parser, syncConfig); // Fill in the defaults value for the parser. SelectedConfigSections selectedConfig = FillDefaults(parser, syncConfig); Log("Generating DbSyncScopeDescription for scope {0}...", selectedConfig.SelectedSyncScope.Name); scopeDescription = GetDbSyncScopeDescription(selectedConfig); tablesToColumnMappingsInfo = BuildColumnMappingInfo(selectedConfig); switch (parser.OperationMode) { case OperationMode.Provision: Provision(selectedConfig, scopeDescription, false, parser.WorkingDirectory); break; case OperationMode.ProvisionScript: Provision(selectedConfig, scopeDescription, true, parser.WorkingDirectory); break; case OperationMode.Deprovision: Deprovision(selectedConfig, false, parser.WorkingDirectory); break; case OperationMode.DeprovisionScript: Deprovision(selectedConfig, true, parser.WorkingDirectory); break; case OperationMode.Deprovisionstore: DeprovisionStore(selectedConfig, false, parser.WorkingDirectory); break; case OperationMode.DeprovisionstoreScript: DeprovisionStore(selectedConfig, true, parser.WorkingDirectory); break; case OperationMode.Codegen: Log("Generating files..."); EntityGenerator generator = EntityGeneratorFactory.Create(parser.CodeGenMode, selectedConfig.SelectedSyncScope.SchemaName); generator.GenerateEntities(parser.GeneratedFilePrefix, string.IsNullOrEmpty(parser.Namespace) ? string.IsNullOrEmpty(parser.GeneratedFilePrefix) ? scopeDescription.ScopeName : parser.GeneratedFilePrefix : parser.Namespace, scopeDescription, tablesToColumnMappingsInfo, parser.WorkingDirectory, parser.Language, null /*serviceUri*/); break; default: break; } }
public static DbSyncScopeDescription GetDescriptionFromUri(ArgsParser parser, string uriString, out string serviceUri) { // Issue the request. WebClient request = new WebClient(); SyncSvcUtil.Log("Trying to connect to Uri '{0}' to check for SyncScopeSchema document", uriString); string 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); XDocument 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."); } XElement 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 XElement[] collectionElements = workspaceElement.Elements(Constants.SyncScopeWorkspaceCollectionElement).ToArray(); if (collectionElements.Length == 0) { throw new CsdlException("Remote SyncScope services document did not contain a <collection> element."); } else if (collectionElements.Length > 1) { SyncSvcUtil.Log("Multiple SyncScopes were found in the <service> document. Please specify the correct Url."); foreach (XElement elem in collectionElements) { XAttribute 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."); } else { // We have exactly one SyncScope. Download the schema for that XAttribute 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].ToString())); } // Look for and remove $syncScopes string 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 CSDLParser.GetDescriptionFromUri(parser, uriString, out serviceUri); } } else 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); } else { throw new CsdlException(string.Format("Downloaded XML content is not a valid <service> document. \nDocument Content: {0}", content)); } }
private static SelectedConfigSections FillDefaults(ArgsParser parser, SyncConfigurationSection syncConfig) { SelectedConfigSections sections = new SelectedConfigSections(); if (string.IsNullOrEmpty(parser.ScopeName) && syncConfig.SyncScopes.Count == 1) { sections.SelectedSyncScope = syncConfig.SyncScopes.Cast<SyncScopeConfigElement>().First(); parser.ScopeName = sections.SelectedSyncScope.Name; } else { sections.SelectedSyncScope = syncConfig.SyncScopes.Cast<SyncScopeConfigElement>().Single((e) => e.Name.Equals(parser.ScopeName, StringComparison.InvariantCultureIgnoreCase)); } if (string.IsNullOrEmpty(parser.TargetDatabaseName) && syncConfig.Databases.Count == 1) { sections.SelectedTargetDatabase = syncConfig.Databases.Cast<TargetDatabaseConfigElement>().First(); parser.TargetDatabaseName = sections.SelectedTargetDatabase.Name; } else { sections.SelectedTargetDatabase = syncConfig.Databases.Cast<TargetDatabaseConfigElement>().Single((e) => e.Name.Equals(parser.TargetDatabaseName, StringComparison.InvariantCultureIgnoreCase)); } return sections; }
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); }
private static void ProcessCSDLUri(ArgsParser parser) { Dictionary<string, Dictionary<string, string>> tablesToColumnMappingsInfo = new Dictionary<string, Dictionary<string, string>>(); ValidateCSDLMode(parser); string serviceUri = null; DbSyncScopeDescription scopeDescription = CSDLParser.GetDescriptionFromUri(parser, parser.CSDLUrl, out serviceUri); Log("Generating files..."); EntityGenerator generator = EntityGeneratorFactory.Create(parser.CodeGenMode, null /* null syncSchema - not needed for client code generation */); generator.GenerateEntities(parser.GeneratedFilePrefix, string.IsNullOrEmpty(parser.Namespace) ? string.IsNullOrEmpty(parser.GeneratedFilePrefix) ? scopeDescription.ScopeName : parser.GeneratedFilePrefix : parser.Namespace, scopeDescription, tablesToColumnMappingsInfo, parser.WorkingDirectory, parser.Language, serviceUri); }
public static ArgsParser ParseArgs(string[] args) { ArgsParser parser = new ArgsParser(); foreach (string param in args) { string[] tokens = param.Split(new char[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries); if (tokens.Count<string>() != 2 && !tokens[0].Equals("/?", StringComparison.InvariantCultureIgnoreCase) && !tokens[0].Equals("/verbose", StringComparison.InvariantCultureIgnoreCase)) { throw new ArgumentException("Invalid parameter passed", param); } switch (tokens[0].ToLowerInvariant()) { case "/mode": parser._modeSpecified = true; if (!EnumUtils.TryEnumParse<OperationMode>(tokens[1], out parser._operationMode)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/target": if (!EnumUtils.TryEnumParse<CodeGenTarget>(tokens[1], out parser._codeGenMode)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/language": if (!EnumUtils.TryEnumParse<CodeLanguage>(tokens[1], out parser._language)) { throw new ArgumentOutOfRangeException(param, string.Format("Invalid {0} option specified.", tokens[0])); } break; case "/scopeconfig": if (parser._csdlUri != null) { throw new InvalidOperationException("Cannot specify both /scopeconfig and /url option."); } parser._configFile = tokens[1]; break; case "/url": if (parser._configFile != null) { throw new InvalidOperationException("Cannot specify both /scopeconfig and /url option."); } parser._csdlUri = tokens[1]; if (!new Uri(parser._csdlUri).IsAbsoluteUri) { throw new ArgumentException(string.Format("Sync scope schema Uri cannot be relative."), param); } break; case "/scopename": parser._scopeName = tokens[1]; break; case "/database": parser._targetDatabaseName = tokens[1]; break; case "/outprefix": parser._generateFilePrefix = tokens[1]; break; case "/directory": parser._workingDirectory = new DirectoryInfo(tokens[1]); break; case "/namespace": parser._namespace = tokens[1]; break; case "/verbose": parser._verboseEnabled = true; break; case "/?": parser._helpRequested = true; Console.WriteLine(GetHelpString()); break; default: throw new ArgumentOutOfRangeException(param); } } return parser; }
private static void ProcessConfigFile(ArgsParser parser) { DbSyncScopeDescription scopeDescription; Dictionary <string, Dictionary <string, string> > tablesToColumnMappingsInfo = new Dictionary <string, Dictionary <string, string> >(); if (string.IsNullOrEmpty(parser.ConfigFile)) { LogArgsError("Required argument /scopeconfig is not specified."); return; } if (!System.IO.File.Exists(parser.ConfigFile)) { LogArgsError("Unable to find scopeconfig file '" + parser.ConfigFile + "'"); return; } System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename = parser.ConfigFile }, ConfigurationUserLevel.None); Log("Reading specified config file..."); SyncConfigurationSection syncConfig = config.GetSection(Constants.SyncConfigurationSectionName) as SyncConfigurationSection; // ValidateConfigFile the config and the passed in input parameters ValidateConfigFile(parser, syncConfig); // Fill in the defaults value for the parser. SelectedConfigSections selectedConfig = FillDefaults(parser, syncConfig); Log("Generating DbSyncScopeDescription for scope {0}...", selectedConfig.SelectedSyncScope.Name); scopeDescription = GetDbSyncScopeDescription(selectedConfig); tablesToColumnMappingsInfo = BuildColumnMappingInfo(selectedConfig); switch (parser.OperationMode) { case OperationMode.Provision: try { SqlSyncScopeProvisioning prov = new SqlSyncScopeProvisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString()), scopeDescription, selectedConfig.SelectedSyncScope.IsTemplateScope ? SqlSyncScopeProvisioningType.Template : SqlSyncScopeProvisioningType.Scope); // Note: Deprovisioning does not work because of a bug in the provider when you set the ObjectSchema property to “dbo”. // The workaround is to not set the property (it internally assumes dbo in this case) so that things work on deprovisioning. if (!String.IsNullOrEmpty(selectedConfig.SelectedSyncScope.SchemaName)) { prov.ObjectSchema = selectedConfig.SelectedSyncScope.SchemaName; } foreach (SyncTableConfigElement tableElement in selectedConfig.SelectedSyncScope.SyncTables) { // Check and set the SchemaName for individual table if specified if (!string.IsNullOrEmpty(tableElement.SchemaName)) { prov.Tables[tableElement.GlobalName].ObjectSchema = tableElement.SchemaName; } prov.Tables[tableElement.GlobalName].FilterClause = tableElement.FilterClause; foreach (FilterColumnConfigElement filterCol in tableElement.FilterColumns) { prov.Tables[tableElement.GlobalName].FilterColumns.Add(scopeDescription.Tables[tableElement.GlobalName].Columns[filterCol.Name]); } foreach (FilterParameterConfigElement filterParam in tableElement.FilterParameters) { CheckFilterParamTypeAndSize(filterParam); prov.Tables[tableElement.GlobalName].FilterParameters.Add(new SqlParameter(filterParam.Name, (SqlDbType)Enum.Parse(typeof(SqlDbType), filterParam.SqlType, true))); prov.Tables[tableElement.GlobalName].FilterParameters[filterParam.Name].Size = filterParam.DataSize; } } // enable bulk procedures. prov.SetUseBulkProceduresDefault(selectedConfig.SelectedSyncScope.EnableBulkApplyProcedures); // Create a new set of enumeration stored procs per scope. // Without this multiple scopes share the same stored procedure which is not desirable. prov.SetCreateProceduresForAdditionalScopeDefault(DbSyncCreationOption.Create); if (selectedConfig.SelectedSyncScope.IsTemplateScope) { if (!prov.TemplateExists(selectedConfig.SelectedSyncScope.Name)) { Log("Provisioning Database {0} for template scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); prov.Apply(); } else { throw new InvalidOperationException(string.Format("Database {0} already contains a template scope {1}. Please deprovision the scope and retry.", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name)); } } else { if (!prov.ScopeExists(selectedConfig.SelectedSyncScope.Name)) { Log("Provisioning Database {0} for scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); prov.Apply(); } else { throw new InvalidOperationException(string.Format("Database {0} already contains a scope {1}. Please deprovision the scope and retry.", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name)); } } } catch (ConfigurationErrorsException) { throw; } catch (InvalidOperationException) { throw; } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Provisioning command. See inner exception for details.", e); } break; case OperationMode.Deprovision: try { SqlSyncScopeDeprovisioning deprov = new SqlSyncScopeDeprovisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString())); // Set the ObjectSchema property. if (!String.IsNullOrEmpty(selectedConfig.SelectedSyncScope.SchemaName)) { deprov.ObjectSchema = selectedConfig.SelectedSyncScope.SchemaName; } Log("Deprovisioning Database {0} for scope {1}...", selectedConfig.SelectedTargetDatabase.Name, selectedConfig.SelectedSyncScope.Name); if (selectedConfig.SelectedSyncScope.IsTemplateScope) { deprov.DeprovisionTemplate(selectedConfig.SelectedSyncScope.Name); } else { deprov.DeprovisionScope(selectedConfig.SelectedSyncScope.Name); } } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Deprovisioning command. See inner exception for details.", e); } break; case OperationMode.Deprovisionstore: try { SqlSyncScopeDeprovisioning deprov = new SqlSyncScopeDeprovisioning(new SqlConnection(selectedConfig.SelectedTargetDatabase.GetConnectionString())); Log("Deprovisioning Store Database {0} ...", selectedConfig.SelectedTargetDatabase.Name); deprov.DeprovisionStore(); } catch (Exception e) { throw new InvalidOperationException("Unexpected error when executing the Deprovisioning command. See inner exception for details.", e); } break; case OperationMode.Codegen: Log("Generating files..."); EntityGenerator generator = EntityGeneratorFactory.Create(parser.CodeGenMode, selectedConfig.SelectedSyncScope.SchemaName); generator.GenerateEntities(parser.GeneratedFilePrefix, string.IsNullOrEmpty(parser.Namespace) ? string.IsNullOrEmpty(parser.GeneratedFilePrefix) ? scopeDescription.ScopeName : parser.GeneratedFilePrefix : parser.Namespace, scopeDescription, tablesToColumnMappingsInfo, parser.WorkingDirectory, parser.Language, null /*serviceUri*/); break; default: break; } }