Recent Content
Dashboard: XFBR String
Business rules written and used as Parameters that return a specific value based on the defined inputs. This Business Rule can be applied to any Dashboard or Cube View property where a Parameter is used. Imports System Imports System.Data Imports System.Data.Common Imports System.IO Imports System.Collections.Generic Imports System.Globalization Imports System.Linq Imports Microsoft.VisualBasic Imports System.Windows.Forms Imports OneStream.Shared.Common Imports OneStream.Shared.Wcf Imports OneStream.Shared.Engine Imports OneStream.Shared.Database Imports OneStream.Stage.Engine Imports OneStream.Stage.Database Imports OneStream.Finance.Engine Imports OneStream.Finance.Database Namespace OneStream.BusinessRule.DashboardStringFunction.XFBRStringExamples Public Class MainClass Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As DashboardStringFunctionArgs) As Object '------------------------------------------------------------------------------------------------------------ 'Reference Code: GetCalcStatus ' 'Usage: This shows different examples of BRString usage in Cube Views and Dashboards ' 'GetCalcStatus ' Parameter Example: ' BRString(XFBRStringExamples, GetCalcStatus, ParamCube = GolfStream, ' ParamEntity = [Houston Heights], ParamParent = Houston, ' ParamCons = USD, ParamScenario = Actual, ParamPeriod = 2011M1) ' 'GetUserIsAdmin ' Parameter Example: BRString(XFBRStringExamples, GetUserIsAdmin) ' 'EntityDesc ' Parameter Example: BRString(XFBRStringExamples, EntityDesc, ParamEntity=[|!MyEntity!|]) ' 'Created By: OneStream 'Date Created: 12-18-2017 '------------------------------------------------------------------------------------------------------------ Try Select Case args.FunctionName Case Is = "GetCalcStatus" 'Get the Passed in parameters Dim paramCubeValue As String = args.NameValuePairs("ParamCube") Dim paramEntityValue As String = args.NameValuePairs("ParamEntity") Dim paramParentValue As String = args.NameValuePairs("ParamParent") Dim paramConsValue As String = args.NameValuePairs("ParamCons") Dim paramScenarioValue As String = args.NameValuePairs("ParamScenario") Dim paramPeriodValue As String = args.NameValuePairs("ParamPeriod") Return BRApi.Finance.Data.GetCalcStatus(si, _ paramCubeValue, paramEntityValue, paramParentValue, _ paramConsValue, paramScenarioValue, paramPeriodValue) Case Is = "GetUserIsAdmin" 'Check to see if the user is an administrator ' (Can be used to hide objects that are administrator only) Return BRApi.Security.Authorization.IsUserInAdminGroup(si) Case Is = "EntityDesc" 'Get the Passed in parameter Dim myEntity As String = args.NameValuePairs("ParamEntity") Dim myEntityDesc As String = BRApi.Finance.Metadata.GetMember( _ si, 0, myEntity).Member.Description Return myEntityDesc End Select Return Nothing Catch ex As Exception Throw ErrorHandler.LogWrite(si, New XFException(si, ex)) End Try End Function End Class End Namespace4.2KViews2likes0CommentsDashboard: Get or Set Literal Parameter
Literal Parameters are, effectively, application-wide variables that can be used to drive Dashboards and Cube Views. Their values can be set or retrieved in code as shown below. From version 7.3 onwards, they are contained in a Workspace. That means that the relevant calls now require the ID of the Workspace where the Parameter is located. Note that the ID of Default Workspace is an empty Guid. Note: Literal Parameters are effectively shared between all users; which means that, if a Dashboard Action triggered by "User1" modifies a Literal Parameter value, "User2" will also receive the changed value in any Dashboards they are using (at the first refresh). OneStream Version 7.3+ Dim parameterName As String = "MyParameterName" ' if Parameter lives in Dashboards under "System" tab, set this to True Dim isSystemLevel as Boolean = False ' retrieve workspace ID. If it's Default Workspace, you can just use Guid.Empty instead Dim workspaceID As Guid = BRApi.Dashboards.Workspaces.GetWorkspaceIDFromName( _ si, isSystemLevel, workspaceName) ' --- Set literal parameter value Dim newValue As String = "New Literal Value" BRApi.Dashboards.Parameters.SetLiteralParameterValue( _ si, isSystemLevel, workspaceID, parameterName, newValue) ' --- Retrieve literal parameter value Dim pValue as String = BRApi.Dashboards.Parameters.GetLiteralParameterValue( _ si, isSystemLevel, workspaceID, parameterName) OneStream Version 7.2 and below Dim parameterName As String = "MyParameterName" ' if Parameter lives in Dashboards under "System" tab, set this to True Dim isSystemLevel as Boolean = False ' --- Set literal parameter value Dim newValue As String = "New Literal Value" BRApi.Dashboards.Parameters.SetLiteralParameterValue( _ si, isSystemLevel, parameterName, newValue) ' --- Retrieve literal parameter value Dim pValue as String = BRApi.Dashboards.Parameters.GetLiteralParameterValue( _ si, isSystemLevel, parameterName)3.1KViews2likes0CommentsCan you give an example of the best way to sum a row from a cube view? For example, if I add two rows to a cube view, one with a selection of members, how do I make the second row a custom sum of the first? I know one way to do it, but I don’t love it, I would love to see what else you guys recommend.
Clarification – this is referring to using Cube View Row/Column math when members expansions are in use. Answer No “elegant” ways to address this currently – options are: Avoid using member filters (i.e., break the source rows down to more detail) Replicate the member filter in a GetDataCell function on the total row Create a CV Helper function to return the same thing Enhancement Status: There have been numerous enhancement requests to allow CV Row/Column math to work against member expansions, as well as suppressed rows/columns. These have all been approved by Development and should be in one of the upcoming quarterly releases in 2020. Source: Office Hours 2020-03-26 Partner Enablement2.9KViews1like1CommentQuestion: Is there a way to report the total of a group of members based on a text attribute with multiple dimensions in a row?
Question (cont) The example below is for an operating expense that I want to be filtered using a text property of “HP” (a specific business line). I thought the following would work, but it is still pulling in the total company business line, not just a total of the “HP” UD3 members. A#Marketing:U1#TotalBU:U2#Top:U3#CustomerGroup_0002.Base.Where(Text1 contains HP):Name(Market) I don’t need an expansion of the customers (UD3) but a total of the relevant members. Answer Using CVR Math in a cubeview row will not work in this situation. CVR Math Example A Dashboard XFBR String will be needed to aggregate the members with the Text property to produce a total. XFBR Example Source: Office Hours 2021-02-04 - Partner Enablement2.7KViews4likes0CommentsOneStream 9.0 Stage Schema Changes
Introduction As part of our ongoing commitment to improving functionality and performance, the 9.0 OneStream release includes significant changes to the database schema related to Stage operations. These changes are designed to enhance data management, streamline processes, and provide increased flexibility for future developments. Overview of Changes The following tables have been replaced by views: StageSourceData StageSummaryTargetData StageTargetData The following tables have been added, to increase normalization of Stage-related data structures: StageDataName StageSourceDataSlim StageSummaryTargetDataSlim StageTargetDataSlim The following views have been added: vStageSourceAndAttributeData vStageSourceAndAttributeDataSlim vStageSourceAndTargetDataR vStageSourceAndTargetDataSlim vStageSourceAndTargetDataWithAttributesR vStageSourceAndTargetDataWithAttributesSlim vStageSourceData vStageTargetData vStageTargetDataR The StageRules table now has an additional RuleIdentityId column, which can be leveraged in joins with the new “Slim” tables. StageRulesHistory and AuditStageRules also contain the new column. Indexing and partitioning strategies on Stage* and Taskflow* tables have been streamlined and optimized. See the Appendix for details of modified indexes. Impact on Existing Applications The following advice applies only to custom queries that developers, partners, or administrators might implement in their applications. Internal operations (e.g. standard Import/Validate/Load workflows) are unaffected. The changes to the database schema in OneStream 9.0 might require small modifications to existing applications. Although we tried to maintain compatibility as much as possible, it is suggested to review and update SQL queries and application logic that interact with tables StageSourceData, StageSummaryTargetData, and StageTargetData. In most cases no change should be necessary, as SQL statements will typically retrieve data from a named object regardless of whether it is a table or a view, and the old table names are now aliases for views. However, if the query or api is explicitly looking for one of the removed tables (rather than a table or view), it will be necessary to change it so that the query can accept a view as well as a table. Note that performance of such queries might differ from previous releases. Impact on New Development The following advice applies only to custom queries that developers, partners, or administrators might implement in their applications. Internal operations (e.g. standard Import/Validate/Load workflows) are unaffected. Member names are now stored only once, in the StageDataName table, and referred to from “Slim” tables and views via IDs. In future development, in a few cases it might be more performant to move from operating on the old Stage tables to working with the new “Slim” counterparts, joining on StageDataName only where necessary. Note that these members are not necessarily related to Metadata members; they represent any type of item name involved in stage loads (source members, etc.) These JOINs should be done sparingly; in most cases, leveraging views will likely be preferable, since they already do that work for you (returning actual member names as strings) and allow for better caching. Unless there is a clear and significant performance difference, queries should be performed on views. In these new “Slim” tables, the string and uniqueidentifier columns have been replaced with integer types that reference the “NameId” column in the StageDataName table. When querying “Slim” tables, the integer values in columns are used to join with the “NameId” table of the StageDataName table to retrieve the actual names. As an example, if we wanted to obtain the text value of the “CnId” column in the StageSourceDataSlim table, we would now need to perform a SQL join to the StageDataName on the NameId column. SELECT TOP (100) Slim.Wfk, Slim.Wsk, Slim.Wtk, Slim.CnId, Sdn.[Name] FROM [dbo].[StageSourceDataSlim] AS Slim LEFT OUTER JOIN [dbo].[StageDataName] AS Sdn ON Sdn.NameId = Slim.CnId IMPORTANT! The collation of the “Name” column in the StageDataName table has been set to “Latin1_General_100_BIN2”. The “Latin1_General_100_BIN2” collation allows us to compare string values properly between Unicode and non-unicode values. If you need to perform a join to the StageDataName table on the “Name” column, you will need to force the source column in the join to use this collation. An example of this is below where a string column using the default database collation is joined with the [Name] column of the StageDataName table. The “COLLATE” clause is added to the join to force a binary comparison of the two column values. SELECT Src.Et, Sdn.NameId FROM [dbo].[StageSourceDataSlim] AS Src LEFT OUTER JOIN [dbo].[StageDataName] AS Sdn ON Sdn.Name = Src.Et COLLATE Latin1_General_100_BIN2 Failure to add this collate clause when joining on the [Name] column of the StageDataName table will result in an error like the one below. Msg 468, Level 16, State 9, Line 4 Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_100_BIN2" in the equal to operation. There is no requirement to use COLLATE when operating on the prebuilt views. Conclusion The database schema changes in OneStream 9.0 represent a significant step forward in our data management capabilities. By optimizing table structures and improving indexing, we are laying the groundwork for more robust and scalable applications. We appreciate your cooperation during this transition and are confident that these improvements will provide substantial benefits to our operations. Appendix Modified Indexes The following changes to indexes have been implemented: Table Name Index Name Comment Member IX_Member1 New nonclustered index on Name, DimTypeId, DimId StageArchivesInformation IX_StageArchivesInformation0 Index is now a unique clustered index. The “UniqueID” column has been added to this index. StageAttributeData IX_StageAttributeData0 This index has been removed. StateBiBlendInformation IX_StageBiBlendInformation0 New unique nonclustered index added using the column “BlendTableName” in ascending order. StageRuleGroupsHistory IX_StageRuleGroupsHistory0 Index has been removed. StageRuleProfileMembersHistory IX_StargeRuleProfileMembersHistory0 Index has been removed. StageRuleProfilesHistory IX_ StageRuleProfilesHistory0 Index has been removed. StageRules IX_StageRules1 New index on the “RuleIdentityId” column in ascending order was created. StageRulesHistory IX_StageRulesHistory0 Column “RuleIdentityId” added to index. Index changed to a unique nonclustered index. StageToFinanceDataUnit IX_StageToFinanceDataUnit0 Index is now nonclustered. “WorkflowProfileKey” column has been removed. “FillFactor” has been removed. StageToFinanceDataUnit IX_StageToFinanceDataUnit1 The “WorkflowProfileKey”, “WorkflowScenarioKey” and “WorkflowTimeKey” have been replaced with the “ScenarioId”, “TimeId”, “CubeId” and “EntityId” columns. StageToFinanceDataUnit IX_StageToFinanceDataUnit2 Index has been removed. StageToFinanceLoadResult IX_StageToFinanceLoadResult0 Index is now a unique clustered index. The “SummaryRowID” column has been added and the “FillFactor” has been removed. StageToFinanceLoadResult IX_StageToFinanceLoadResult1 The “WorkflowProfileKey” column has been removed. StageToFinanceValidationError IX_StageToFinanceValidationError0 Now a unique clustered index with “FillFactor” removed. StageToFinanceValidationError IX_StageToFinanceValidationError1 Index has been removed. TaskflowStepInstances IX_TaskflowStepInstances0 “FillFactor” increased from 80 to 95. TaskflowStepLog IX_TaskflowStepLog0 Now a unique clustered index. “UniqueID” column has been added. “FillFactor” increased from 80 to 95.2.6KViews10likes3CommentsExtender: Auto Create Member
This snippet will create a new Account member, including setting some properties that can vary by Scenario Type and/or Time. Note: SaveMemberInfo does not create entries in Audit tables, which means the Audit Metadata report will not contain anything related to this operation. For this reason, we do not recommend to use this snippet outside of implementation activities or in production environments. 'Create a new MemberInfo object with its child objects. Dim objMemberPk As New MemberPk(DimType.Account.Id, DimConstants.Unknown) 'Update Dim Name accordingly Dim objDim As OneStream.Shared.Wcf.Dim = BRApi.Finance.Dim.GetDim(si, "<Dimension Name>") 'Create New Member Dim objMember As New Member(objMemberPk, _ "<New Member Name>", "<Member Description>", objDim.DimPk.DimId) 'Create VaryingMemberProperties object Dim objProperties As New VaryingMemberProperties( _ objMemberPk.DimTypeId, objMemberPk.MemberId, DimConstants.Unknown) 'Create new member info object for new member Dim objMemberInfo As New MemberInfo( _ objMember, objProperties, Nothing, objDim, DimConstants.Unknown) 'Modify some member properties. Account dimension, in this example. Dim accountProperties As AccountVMProperties = objMemberInfo.GetAccountProperties() accountProperties.AccountType.SetStoredValue(AccountType.Revenue.Id) accountProperties.Text1.SetStoredValue( _ ScenarioType.Unknown.Id, DimConstants.Unknown, "MyNewText1Value") 'Save the member and its properties. Dim isNew As TriStateBool = TriStateBool.TrueValue BRApi.Finance.MemberAdmin.SaveMemberInfo(si, objMemberInfo, True, True, False, isNew)Finance Dynamic Calc: Annotations Consolidations
This DynamicCalc formula consolidates Annotations for entities in a hierarchy. 'Cube View definition 'Row Definition: E#[Total GolfStream].tree 'Col1 Definition: V#Annotation:Name("Comment") 'Col2 Definition: V#Annotation:UD8#DynamicTextCons:Name("Consolidated") 'UD8 Member setup 'Name: DynamicTextCons <-- If this is changed it must be updated in the CV Col2 definition above. 'Formula Type: DynamicCalc 'Allow Input: True 'Is Consolidated: False 'In Use: True 'Assign the formula below to the UD8 member to show the consolidated text Dim iEntityID As Integer = api.pov.Entity.MemberId Dim iETestID As Integer Dim strETestName As String = string.empty Dim eTest As Member Dim sSave As String = string.empty Dim sSource As String = string.empty If api.View.IsAnnotationType Then If api.Entity.HasChildren() Then For Each etest In api.Members.GetDescendents( _ api.Dimensions.GetBaseDim(dimtypeid.Entity).DimPk, iEntityID) iETestID = etest.MemberId sSource = api.Data.GetDataCellEx("U8#None:E#[" & etest.Name & "]:C#" & _ api.Entity.GetLocalCurrency(iETestID).Name).DataCellAnnotation If Not sSource.Equals(String.Empty) Then sSave = sSave.Trim & "; " & eTest.Name & ": " & sSource End If Next Else sSource = api.Data.GetDataCellEx($"U8#None:E#[{api.pov.entity.Name}]").DataCellAnnotation If Not sSource.Equals(String.Empty) Then sSave = ";" & sSource End If End If End If If Not sSave.Equals(String.Empty) Then Return right(sSave, len(sSave)-1).Trim Else Return Nothing End If Source: Snippet Editor: Finance - Common Account - DynamicCalcExtender: User Inactivity Email
This Extender can be executed in a Data Management step to automate emailing details of an auto-expiring account to the related user. Imports System Imports System.Data Imports System.Data.Common Imports System.IO Imports System.Collections.Generic Imports System.Globalization Imports System.Linq Imports Microsoft.VisualBasic Imports System.Threading Imports System.Windows.Forms Imports OneStream.Shared.Common Imports OneStream.Shared.Wcf Imports OneStream.Shared.Engine Imports OneStream.Shared.Database Imports OneStream.Stage.Engine Imports OneStream.Stage.Database Imports OneStream.Finance.Engine Imports OneStream.Finance.Database '** IMPORTANT! Update "InactiveEmailNotification" in line below ' with actual name of the Extender business rule you created Namespace OneStream.BusinessRule.Extender.InactiveEmailNotification Public Class MainClass Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As ExtenderArgs) As Object '--------------------------------------------------------------------------------------- 'Description: User Expiration Warning ' 'Usage: Sends an email to users prior their account auto expire date. ' This Snippet should replace an entire rule as it includes non-standard Imports [Lines 1-18] ' 'Notes: Administrator must set the following values prior to use: ' BusinessRuleName [Line 20] ' EmailConnectionName [Line 58] ' UserWarningThreshold [Line 61] ' EmailTitle [Line 64] ' EmailMessage [Line 68-75] ' EmailList [Line 78] ' 'Created By: OneStream Software 'Date Created: 09-15-2020 '--------------------------------------------------------------------------------------- Try Select Case args.FunctionType Case Is = ExtenderFunctionType.Unknown Me.EmailNotification(si) Case Is = ExtenderFunctionType.ExecuteDataMgmtBusinessRuleStep Me.EmailNotification(si) End Select Return Nothing Catch ex As Exception Throw ErrorHandler.LogWrite(si, New XFException(si, ex)) End Try End Function #Region "General Helpers" Public Sub EmailNotification(ByVal si As SessionInfo) Try 'Specify the email connection (Defined in Application Server setup) Dim emailConnectionName As String = "OneStreamEmail" 'Enter number of days prior to user expiration that warning will be sent. ' Email will only be sent if "Remaining Allowed Inactivity" is less than or equal to threshold days. Dim userWarningThreshold As Double = 20 'Define the the email title to be sent. The value [days] will be updated during processing. Dim emailTitle As String = "OneStream User ID Expiration - [days] day warning" 'Define the the email body to be sent. ' [user] & [days] will automatically be replaced, during processing, ' with the OneSteam Username & number of days till expiration. Dim emailMessage As New Text.StringBuilder emailMessage.AppendLine("Attention: [user]") emailMessage.AppendLine("") emailMessage.AppendLine("Your login for OneStream will expire in [days] due to inactivity." emailMessage.AppendLine("Please login as soon as possible.") emailMessage.AppendLine("") ' replace xxx in text below with relevant email address emailMessage.AppendLine("If you require assistance, please contact xxxx .") emailMessage.AppendLine("") emailMessage.AppendLine("Thank you.") emailMessage.AppendLine("The Support Team") 'Define any additional email addresses to include other than the user that is expiring. ' All emails will be listed in the "To: field" of the email. Dim emailList As New List(Of String) ' Uncomment following lines if necessary and update with relevant addresses. 'emailList.Add("emailaddress1@customer.com") 'emailList.Add("emailaddress2@customer.com") ' If you need more addresses, just add more lines like the ones above 'Account Expiration Warning Me.ValidateUserExpiration(si, emailConnectionName, _ userWarningThreshold, emailTitle, emailMessage.ToString, emailList) Catch ex As Exception Throw ErrorHandler.LogWrite(si, New XFException(si, ex)) End Try End Sub Public Sub ValidateUserExpiration(ByVal si As SessionInfo, ByVal emailConnectionName As String, _ ByVal userWarningThreshold As Double, ByVal emailTitle As String, ByVal emailMessage As String, ByVal emailList As List(Of String)) Try Dim dtResults As New DataTable Using dbConnApp As DbConnInfo = BRAPi.Database.CreateApplicationDbConnInfo(si) Dim ds As DataSet = BRApi.Database.ExecuteMethodCommand( _ dbConnApp, XFCommandMethodTypeID.Users, "{}", "Users", Nothing) If Not ds Is Nothing Then Dim dt As DataTable = ds.Tables(0).Copy Dim userID As Guid = Guid.Empty Dim userName As String = String.Empty Dim remainingDays As Double = 0 Dim updatedEmailList As New List(Of String) Dim updatedEmailTitle As String = String.Empty Dim updatedEmailMessage As String = String.Empty Dim objUserInfoAndStatus As UserInfoAndStatus = BRApi.Security.Admin.GetUserAndStatus( _ si, si.UserName) For Each dr As DataRow In dt.Rows 'Filter out inactive users and users without a defined email address If(dr(4).ToString().XFContainsIgnoreCase("TRUE")) And (Not String.IsNullOrEmpty(dr(7).ToString)) Then 'Get UserName and UserInfoAndStatus userName = dr(1) objUserInfoAndStatus = BRApi.Security.Admin.GetUserAndStatus(si, userName) If objUserInfoAndStatus.LogonStatus.GetNumDaysOfRemainingAllowedInactivity <= userWarningThreshold Then remainingDays = objUserInfoAndStatus.LogonStatus.GetNumDaysOfRemainingAllowedInactivity 'Reset email list for next user updatedemailList.Clear updatedemailList.AddRange(emailList) 'Add user to email list updatedemailList.Add(dr(6).ToString) 'Replace [days] & [user] values in email EmailTitle and EmailMessage updatedEmailTitle = emailTitle.Replace( _ "[days]", ConvertHelper.ToInt32(remainingDays)) updatedEmailMessage = emailMessage.Replace( _ "[days]", ConvertHelper.ToInt32(remainingDays)) updatedEmailMessage = updatedEmailMessage.Replace( _ "[user]", userName) 'Send the email using a worker background thread Dim mailThread As New SendMailThread(si, _ emailConnectionName, updatedemailList, _ updatedEmailTitle, updatedEmailMessage, Nothing) mailThread.Start End If End If Next End If End Using Catch ex As Exception Throw ErrorHandler.LogWrite(si, New XFException(si, ex)) End Try End Sub #End Region #Region "Constants and Enumerations" 'String Messages Public m_MsgNoEmailConnection As String = "Cannot Send Notifications: Email Connection must be specified." #End Region Public Class SendMailThread #Region "Module Level Variables" Private Const m_ThreadNamePrefix As String = "XF Send Mail Thread" Private m_SI As SessionInfo = Nothing Private m_MailConnectionName As String = String.Empty Private m_ToEmailAddresses As New List(Of String) Private m_Subject As String = String.Empty Private m_Body As String = String.Empty Private m_AttachmentFilePaths As New List(Of String) Private m_WorkerThread As Thread #End Region #Region "Constructor" Public Sub New(ByVal si As SessionInfo, ByVal mailConnectionName As String, _ ByVal toEmailAddresses As List(Of String), ByVal subject As String, _ ByVal body As String, ByVal attachmentFilePaths As List(Of String)) 'Copy the input parameters so the background thread can access them. m_SI = si m_MailConnectionName = mailConnectionName m_ToEmailAddresses = toEmailAddresses m_Subject = subject m_Body = body m_AttachmentFilePaths = attachmentFilePaths End Sub #End Region #Region "Public Methods" Public Sub Start() Try 'Create the Background Thread m_WorkerThread = New Thread(AddressOf Me.WorkerThreadMethod) 'We don't want this worker thread to keep the process from being shut down. m_WorkerThread.IsBackground = True 'Naming thread to provide a unique recognizable marker when debugging. m_WorkerThread.Name = m_ThreadNamePrefix & " " & Guid.NewGuid().ToString("N") XFWcfOperationInvoker.SetCultureInfoForUserToThread(m_SI, m_WorkerThread) m_WorkerThread.Start() Catch ex As Exception Throw ErrorHandler.LogWrite(m_SI, New XFException(m_SI, ex)) End Try End Sub #End Region #Region "Private Methods" Private Sub WorkerThreadMethod() Try 'Send the email BRApi.Utilities.SendMail(m_SI, m_MailConnectionName, m_ToEmailAddresses, m_Subject, m_Body, m_AttachmentFilePaths) Catch ex As Exception 'Important: do not re-throw the exception from this worker thread since it will be processed by .NET as an unhandled exception. 'Even if an exception could be processed normally, it couldn't be sent back to the client via WCF because the client isn't 'waiting for a WCF method to complete and the client might not not even be logged on anymore. Try BRApi.ErrorLog.LogError(m_SI, ex) Catch End Try End Try End Sub #End Region End Class End Class End Namespace2.4KViews2likes0Comments