/// <summary> /// Gets the taxonomy hierarchy for client term set. /// </summary> /// <param name="termSet">Term set object holding Client terms</param> /// <param name="termStoreDetails">Term Store object containing Term store data</param> /// <returns>Serialized Object of Client Term Set</returns> internal static string GetClientTermSetHierarchy(TermSet termSet, TermStoreDetails termStoreDetails) { string result = string.Empty; try { ClientTermSets tempClientTermSet = new ClientTermSets(); tempClientTermSet.Name = termSet.Name; /////Retrieve the Terms - level 1 tempClientTermSet.ClientTerms = new List <Client>(); TermCollection termColl = termSet.Terms; foreach (Term term in termColl) { Client tempTermPG = new Client(); tempTermPG.Name = term.Name; if (term.CustomProperties.Count > 0) { tempTermPG.Url = string.Empty; tempTermPG.Id = string.Empty; foreach (KeyValuePair <string, string> customProperty in term.CustomProperties) { if (customProperty.Key.Equals(termStoreDetails.CustomPropertyName, StringComparison.Ordinal)) { tempTermPG.Url = customProperty.Value; } if (customProperty.Key.Equals(ServiceConstantStrings.ClientCustomPropertiesId, StringComparison.Ordinal)) { tempTermPG.Id = customProperty.Value; } } } tempClientTermSet.ClientTerms.Add(tempTermPG); } /////Serialize the Term set (Practice Group) object to get all terms under it result = JsonConvert.SerializeObject(tempClientTermSet); } catch (Exception exception) { result = Logger.LogError(exception, MethodBase.GetCurrentMethod().DeclaringType.Name, MethodBase.GetCurrentMethod().Name, ServiceConstantStrings.LogTableName); } return(result); }
/// <summary> /// This method will be called if the clients are defined as term set and not as terms /// </summary> /// <param name="termSet">Term set object holding Client terms</param> /// <param name="termStoreDetails">Term Store object containing Term store data</param> /// <returns>Serialized Object of Client Term Set</returns> private ClientTermSets GetClientTermSetHierarchy(ClientContext clientContext, TermSet termSet, TermStoreDetails termStoreDetails) { ClientTermSets tempClientTermSet = new ClientTermSets(); try { tempClientTermSet.Name = taxonomySettings.ClientTermSetName; /////Retrieve the Terms - level 1 tempClientTermSet.ClientTerms = new List <Client>(); TermCollection termColl = null; termColl = termSet.Terms; return(GetClientTermProperties(termColl, termStoreDetails, tempClientTermSet)); } catch (Exception ex) { customLogger.LogError(ex, MethodBase.GetCurrentMethod().DeclaringType.Name, MethodBase.GetCurrentMethod().Name, logTables.SPOLogTable); throw; } }
/// <summary> /// Creates sample data based on the information provided /// </summary> /// <param name="listval">Matter details collection</param> /// <param name="clientDetails">Client details collection</param> /// <param name="configVal">Config values from Excel</param> internal static void CreateData(List<DataStorage> listval, ClientTermSets clientDetails, Dictionary<string, string> configVal) { try { int successMatterNameCount = 0, alreadyExistsMatterCount = 0; Regex validateMatterId = new Regex(ConfigurationManager.AppSettings["SpecialCharacterExpressionMatterId"]), validateMatterTitle = new Regex(ConfigurationManager.AppSettings["SpecialCharacterExpressionMatterTitle"]), validateMatterDesc = new Regex(ConfigurationManager.AppSettings["SpecialCharacterExpressionMatterDescription"]); //Read data from term store TermSets terms = TermStoreOperations.FetchGroupTerms(configVal); if (null == terms) { Utility.DisplayAndLogError(errorFilePath, "Failed to get Group Terms, skipping matter creation."); return; } else { MatterMetadata matterMetadata = new MatterMetadata(); //retrieve data from the list for (int count = 0; count < listval.Count; count++) { string clientName = listval[count].ClientName; /* Read from Term store */ Client client = clientDetails.ClientTerms.Where(item => item.ClientName.Equals(clientName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (null == client) { Console.WriteLine("Failed to get client Id and/or client Url from term store for '{0}' client.", clientName); Console.WriteLine("-------------------------------------------------------------------------------"); continue; } List<string> practiceGroupsList = Utility.ProcessString(listval[count].PracticeGroup).Split(';').ToList(); List<string> areaOfLawsList = Utility.ProcessString(listval[count].AreaOfLaw).Split(';').ToList(); List<string> subAreaOfLawsList = Utility.ProcessString(listval[count].SubAreaOfLaw).Split(';').ToList(); string folders = string.Empty; string documentTemplate = string.Empty; bool flag = false; AssociateTermStoreProperties(listval, terms, matterMetadata, count, practiceGroupsList, areaOfLawsList, subAreaOfLawsList, ref folders, ref documentTemplate, ref flag); if (string.IsNullOrWhiteSpace(documentTemplate) || string.IsNullOrWhiteSpace(listval[count].DefaultContentType)) { Console.WriteLine("Skipping matter creation as no matching document templates exists in term store corresponding to entry for '{0}' in the configuration Excel", client.ClientName); Console.WriteLine("-------------------------------------------------------------------------------"); continue; } string[] contentTypes = documentTemplate.Split(';'); Matter matterObj = new Matter(listval[count]); Console.WriteLine("Client details fetched"); Console.WriteLine("Client name: {0}", clientName); using (ClientContext clientContext = MatterProvisionHelperUtility.GetClientContext(client.ClientUrl, configVal)) { CheckMatterCreationStatus(configVal, ref successMatterNameCount, ref alreadyExistsMatterCount, validateMatterId, validateMatterTitle, validateMatterDesc, matterMetadata, clientName, client, folders, contentTypes, matterObj, clientContext); } } // end of for Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(ConfigurationManager.AppSettings["MatterSuccess"], successMatterNameCount, listval.Count); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(ConfigurationManager.AppSettings["MatterFound"], alreadyExistsMatterCount); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(ConfigurationManager.AppSettings["MatterFailure"], Convert.ToString((listval.Count - (successMatterNameCount + alreadyExistsMatterCount)), CultureInfo.InvariantCulture), listval.Count); Console.ForegroundColor = ConsoleColor.White; } } catch (Exception exception) { Utility.DisplayAndLogError(errorFilePath, "Message: " + exception.Message + "\nStacktrace: " + exception.StackTrace); } }
/// <summary> /// Reverts the changes for sample data based on the information provided /// </summary> /// <param name="matterDetailsCollection">Matter details collection</param> /// <param name="clientCollection">Client details collection</param> /// <param name="configVal">Configuration values from Excel</param> internal static void RevertData(List<DataStorage> matterDetailsCollection, ClientTermSets clientCollection, Dictionary<string, string> configVal) { try { if (null != matterDetailsCollection && null != clientCollection && null != configVal && 0 < matterDetailsCollection.Count) { foreach (DataStorage matterDetails in matterDetailsCollection) { Client clientObject = clientCollection.ClientTerms.Where(item => item.ClientName.Equals(matterDetails.ClientName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (null != clientObject) { using (ClientContext clientContext = MatterProvisionHelperUtility.GetClientContext(clientObject.ClientUrl, configVal)) { PropertyValues properties = clientContext.Web.Lists.GetByTitle(matterDetails.MatterPrefix).RootFolder.Properties; clientContext.Load(properties); clientContext.ExecuteQuery(); Matter matter = new Matter(matterDetails); matter.MatterGuid = properties.FieldValues.ContainsKey("MatterGUID") ? System.Web.HttpUtility.HtmlDecode(Convert.ToString(properties.FieldValues["MatterGUID"], CultureInfo.InvariantCulture)) : matterDetails.MatterPrefix; MatterProvisionHelper.DeleteMatter(clientContext, matter); } } else { Console.WriteLine("Failed to get Client Url for client: {0}", matterDetails.ClientName); Console.WriteLine("-------------------------------------------------------------------------------"); continue; } } } } catch (Exception exception) { Utility.DisplayAndLogError(errorFilePath, "Message: " + exception.Message + "\nStacktrace: " + exception.StackTrace); } }
/// <summary> /// Main method - Start of the program /// </summary> /// <param name="args"> argument as parameter </param> public static void Main(string[] args) { if (2 <= args.Length && !ExcelOperations.IsNullOrEmptyCredential(args[0], args[1])) { try { // Read login credentials from excel string filePath = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName + "\\" + ConfigurationManager.AppSettings["filename"]; string sheetName = ConfigurationManager.AppSettings["SheetName"]; Dictionary <string, string> listval = ExcelOperations.ReadFromExcel(filePath, sheetName); bool isDeployedOnAzure = Convert.ToBoolean(listval["IsDeployedOnAzure"], CultureInfo.InvariantCulture); string username = args[0].Trim(); string password = args[1].Trim(); // Read client context for Tenant URL using (ClientContext userClientContext = ConfigureSharePointContext.ConfigureClientContext(listval["CatalogSiteURL"], username, password, isDeployedOnAzure)) { // Reading SharePoint properties string groupName = ConfigurationManager.AppSettings["PracticeGroupName"]; // Get Practice Group Name string termSetName = ConfigurationManager.AppSettings["TermSetName"]; // Get Term Set Name string clientIdProperty = ConfigurationManager.AppSettings["ClientIDProperty"]; // Get Client ID string clientUrlProperty = ConfigurationManager.AppSettings["ClientUrlProperty"]; // Get Client Url string clientUrl = null; // Store Client URL string selectedField = null; // Reading client term sets ClientTermSets clientTermSets = TermStoreOperations.GetClientDetails(userClientContext, groupName, termSetName, clientIdProperty, clientUrlProperty); // Iterating over clients in term store to get client URL and update lists foreach (Client client in clientTermSets.ClientTerms) { try { clientUrl = client.ClientUrl; if (!string.IsNullOrEmpty(clientUrl)) { ClientContext clientContext = ConfigureSharePointContext.ConfigureClientContext(clientUrl, username, password, isDeployedOnAzure); List list = clientContext.Web.Lists.GetByTitle(ConfigurationManager.AppSettings["ListName"]); ViewCollection viewFields = list.Views; View targetView = viewFields.GetByTitle(ConfigurationManager.AppSettings["ViewName"]); clientContext.Load(targetView.ViewFields); clientContext.ExecuteQuery(); // Update fields to list only if title field is not present already selectedField = UpdateFields(selectedField, client, clientContext, list, targetView); } ErrorMessage.ShowMessage(client.ClientName + " site collection view updated with field", ErrorMessage.MessageType.Success); } catch (Exception exception) { ErrorMessage.ShowMessage(exception.Message + client.ClientName, ErrorMessage.MessageType.Error); } } } } catch (Exception exception) { ErrorMessage.ShowMessage(exception.Message, ErrorMessage.MessageType.Error); } } else { ErrorMessage.ShowMessage("Please enter the Username and Password", ErrorMessage.MessageType.Error); } }
/// <summary> /// Function to return client id and client url from term store /// </summary> /// <param name="clientName">Client Name</param> /// <param name="configVal">Configuration from excel file</param> /// <returns>ClientId and ClientUrl</returns> internal static ClientTermSets GetClientDetails(Dictionary<string, string> configVal) { ClientTermSets clientDetails = new ClientTermSets(); clientDetails.ClientTerms = new List<Client>(); string groupName = ConfigurationManager.AppSettings["PracticeGroupName"]; string termSetName = ConfigurationManager.AppSettings["TermSetName"]; string clientIdProperty = ConfigurationManager.AppSettings["ClientIDProperty"]; string clientUrlProperty = ConfigurationManager.AppSettings["ClientUrlProperty"]; // 1. get client context using (ClientContext clientContext = GetClientContext(configVal["TenantAdminURL"], configVal)) { if (null != clientContext) { // 2. Create taxonomy session TaxonomySession taxonomySession = TaxonomySession.GetTaxonomySession(clientContext); clientContext.Load(taxonomySession.TermStores); clientContext.ExecuteQuery(); // 3. Create term store object and load data TermStore termStore = taxonomySession.TermStores[0]; clientContext.Load( termStore, store => store.Name, store => store.Groups.Include( group => group.Name)); clientContext.ExecuteQuery(); // 4. create a term group object and load data TermGroup termGroup = termStore.Groups.Where(item => item.Name.Equals(groupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); clientContext.Load( termGroup, group => group.Name, group => group.TermSets.Include( termSet => termSet.Name, termSet => termSet.Terms.Include( term => term.Name, term => term.CustomProperties))); clientContext.ExecuteQuery(); // 5. Get required term from term from extracted term set TermCollection fillteredTerms = termGroup.TermSets.Where(item => item.Name.Equals(termSetName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault().Terms; Client client; foreach (Term term in fillteredTerms) { if (term.CustomProperties.ContainsKey(clientIdProperty) && term.CustomProperties.ContainsKey(clientUrlProperty)) { client = new Client(); client.ClientName = term.Name; client.ClientId = term.CustomProperties[clientIdProperty]; client.ClientUrl = term.CustomProperties[clientUrlProperty]; clientDetails.ClientTerms.Add(client); } } } else { clientDetails = null; } } return clientDetails; }
/// <summary> /// Method to get client terms /// </summary> /// <param name="clientDetails">client term sets</param> /// <param name="clientIdProperty">client id property</param> /// <param name="clientUrlProperty">client url property</param> /// <param name="fillteredTerms">term collection</param> private static void GetClientTerms(ClientTermSets clientDetails, string clientIdProperty, string clientUrlProperty, TermCollection fillteredTerms) { Client client; foreach (Term term in fillteredTerms) { if (term.CustomProperties.ContainsKey(clientIdProperty) && term.CustomProperties.ContainsKey(clientUrlProperty)) { client = new Client(); client.ClientName = term.Name; client.ClientId = term.CustomProperties[clientIdProperty]; client.ClientUrl = term.CustomProperties[clientUrlProperty]; clientDetails.ClientTerms.Add(client); } } }
/// <summary> /// This method will get all client term properties such as ID and URL /// </summary> /// <param name="termColl">The taxonomy client terms</param> /// <param name="termStoreDetails">The term store details which the UI has sent to the clients</param> /// <param name="clientTermSet">The ClientTermSets object to which all the client terms are added</param> /// <returns></returns> private ClientTermSets GetClientTermProperties(TermCollection termColl, TermStoreDetails termStoreDetails, ClientTermSets clientTermSet) { try { foreach (Term term in termColl) { Client tempTermPG = new Client(); tempTermPG.Name = term.Name; if (term.CustomProperties.Count > 0) { tempTermPG.Url = string.Empty; tempTermPG.Id = string.Empty; foreach (KeyValuePair <string, string> customProperty in term.CustomProperties) { if (customProperty.Key.Equals(termStoreDetails.CustomPropertyName, StringComparison.Ordinal)) { tempTermPG.Url = customProperty.Value; } if (customProperty.Key.Equals(taxonomySettings.ClientCustomPropertiesId, StringComparison.Ordinal)) { tempTermPG.Id = customProperty.Value; } } } if (!string.IsNullOrWhiteSpace(tempTermPG.Id) && !string.IsNullOrWhiteSpace(tempTermPG.Url)) { clientTermSet.ClientTerms.Add(tempTermPG); } } return(clientTermSet); } catch (Exception ex) { customLogger.LogError(ex, MethodBase.GetCurrentMethod().DeclaringType.Name, MethodBase.GetCurrentMethod().Name, logTables.SPOLogTable); throw; } }
/// <summary> /// Delete matter center home page, settings page and document details page /// </summary> /// <param name="configVal">Values in Config sheet</param> /// <param name="pageType">Page to be deleted</param> internal static void DeleteSitePages(Dictionary <string, string> configVal, MatterCenterPage pageType) { try { string login = configVal["Username"]; // Get the user name string password = configVal["Password"]; // Get the password List <string> files = new List <string>(); string tenantUrl = configVal["TenantURL"]; string tenantDashboardFileName, oldWebdashboard, catalogSiteURL, FileName = string.Empty; ClientContext siteClientContext = ConfigureSharePointContext.ConfigureClientContext(tenantUrl, login, password); switch (Convert.ToString(pageType, CultureInfo.InvariantCulture).ToUpperInvariant()) { case Constants.MatterCenterHome: { tenantDashboardFileName = tenantUrl + ConfigurationManager.AppSettings["spWebDashboardPage"]; oldWebdashboard = ConfigurationManager.AppSettings["oldWebdashboardPage"]; FileName = tenantDashboardFileName.Substring(tenantDashboardFileName.LastIndexOf('/') + 1); files.Add(oldWebdashboard); Console.WriteLine(string.Concat(Constants.DeletePageSuccessMessage, tenantUrl)); break; } case Constants.Settings: { FileName = ConfigurationManager.AppSettings["SettingsPageName"]; Console.WriteLine(string.Concat(Constants.DeletePageSuccessMessage, tenantUrl)); break; } case Constants.DocumentDetails: { catalogSiteURL = configVal["CatalogSiteURL"]; siteClientContext = ConfigureSharePointContext.ConfigureClientContext(catalogSiteURL, login, password); FileName = ConfigurationManager.AppSettings["DocumentLandingPageName"]; Console.WriteLine(string.Concat(Constants.DeletePageSuccessMessage + catalogSiteURL)); break; } } files.Add(FileName); DeletePages(siteClientContext, files); if (string.Equals(Constants.Settings, Convert.ToString(pageType, CultureInfo.InvariantCulture).ToUpperInvariant())) { string groupName = ConfigurationManager.AppSettings["PracticeGroupName"], termSetName = ConfigurationManager.AppSettings["TermSetName"], clientIdProperty = ConfigurationManager.AppSettings["ClientIDProperty"], clientUrlProperty = ConfigurationManager.AppSettings["ClientUrlProperty"]; try { ClientTermSets clients = TermStoreOperations.GetClientDetails(siteClientContext, groupName, termSetName, clientIdProperty, clientUrlProperty); foreach (Client client in clients.ClientTerms) { try { string clientUrl = client.ClientUrl; ClientContext clientContext = ConfigureSharePointContext.ConfigureClientContext(clientUrl, login, password); Console.WriteLine(string.Concat(Constants.DeletePageSuccessMessage, clientUrl)); DeletePages(clientContext, files); DeleteList(clientContext); } catch (Exception exception) { ErrorLogger.DisplayErrorMessage(string.Format(CultureInfo.InvariantCulture, Constants.ProvisioningPageExceptionMessage, exception.Message, exception.StackTrace)); } } } catch (Exception exception) { ErrorLogger.DisplayErrorMessage(string.Format(CultureInfo.InvariantCulture, Constants.ProvisioningPageExceptionMessage, exception.Message, exception.StackTrace)); } } } catch (Exception exception) { ErrorLogger.DisplayErrorMessage(string.Format(CultureInfo.InvariantCulture, Constants.ProvisioningPageExceptionMessage, exception.Message, exception.StackTrace)); } }
/// <summary> /// To create Provision matter pages /// </summary> /// <param name="configVal">values in the config sheet</param> /// <param name="urlConstantName">To get constant name of page</param> /// <param name="sourceFileTemplate">To get source file path</param> /// <param name="pageUrlName">To get page Url name</param> /// <param name="pageType">Page to be deleted</param> internal static void CreateProvisionPages(Dictionary <string, string> configVal, string urlConstantName, string sourceFileTemplate, string pageUrlName, MatterCenterPage pageType) { try { string login = configVal["Username"]; // Get the user name string password = configVal["Password"]; // Get the password string tenantUrl = configVal["TenantURL"]; List <string> files = new List <string>(); string FileName = string.Empty; string pageFileName = string.Empty; string pageContent = string.Empty; string catalogSiteURL = configVal["CatalogSiteURL"]; // Get the URL for catalog site collection string catalogSiteConstant = ConfigurationManager.AppSettings["catalogSiteConstant"]; string tenantSettingsFileName = string.Empty; string destinationFileName = string.Empty; ClientContext ClientContext = ConfigureSharePointContext.ConfigureClientContext(tenantUrl, login, password); string pageUrlConstant = urlConstantName; string sourceFileTemplatePath = string.Concat(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName, Constants.Backslash, ConfigurationManager.AppSettings["staticContentFolder"], Constants.Backslash, ConfigurationManager.AppSettings["htmlFolder"], Constants.Backslash, sourceFileTemplate); string pageUrl = configVal["UISiteURL"]; // Read the content of helper file pageContent = System.IO.File.ReadAllText(sourceFileTemplatePath); // Set the Catalog site collection URL in the content of webdashboard helper page pageContent = pageContent.Replace(catalogSiteConstant, new Uri(catalogSiteURL).AbsolutePath); FileName = string.Concat(tenantUrl, ConfigurationManager.AppSettings["spWebDashboardPage"]); bool value = false; // App web part content switch (Convert.ToString(pageType, CultureInfo.InvariantCulture).ToUpperInvariant()) { case Constants.MatterCenterHome: { pageContent = pageContent.Trim(); pageContent = pageContent.Replace(pageUrlConstant, pageUrl); pageFileName = FileName.Substring(FileName.LastIndexOf('/') + 1); Console.WriteLine(string.Concat(Constants.DeletePageMessage, tenantUrl)); destinationFileName = FileName; break; } case Constants.Settings: { pageContent = pageContent.Replace(pageUrlConstant, pageUrl); tenantSettingsFileName = string.Concat(tenantUrl, ConfigurationManager.AppSettings["spSettingsPage"]); pageFileName = ConfigurationManager.AppSettings["SettingsPageName"]; Console.WriteLine(string.Concat(Constants.DeletePageMessage, tenantUrl)); destinationFileName = tenantSettingsFileName; value = true; break; } case Constants.DocumentDetails: { pageFileName = ConfigurationManager.AppSettings["DocumentLandingPageName"]; ClientContext = ConfigureSharePointContext.ConfigureClientContext(catalogSiteURL, login, password); FileName = string.Concat(catalogSiteURL, ConfigurationManager.AppSettings["spDocumentLanding"]); Console.WriteLine(string.Concat(Constants.DeletePageSuccessMessage, catalogSiteURL)); destinationFileName = pageFileName; break; } } files.Add(pageFileName); DeletePages(ClientContext, files); Folder DestinationFolder = CreateHelperPage(ClientContext, FileName); CreateMatterCenterPage(ClientContext, DestinationFolder, destinationFileName, pageContent, value); if (string.Equals(Constants.Settings, Convert.ToString(pageType, CultureInfo.InvariantCulture).ToUpperInvariant())) { string groupName = ConfigurationManager.AppSettings["PracticeGroupName"], termSetName = ConfigurationManager.AppSettings["TermSetName"], clientIdProperty = ConfigurationManager.AppSettings["ClientIDProperty"], clientUrlProperty = ConfigurationManager.AppSettings["ClientUrlProperty"]; ClientTermSets clients = TermStoreOperations.GetClientDetails(ClientContext, groupName, termSetName, clientIdProperty, clientUrlProperty); // Read client details from term store // As Client level web dashboard will not be created, commenting code for provisioning client web dashboard page foreach (Client client in clients.ClientTerms) { try { string clientUrl = client.ClientUrl; // string fileName = clientUrl + ConfigurationManager.AppSettings["spWebDashboardPage"]; string settingsFileName = string.Concat(clientUrl, ConfigurationManager.AppSettings["spSettingsPage"]); ClientContext clientContext = ConfigureSharePointContext.ConfigureClientContext(clientUrl, login, password); // Deleting client web dashboard pages Console.WriteLine(string.Concat(Constants.DeletePageMessage, clientUrl)); DeletePages(clientContext, files); // Create helper file for site collections // Folder destinationFolder = CreateHelperPage(clientContext, fileName); Folder destinationFolder = CreateHelperPage(clientContext, settingsFileName); // Upload web dashboard page // CreateMatterCenterPage(clientContext, destinationFolder, fileName, helperFileContent, false); // Upload settings page CreateMatterCenterPage(clientContext, destinationFolder, settingsFileName, pageContent, true); // Create list CreateList(clientContext); } catch (Exception exception) { ErrorLogger.DisplayErrorMessage(string.Format(CultureInfo.InvariantCulture, Constants.ProvisioningPageExceptionMessage, exception.Message, exception.StackTrace)); } } } } catch (Exception exception) { ErrorLogger.DisplayErrorMessage(string.Format(CultureInfo.InvariantCulture, Constants.ProvisioningPageExceptionMessage, exception.Message, exception.StackTrace)); } }