Select your user interface:
Business Operations Services (BizOps)
Note: For examples that use Business Operations Services, see Business Operations Services (BizOps) API.
Note: Business Operations Services is a wrapper API for AppFxWebService targeted to all developers.
Blackbaud CRM has THOUSANDS of features and the number keeps growing. In each release of Blackbaud CRM, the number of features continues to grow. And don't forget the custom features that you can author with the Infinity SDK! And don't forget that an end user can extend the list of features by authoring a user-defined data list. Certain types of these features can be addressed through a certain type SOAP web service called a BizOp. The nice thing about BizOps is that they are strongly typed web services. If you know which Infinity feature you need access, you can add a reference to the ASMX and begin to code. They are granular, and each BizOp web service represents a single Infinity feature. If you have a small application that only needs access to a few features, you can add the necessary references to the ASMX/WSDL and begin to program. Below is a listing of features that can be addressed through a BizOp:
-
Code tables
-
Data lists
-
Record views (view data forms)
-
Record creates (add data forms)
-
Record updates (edit data forms)
-
Delete record operations
-
Record searches (search lists)
Add a Web Reference to a BizOp SOAP Endpoint
Making a reference to a business operation service is quite easy. The catch is that you have to know ahead of time which feature you want to automate. You can locate the web service for the feature in a couple ways. First, you can navigate to the Blackbaud AppFx Server HTTP Endpoint Reference web page and click the Business Operations Services link.
Next, you select the database key that directs you to the Blackbaud Infinity database that houses your Infinity features.
After you select the database key, you must select the Infinity feature type /category.
Finally, assuming you know the name of the Infinity feature, you can select. For my example, I am looking for the "Individual Search" search list. So I click the Record Searches link, and I am presented with a listing of all the Record Search web services.
Within my browser, I press CTRL+F to begin my search. I search for "Individual Search" and click the SOAP link to obtain the URL to the web service endpoint.
Within my web browser, I grab the web service URL that points me to the appropriate SOAP web service:
http://localhost/BBInfinityPROD/vpp/bizops/db[BBINFINITYPROD2.7.1633.0]/searchlists/4685952f-6964-486c-9acd-5560a8a30862/soap.asmx
Since I use Visual Studio.NET 2008, I can create a web reference and supply the URL to the soap.asmx:
As you can see, I now have the Individual Search web reference added to my project along with references to the Constituent Summary View, Phone Add, and Phone List BizOp SOAP endpoints.
A second way to find the feature and its associated SOAP endpoint is to use the Go To feature to review the metadata for an Infinity feature. API information is included with the feature metadata. You can utilize the shell to find the appropriate feature and then view its metadata, which includes the URL to the BizOp web service. For more information, see application features.
One advantage of BizOps is the lack of dependence to Infinity .NET assemblies. This is useful if you use a non-Windows development platform to access Infinity features. Here you can see the references for the BizOp project. Note the absence of any Infinity .NET assemblies.
Here is a piece of code that searches for a constituent by a lookup ID using BizOps. It utilizes a search list called "Individual Search" to find the constituent and then makes a call to the Constituent Summary view data form to retrieve summary information for the searched constituent. A façade class called Person.vb is used to ease access to the BizOp web services.
Form1 vb.net WinForm
Private Sub btnRetrieve_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRetrieve.Click btnRetrieve.Enabled = False btnAddPhone.Enabled = False Try 'New up a Person object which makes a call 'to the BizOp webservice on our behalf Dim Person As New InfinityAPIBizOpDemo.Person(formLog, credentials) Person.Search(txtLookupID.Text) constituentID = Person.ConstituentID btnAddPhone.Enabled = True Catch ex As Exception txtLog.AppendText(ex.ToString()) Finally btnRetrieve.Enabled = True End Try End Sub
Person.vb
Imports System Imports System.Net Namespace InfinityAPIBizOpDemo Public Class Person Private constitID As Guid = Guid.Empty Private credentials As ICredentials Public Sub Search(ByVal lookupId As String) logger(String.Format("Retrieve: {0}", lookupId)) 'new up a SearchCriteria object from the IndividualSearch Web Reference Dim searchCriteria As IndividualSearch.SearchCriteria = New IndividualSearch.SearchCriteria() 'Here's the search criteria searchCriteria.LOOKUPID = lookupId 'Set flags to ensure we only get individuals searchCriteria.INCLUDEINACTIVE = False searchCriteria.INCLUDEORGANIZATIONS = False searchCriteria.INCLUDEGROUPS = False searchCriteria.INCLUDEDECEASED = False 'Set this to avoid one row for each address searchCriteria.ONLYPRIMARYADDRESS = True 'New up a searchrequest and pass in the searchcriteria Dim searchRequest As IndividualSearch.SearchRequest = New IndividualSearch.SearchRequest() searchRequest.Criteria = searchCriteria 'New up a search service... preparing to make the call to the web service Dim searchService As IndividualSearch.SearchService = New IndividualSearch.SearchService() 'Pass the credentials to the search service searchService.Credentials = credentials logger("Running search...") 'Using the search request object as part of the call to the search service 'Use the search reply to catch the results/reply of the call to the web service Dim searchReply As IndividualSearch.SearchReply = searchService.Search(searchRequest) ' Make sure search was successful If (searchReply.StatusOK) Then logger("Successful") Else logger("Failed.") logger(String.Format("Code: {0}", searchReply.StatusCode)) logger(String.Format("Message: {0}", searchReply.StatusMessage)) Return End If 'Check count of rows, bail if it isn't 1. 'Retrieve constituent ID if we only have 1 row If (searchReply.Rows.Length <> 1) Then logger(String.Format("Unexpected number of rows returned: {0}", searchReply.Rows.Length)) Return Else constitID = searchReply.Rows(0).ID End If If (ConstituentID.Equals(Guid.Empty)) Then logger("No constituent ID returned.") Return End If 'Use returned ID to load "Constituent Summary Profile View Form" Dim getViewReq As ConstituentSummary.GetViewDataRequest = New ConstituentSummary.GetViewDataRequest() getViewReq.RecordID = ConstituentID.ToString() Dim viewService As ConstituentSummary.ViewRecordService = New ConstituentSummary.ViewRecordService() viewService.Credentials = credentials logger("Sending get view request.") Dim getViewReply As ConstituentSummary.GetViewDataReply = viewService.GetViewData(getViewReq) If (getViewReply.StatusOK) Then logger("Successful") Else logger("Failed.") logger(String.Format("Code: {0}", getViewReply.StatusCode)) logger(String.Format("Message: {0}", getViewReply.StatusMessage)) Return End If 'Display a few fields from the view form logger(String.Format("Address: {0}", getViewReply.ViewData.ADDRESS)) logger(String.Format("E-mail: {0}", getViewReply.ViewData.EMAILADDRESS)) logger(String.Format("Spouse: {0}", getViewReply.ViewData.RELATEDCONSTITUENT)) logger(String.Format("Primary business: {0}", getViewReply.ViewData.PRIMARYBUSINESS)) logger(String.Format("Primary education: {0}", getViewReply.ViewData.PRIMARYEDUCATION)) logger("Retrieve done.") End Sub Public ReadOnly Property ConstituentID() As Guid Get Return constitID End Get End Property End Class End Namespace
BizOps UML Sequence Diagram
Finally, here is a UML Sequence diagram that shows the various layers of the objects used and the interactions between the objects over time. Sequence diagrams can describe how a new piece of software that interacts with an Infinity application should behave. During the design phase, architects and developers can use the diagram to force out the system's object interactions, thus fleshing out overall system design. Since we added four web references to our .NET project, .NET was kind enough to create web service proxy classes. Note how the Person façade object talks to the web service proxies that call the BizOp web services that in turn make calls to the AppFxWebService.
Pros of BizOps
-
Each BizOp is an individual, specific feature.
-
Each BizOp is a strongly typed wrapper around the AppFxWebService.asmx web service.
-
BizOps enables quick programmatic access to a single Infinity feature.
-
BizOps supports non-Windows development with no dependency on .NET assemblies.
Cons of BizOps
-
Not every Infinity feature type is represented as a BizOp.
-
You need to know exactly which features you want to automate ahead of time.
-
Lack of organization into a namespace.
-
Setting individual web references to lots of individual BizOps could get messy.
The source code for this sample can be found here: InfinityAPIBizOpDemo.zip.
For more background information about Infinity Web APIs, see:
Introduction to the Infinity Web Service APIs
Or a for a more in depth discussion, see: