Get Microsoft Silverlight
Install Silverlight plugin for a richer experience...
Blog Home |  Freeware |  Publications |  Speaking |  About me

SharePoint Happenings

:

Home
January 12
Speaking at SharePoint Fest in March!

Here's another speaking update... In March I'll be presenting at SharePoint Fest in Dallas TX. Go to www.sharepointfest.com/dallas to check out the details of this significant, regional event! Here's my topic:

 

Leveraging External Data Sources in your SharePoint Designer Workflows
There are many scenarios where a business process needs to leverage an external data source to do what it needs to do. With SharePoint 2010, and the new Business Connectivity Services capabilities, performing data access against external sources is now possible. Come to this session to see how to take your SharePoint Designer workflows into this new frontier!​



January 11
Speaking at SPTechCon next month!

Hey all! This is a note that I’ll be speaking at The SharePoint Technology Conference in the beautiful Sanfrancisco California in 4 weeks! The conference starts on February 7th, and runs for 4 days. If you’ll be in attendance, send me an email and let’s meet up! Below is my session topic:
 

 

Silverlight, SharePoint, and Sandboxed Solutions
Silverlight is a powerful tool in a SharePoint Developer's tool box. Not only is it a platform for rich internet applications, but it is also a door. With SharePoint 2010 sandboxed solutions, standard .NET code is prevented from accessing the out side world but Silverlight applications are not. This makes them a compelling solution when in the cloud. This session will cover how to build a Silverlight application from scratch, and render that application in the context of a SharePoint 2010 site. Then we'll go into the client object model, and show how to gather SharePoint data from within a Silverlight application. Finally, we'll see how to consume external data from a Silverlight app while in a SharePoint 2010 sandboxed solution.

 
 



October 19
Setting up dynamic drop downs in the out of box SharePoint forms

A new feature in InfoPath 2010 is the ability to customize out of the box, auto-generated forms. Even if you choose the out of the box approach and later decide you need more functionality, you can customize those forms with InfoPath. This blog post walks you through the steps involved in customizing the out of the box forms. You'll also see how to setup dynamic drop downs in your form, where the values of the drop down are dependent upon another drop down's selection.

 

To get started, first create a SharePoint list or library, and onto that list add all the columns you need to track. You can do this through the List Settings and then Add a column option. After you have all your columns set up for your data requirements, edit the forms to suite your user interface requirements. When in the Ribbon, in the List tab, you can use the Customize List dropdown and choose Customize Form to launch InfoPath.

 

 

The example you're going to build involves a tasks list, and each task has an associated Project and Sub Project. What we want to do is change the lookup columns for these associated projects to be dynamic, so when you select a Project, the Sub Project dropdown will change its items based on what parent Project was selected. The figure below shows how the tasks form looks when it's first opened in InfoPath. Notice the Parent Project and Sub Project columns that you'll alter.

 

The Parent Project column is a lookup column to a separate list called Project Names. Sub Project is another lookup column to a second separate list called Sub Project Names. The Sub Project Names list has two columns, Title and Parent Project. Parent Project again is a lookup to the Project Names list. Before continuing, set up the two Project Names and Sub Project Names lists as previously described. Then add a Project Name lookup column and a Sub Project Name lookup column to a Tasks lists. Last, edit the out of the box form in InfoPath as shown in the figure.

 

To make the Sub Project dropdown in your tasks form dynamically, select its items based on what is selected in the Parent Project dropdown. First create a new data source for the Sub Project dropdown. The default data source for this dropdown only has two fields, Title and ID. Because you need to filter on the Parent Project column, add a new data source. Then filter that data source on the selected Parent Project. And last, set up a rule on the Parent Project dropdown to ensure the data source is updated each time the dropdown is changed. The following steps will walk you through this process:

 

STEP 1

While customizing the out of the box task edit form in InfoPath, set up a new data source for the sub project dropdown menu:

1) In the Data tab within the Ribbon in InfoPath, click the Data Connections button. Click the Add button to add a new data source.

2) Select the radio buttons to create a new data source that receives data. Click Next.

3) Choose to receive data from a SharePoint library or list. Click Next.

4) Type the URL to the SharePoint site that contains the Sub Project Names lists and then click Next and continue wizard in step 2.

 

STEP 2

Specify your list and columns to be included in the data source:

1) Select the list name that contains the data for your connection (in this case, Sub Project Names) and click Next.

2) Select the columns to include in the data source (in this case Title, Parent Project, and ID columns). Click Next twice.

3) Give the data source a name, like Sub Projects Filtered by Parent Project Selection. Click Finish to complete the data source wizard, and then Close.

4) Change the Sub Project dropdown to use this data connection instead of the default connection by right clicking on the dropdown and choosing Drop Down List Box Properties. Then change the Data Source dropdown menu to be the data connection that was created and finally click OK.

 

STEP 3

Filter the new data source on the selected parent project. In the entries box directly below the Data Source selection on the Sub Project dropdown, you'll see the XPath query that points to the data. Click the Select XPath button directly to the right of the text box (click the ellipses image). Follow these steps to set up the filter:

1) In the Select a Field or Group dialog box, click the Filter Data button and then click Add.

2) The first dialog box contains the fields in your custom data source. Select the Parent Project field. Leave the comparing field (middle dropdown) set to is equal to.

3) In the third dropdown, choose Select a field or group. Another dialog box will appear; change the Fields dropdown to Main to select the data that is represented on the form itself.

4) There are two sub folders: the queryFields folder will query fresh data, and dataFields folder contains the data on the form at the present time. Expand the dataFields folder.

5) Expand the sub folder containing the list item data. Select the Parent Project field. Click OK five times.

 

STEP 4

Now your Sub Project dropdown is correctly filtering its data based on what the Parent Project dropdown's selected value is set to. The last thing you need to do before you publish the form into our SharePoint tasks list is to set up a rule on the Parent Project dropdown. Each time the Parent Project dropdown's value is changed, you need to tell the Sub Project dropdown to update its items.

1) Right click the Parent Project dropdown, and under the Rules flyout, select Manage Rules.

2) Click the New dropdown in the Manage Rules tool bar, and select Action.

3) Give the rule a name, like Update Sub Project Dropdown.

4) Next to Run these actions click the Add dropdown and select Query for data.

5) In the Data Connection dropdown, select the custom data connection you created earlier. In this case, select Sub Projects Filtered by Parent Project Selection and then click OK.

 

That's it! Now all that's left is to publish the form into your tasks list. Under the File menu in the Info tab, click Quick Publish. Now when you go to create a new task in your tasks list, the Sub Projects dropdown will be dynamically populated based on what Parent Project is selected!

 

 

Only Positive Integers Allowed Error

If you get an error that says Only Positive Integers Allowed, you're trying to save a string into a column that is expecting a number. Most likely the Sub Project drop down's value is set to a string but because the column is a lookup, it needs to be a number instead. To fix this error, right click on the Sub Project dropdown and choose Dropdown list properties. Then underneath Entries, change the Value from d:Title to d:ID. After you republish the form, the form should start saving properly.

 

Phil

October 18
Highly Available, Scalable, and Performant SharePoint Architectures

On October 2nd I presented at the Twin Cities SharePoint Saturday event on highly available, scalable, and performant SharePoint architectures. I took a particular look at the new/updated service applications in SharePoint 2010 and their respective impact to performance and sizing. Some say the rule of thumb is to double your infrastructure – and this presentation was my attempt as discussing why some believe that:

 

 

Phil

September 20
Upgraded to SP 2010 - Branding on the fritz

Hey all! I upgraded my blog to SharePoint 2010 today to notice that my branding got whacked. All seems to work except when you click on  a post you get the "401 Unauthorized" message. I'm using CKS.EBE and can't figure out the problem. Till then, I've deactivated the CKS.EBE feature, thus showing the out-of-box look and feel.

 

My apologies!​

July 17
Unexpected error when browsing to User Profile Service Application

So I'm working on configuring the SharePoint 2010 user profile service application in partitioned mode. However, after the service application was created I was getting these weird errors in the ULS log (just a generic "unexpected error" in the user interface):

 

UserProfileServiceUserStatisticsWebPart:LoadControl failed, Exception: System.IO.FileLoadException: The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager.InitializeIlmClient(String ILMMachineName, Int32 FIMWebClientTimeOut)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager..ctor(UserProfileApplicationProxy userProfileApplicationProxy, Guid partitionID)

at Microsoft.SharePoint.Portal.WebControls.UserProfileServiceStatisticsWebPartBase.LoadControl(Object sender, EventArgs e)

 

 

UserProfileServiceAudienceStatisticsWebPart:LoadControl failed, Exception: System.IO.FileLoadException: The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager.InitializeIlmClient(String ILMMachineName, Int32 FIMWebClientTimeOut)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager..ctor(UserProfileApplicationProxy userProfileApplicationProxy, Guid partitionID)

at Microsoft.SharePoint.Portal.WebControls.UserProfileServiceStatisticsWebPartBase.LoadControl(Object sender, EventArgs e)

 

 

UserProfileServiceImportStatisticsWebPart:LoadControl failed, Exception: System.IO.FileLoadException: The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager.InitializeIlmClient(String ILMMachineName, Int32 FIMWebClientTimeOut)

at Microsoft.Office.Server.UserProfiles.UserProfileConfigManager..ctor(UserProfileApplicationProxy userProfileApplicationProxy, Guid partitionID)

at Microsoft.SharePoint.Portal.WebControls.UserProfileServiceStatisticsWebPartBase.LoadControl(Object sender, EventArgs e)

 

 

This error is because either the user profile service application's app pool or the farm service account isn't set to DBO on the profile sync database. To fix this, go find the PROFILE SYNC database in SQL, and under security, select the farm service account and change the default schema to be dbo instead of the account name:

 

 

ALSO – MAKE SURE TO DO THIS FOR THE APP POOL ACCOUNT AS WELL! They both need DBO on the database and neither is granted it by default!!!

 

After you change the schema, perform and IISRESET and attempt to browse to the service application again. It should work!

 

 

Phil

July 15
Managed Metadata service application not available for SharePoint sites

So I was setting up the managed metadata service application today and things were going fine. I created the service app, and associated the service app with my web application. However, after I added the Enterprise Keywords site column on a list to test it out, I was noticing that the keywords box was greyed out (Figured below). I couldn't specify and enterprise metadata values.

 

 

The fix is very easy. The first problem is around permissions. Even know the service app is associated with my web application, the user I'm running under didn't have access to the term store. So the first step is to grant the running user read access to the Term Store. Within Central admin, manage service applications, select the managed metadata service application and select permissions. Then grant the user (or in my case all users) read and restricted read permissions (Figure below):

 

 

You need restricted read selected to allow users to contribute terms to the Keywords term set in the System group.

 

The next problem is by default the managed metadata service application proxy won't be set as the default provider of terms. If this is not set you'll get an error that says "The site does not contain a default keywords termstore" (Figure below):

 

 

To set it as the default provider, select the service application proxy and then click properties. Then, check the top check box to tell it to be the default provider of terms for all associated web apps (figure below).

 

(you only get the red warnings if you have more than one service app set as the default)

 

Thereafter, you should be able to start adding keywords and managed metadata to your documents!

 

Phil

July 14
Claims error when publishing service applications in SharePoint 2010

So – the other day I was trying to publish service applications in SharePoint 2010 from a provider farm to a consumer farm. I did all the steps I knew possible, but was stilling getting errors in the ULS and in the browser was getting the famed "Unable to connect to the specified address. Verify the URL you entered and contact the service administrator for more details."

 

The first thing to say is go to the ULS for BETTER error, in which case I was getting:

 

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    SPSecurityTokenService.Issue() failed: System.TypeInitializationException: The type initializer for '<Module>' threw an exception. ---> System.TypeInitializationException: The type initializer for '<Module>' threw an exception. ---> <CrtImplementationDetails>.ModuleLoadException: The C++ module failed to load while attempting to initialize the default appdomain. ---> System.Runtime.InteropServices.COMException (0x800703FA): Illegal operation attempted on a registry key that has been marked for deletion. (Exception from HRESULT: 0x800703FA) at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) at <CrtImplementationDetails>.GetDefaultDomain() at <CrtImplementationDetails>.DoCallBackInDefaultDomain(IntPtr function, Void* cookie) ...    

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    ... at <CrtImplementationDetails>.LanguageSupport._Initialize(LanguageSupport* ) at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* ) --- End of inner exception stack trace --- at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* ) at .cctor() --- End of inner exception stack trace --- at <CrtImplementationDetails>.ThrowModuleLoadException(String , Exception ) at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* ) at .cctor() --- End of inner exception stack trace --- at System.Runtime.CompilerServices.RuntimeHelpers._RunClassConstructor(IntPtr type) at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) ...    

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    ... at Microsoft.SharePoint.Administration.SPAutoSerializingObject.GetInstanceFromType(Type type, String typename) at Microsoft.SharePoint.Administration.SPPersistedObject.GetInstance(XmlNode xml, Guid classId, Boolean bResolveMissingTypes) at Microsoft.SharePoint.Administration.SPFileSystemCache.FetchObjectFromFileSystem(Guid id) at Microsoft.SharePoint.Administration.SPFileSystemCache.GetValue(Guid id) at Microsoft.SharePoint.Administration.SPCache`2.get_Item(K key) at Microsoft.SharePoint.Administration.SPConfigurationDatabase.GetObject(Guid id, Boolean checkInMemoryCache, Boolean checkFileSystemCache) at Microsoft.SharePoint.Administration.SPConfigurationDatabase.Microsoft.SharePoint.Administration.ISPPersistedStoreProvider.GetObject(Guid id) at Microsoft.Sha...    

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    ...rePoint.Administration.SPPersistedObjectCollection`1.get_Item(Guid objId) at Microsoft.SharePoint.Administration.SPPersistedObjectCollection`1.<GetEnumeratorImpl>d__0.MoveNext() at Microsoft.SharePoint.Administration.SPPersistedObjectCollection`1.Enumerator`1.MoveNext() at Microsoft.SharePoint.Administration.SPWebApplication.LookupContextWebApplication() at Microsoft.SharePoint.Administration.SPWebApplication.Lookup(SPFarm farm, Uri requestUri, Boolean fallbackToHttpContext, SPAlternateUrl& alternateUrl, SPSiteLookupInfo& hostHeaderSiteInfo, Boolean& lookupRequiredContext) at Microsoft.SharePoint.Administration.Claims.SPClaimProviderManager.GetWebApplicationAndZoneForContext(Uri context, SPWebApplication& webApplication, Nullable`1& zone) at Microsoft.SharePoint.Adm...    

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    ...inistration.Claims.SPClaimProviderManager.GetClaimProvidersForContext(Uri context, SPClaimProviderOperationOptions mode, IEnumerable`1 providerNames) at Microsoft.SharePoint.Administration.Claims.SPClaimProviderOperations.ClaimsForEntity(Uri context, SPClaimProviderOperationOptions mode, String[] providerNames, SPClaim entity) at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.AugmentClaimsIdentity(IClaimsIdentity identity, SPClaim identityClaim, RequestSecurityToken request) at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope) at Microsoft.IdentityModel.SecurityTokenService.SecurityTokenService.Issue(IClaimsPrincipal principal, RequestSecurityToken request) ...    

SharePoint Foundation     Claims Authentication     fo1t    Monitorable    ...at Microsoft.SharePoint.IdentityModel.SPSecurityTokenService.Issue(IClaimsPrincipal principal, RequestSecurityToken request)    

SharePoint Foundation     Claims Authentication     fsq7    High     Request for security token failed with exception: System.ServiceModel.FaultException: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs. at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.ReadResponse(Message response) at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst, RequestSecurityTokenResponse& rstr) at Microsoft.IdentityModel.Protocols.WSTrust.WSTr...    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

SharePoint Foundation     Claims Authentication     fsq7    High     ...ustChannel.Issue(RequestSecurityToken rst) at Microsoft.SharePoint.SPSecurityContext.SecurityTokenForContext(Uri context, Boolean bearerToken, SecurityToken onBehalfOf, SecurityToken actAs, SecurityToken delegateTo)    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

SharePoint Foundation     Claims Authentication     8306    Critical    An exception occurred when trying to issue security token: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs..    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

SharePoint Foundation     Topology     84cx    High     ServiceApplicationConnect.aspx: Unrecognized url. Exception: System.ServiceModel.FaultException: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs. at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.ReadResponse(Message response) at Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel.Issue(RequestSecurityToken rst, RequestSecurityTokenResponse& rstr) at Microsoft.IdentityModel.Protocols.W...    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

SharePoint Foundation     Topology     84cx    High     ...STrust.WSTrustChannel.Issue(RequestSecurityToken rst) at Microsoft.SharePoint.SPSecurityContext.SecurityTokenForContext(Uri context, Boolean bearerToken, SecurityToken onBehalfOf, SecurityToken actAs, SecurityToken delegateTo) at Microsoft.SharePoint.SPSecurityContext.<>c__DisplayClass7.<GetProcessSecurityTokenForServiceContext>b__6() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at Microsoft.SharePoint.SPSecurityContext.GetProcessSecurityTokenForServiceContext() at Microsoft.SharePoint.SPSecurityContext.SecurityTokenForServiceContext(Uri contextUri) at Microsoft.SharePoint.SPChannelFactoryOperations.InternalCreateChannelActingAsLoggedOnUser[TChannel](ChannelFactory`1 factory, EndpointAddress address, Uri via) at Mi...    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

SharePoint Foundation     Topology     84cx    High     ...crosoft.SharePoint.SPChannelFactoryOperations.CreateChannelActingAsLoggedOnUser[TChannel](ChannelFactory`1 factory, EndpointAddress address) at Microsoft.SharePoint.SPTopologyWebServiceApplicationProxy.EnumerateSharedServiceApplications(Uri endpointAddress, SPServiceLoadBalancerContext loadBalancerContext) at Microsoft.SharePoint.Administration.SPDiscoveryUtility.RetrieveSharedServiceApplicationInfo(String url) at Microsoft.SharePoint.ApplicationPages.ServiceApplicationConnectPage.BtnOK_Click(Object sender, EventArgs e)    8a7440ac-a08c-46da-87ad-5c2d79e19dd4

 

The weird thing is all the "Claims Authentication" error. What the? My Central admin servers were using NTLM, not claims. However, the topology service app (load balancing SA) is using claims so obviously it was a problem.

 

Here's some more background:

 

I have two farms (let's just say farm A, and farm B). I'm trying to get farm A to consume service applications published by farm B. I've exchanged root certificates and the sts certificates to setup the trust, and I've granted farm A full control on B's load balancer service app, as well as the service apps I'm trying to connect to. However, when I paste the service app's URL and click ok, I get the error that says to confirm that I have the correct URL. In 14/Logs, I get the above exception, which appears to be a COM exception.

   

Farm A is a two server farm with CA running on 01. Farm B is a single server farm.

   

The firewall is turn on between the two farms, three servers. TCP ports 12345 (central admin) and 32844 (topology service) have been opened between all three servers. I'm running as a farm and box admin.

   

Error message in UI:

 

   

Farm A's farm ID:

 

   

Perms on load balancing service app in farm B:

 

   

Perms on published service app:

 

(FYI – this isn't necessary but I did it to be safe/thorough)

   

   

Service app has been published over HTTP:

 

   

Trust on Farm A:

 

   

Trust on Farm B:

 

 

You'd think that after all this it would work just fine? The funny this is I did do all the publishing just fine, I just missed one small, important step. IISRESET.

 

Perform an IISRESET on all boxes in the provider farm, and all boxes in the consumer farm. Thereafter, try the connection again and it should work. I also created a key in the secure store service application, but I don't think this mattered. To be safe, create a secure store service app in the publishing farm, and click generate new key.

 

 

HURRAY!

 

Still having trouble – check these things for troubleshooting:

 

Troubleshooting Federation Issues:

  • Ensure domain trust (2-way for profile, 1-way for others)
  • Ensure consuming farm's service account has permissions to the topology service app
  • Try browsing to the topology service app http://*/topology.svc
  • Check the ACL on the publishing service app
  • Try using FQDNs for ALL URLs
  • Double check certs.

 

Cheers,

Phil

   

June 24
Pluggable Workflow Services in SharePoint 2010

Pluggable workflow services is one of the most highly anticipated new workflow features for SharePoint 2010. This is because SharePoint 2007 workflows lacked the ability to communicate with the outside world. The most basic scenario is a workflow that needs to go idle and wait for a message from separate system, like a line of business application such as CRM or something. Another desired technique was for inter workflow communication, where one workflow needs to send a message to another workflow. A third scenario would be a long running process. If you had a calculation or web service call that took thirty minutes to execute, there's no sense in keeping the workflow instance in memory. It would be better to hydrate the instance, and dehydrate it when the process is finish.

 

All three of those examples were not easily accomplished in SharePoint 2007. Now, Windows Workflow Foundation on the .NET 3.5 framework has always has the ability to meet these needs via Workflow Communication Services. Since SharePoint 2007 is on the 3.5 framework, you'd think it wouldn't have been a problem. Since SharePoint was the hosting provider there was no class that was provide to easily get at workflow instances and raise events into those instances that the workflow was listening form. This changes in SharePoint 2010 with the introduction of a new class, SPWorkflowExternalDataExchangeService.

 

Just as in Workflow Communication Services, in SharePoint 2010 workflows you can create a Local Service that your workflows can use to communication with each other. By using the CallExternalMethod activity and the HandleExternalEvent activity, SharePoint workflows and .NET applications can send and receive messages between one another (Figure 1).

 

Figure 1 Windows communication services is now friendly with SharePoint workflows. By using the SharePoint workflow external data exchange service, your workflows can send and receive messages from other workflows or .NET applications.

 

Before we get into how to setup a Local Service that uses the SharePoint external data exchange services, let's talk briefly about the example. The example we're going to build is going to be a glorified Hello World example. A workflow is going to say "Hello Event Handler!" to an event receiver on an announcements list by creating a new announcement. Then, an event receiver is going to say "Hello Workflow!" back to the workflow. To accomplish this, the workflow will call into a Local Service. The Local Service then creates an announcement in an announcements list. Thereafter, an event receiver responds to the new announcement by raising an event via the Local Service that the workflow is listening for. This example will serve to demonstrate how a SharePoint workflow can communicate with a .NET application. Follow these steps to build the Hello World pluggable workflow service:

 

1) Create a new Visual Studio 2010 project:

 

    A) Open Visual Studio 2010 and create a new sequential workflow project titled
    PluggableWorkflowServices and click OK.

 

    B) Type the URL of the site you'll use to debug, click Next, select a Site Workflow, and click Finish.

 

A Local Service is basically composed of two components, a service interface and a service class. The service interface lets the sending and receiving parties know what type of data to send to each other. This is done by declaring a method the sender calls and an event the receiver listens for. To tap into the external data exchange services, the interface must be declared with an ExternalDataExchance attribute.

 

2) Create a new class to use for our Local Service and add our interface:

 

    A) Create a new class titled HelloWorldService.cs.

 

    B) Add the following using statements to the class file:

        using System.Workflow.Activities;

        using Microsoft.SharePoint;

        using Microsoft.SharePoint.Workflow;

        using System.Workflow.Runtime;

 

    C) Above the HelloWorldService class, add the following interface:

        [ExternalDataExchange]

        public interface IHelloWorldService

        {

            event EventHandler<HelloWorldEventArgs>

            HelloWorkflow;

            void HelloHost(string message);

        }

 

How this works: the HelloHost method is called by the workflow via the CallExternalMethod activity, in our example, which then creates the announcement. The event receiver then executes and invokes the HelloWorkflow event which the workflow is listening for via the HandleExternalEvent activity.

 

3) Extend the HelloWorldService class, and add the HelloHost method and the event defined in the interface:

 

A) Make the HelloWorldService class extend Microsoft.SharePoint.Workflow.SPWorkflowExternalDataExchangeService.

      

    B) Make the HelloWorldService class implement the IHelloWorldService interface.

      

    C) Add the following code into the HelloWorldService class.

 

    public event EventHandler<HelloWorldEventArgs> HelloWorkflow;

    public void HelloHost(string message)

    {

        SPWeb web = this.CurrentWorkflow.ParentWeb;

        SPList list = web.Lists["Announcements"];

        SPListItem item = list.Items.Add();

        item["Title"] = message;

        item["Instance"] = WorkflowEnvironment.WorkflowInstanceId.ToString();

        item.Update();

    }

 

In the HelloHost method we want to do two things, create the event handler and the method that is defined in the interface. Within the method we're creating the new announcement and passing the workflow's instance ID into the "Instance" column within the announcement. This is how the event receiver will know which workflow to send a message to.

 

After you added the code as well as the code found in 2C, you may have noticed that the compiler cannot find the class for HelloWorldEventArgs. This class we have yet to define, but it will allow our event receiver to send a custom message to our workflow:

 

4) Add the following code below the HelloWorldService class:


    [Serializable]

    public class HelloWorldEventArgs : ExternalDataEventArgs

    {

        public HelloWorldEventArgs(Guid id) : base(id) { }

        public string Answer;

    }

 

Notice how our custom arguments take essentially two values, a GUID that will store the workflow instance ID, and the Answer, which is the message the event receiver will pass to the workflow. This message will eventually get logged into the workflow's History List.

 

There's one more thing we must do before our Local Service is complete and we can move on to build the workflow and the event receiver. There are three more methods we need to add to satisfy an interface within SPExternalDataExchangeService.

 

5) Add the following code into the HelloWorldEventArgs class:

 

    public override void CallEventHandler(

        Type type, string eventName,

        object[] parameters, SPWorkflow workflow,

        string identity,

        System.Workflow.Runtime.IPendingWork handler,

        object item)

    {

        switch (eventName)

        {

            case "HelloWorkflow":

            var args = new HelloWorldEventArgs(workflow.InstanceId);

            args.Answer = parameters[0].ToString();

            this.HelloWorkflow(null, args);

            break;

        }

    }

 

    public override void CreateSubscription(

        MessageEventSubscription subscription)

    { throw new NotImplementedException(); }

 

    public override void DeleteSubscription(

        Guid subscriptionId)

    { throw new NotImplementedException(); }

 

The CallEventHandler method gets called each time an event is being requested in the Local Service. The first thing we do is check to see which event is being requested. If it's our HelloWorkflow event, we create a new HelloWorldEventArgs instance and pass in the workflow's instance ID to so the event knows which workflow it's invoking the event with. We next pass in the message string from the event receiver and lastly actually invoke the event.

 

With the Local Service now complete, we can start building the workflow and the event receiver that interface with this service. Continue the steps to build the workflow and the event receiver:

 

6) Configure the CallExternalMethod activity:

 

    A) Within Workflow1, add the CallExternalMethod activity.

 

    B) Within the properties of the activity, click the ellipsis next to the

    InterfaceType property and specify the IHelloWorldService interface and click OK:

 

    

 

    C) Change the MethodName property to be HelloHost.

      

    D) Change the message property to be "Hello Event Handler!"

 

7) Configure the HandleExternalEvent activity:

 

    A) Add the HandleExternalMethod activity below the CallExternalEvent activity.

 

    B) Within the properties of the activity, click the ellipsis next

    to the InterfaceType property and specify the IHelloWorldService interface and click OK.

      

    C) Change the EventName property to be HelloWorkflow.

Note: this is the only event the workflow will listen for. The event receiver must invoke this event to communicate with the workflow.

 

    D) Bind the "e" property to a new field handleArgs by clicking

    the ellipses and by choosing Field in the Bind to New Member tab and thereafter clicking OK.

 

    E) Go to the code view of the workflow and take the " = new …"

    off the end of the handleArgs property so it looks like this:

 

    public HelloWorldEventArgs handleArgs;

 

8) Configure a Log to History List activity:

 

    A) below the HandleExternalEvent activity, add a LogToHistoryList activity.

 

    B) Right click the LogToHistoryList activity and choose Generate Handlers.

 

    C) Within the activity's MethodInvoking method, add the following line of code

    to write the event receiver's message to the history:

 

    logToHistoryListActivity1.HistoryDescription =

        handleArgs.Answer;

 

9) Add an "Instance" column onto an Announcements list:

 

    A) find or create the announcements list on the site you're unit testing on,

    and under List Settings, click Create Column.

      

    B) Type a name of "Instance" and choose a Single Line of Text column type and click OK.

 

10) Add a new Event Receiver to the project:

 

    A) Right click the project, and choose Add -> New Item select the Event Receiver item.

      

    B) Give the receiver a name of AnnouncementsReciever and click Add.

 

    C) Choose List Events, Announcements, and An item was added, and click Finish:

 

    

 

    D) Add the code below in the ItemAdded method of the receiver:

 

    if (properties.ListTitle == "Announcements")

    {

        Guid instance = new Guid(properties.ListItem["Instance"].ToString());    

        string answer = "Hello Workflow!";

 

        SPWorkflowExternalDataExchangeService.RaiseEvent(

            properties.Web, instance, typeof(IHelloWorldService),

            "HelloWorkflow", new object[] { answer });

    }

 

The first thing this listing does is grabs the workflow's instance ID out of the Instance column and sets it to a GUID. This GUID is passed as a parameter into the RaiseEvent method which is how the RaiseEvent method knows which workflow to send the message to. Other parameters of interest are the SharePoint site where the workflow is running, the event to invoke ("HelloWorld" event), as well as our message to the workflow, "Hello Workflow!". That last parameter is actually an object array, so you can load that up with any serializeable object you think the workflow needs to do its business.

 

The very last think we need to do before we can test is register our Local Service with the SharePoint workflow runtime. This is done by adding an entry into the web.config. Follow this last step to register our service:

 

11) Register the HelloWorldService with the SharePoint workflow runtime:

 

    A) open your web application's web.config under c:\inetpub\wwwroot\wss\virtual directories\

 

    B) Find the <WorkflowServices> element.

 

    C) Add the following WorkflowService in the WorkflowServices element (all on one line):

 

<WorkflowService Assembly="PluggableWorkflowServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c1c16502a94a0846" Class="PluggableWorkflowServices.HelloWorldService">

</WorkflowService>

 

 

Note: if you find yourself getting a "The workflow failed to start due to an internal error" error when you try to start a workflow, you probably didn't perform step 11C correctly or the DLL isn't found in the GAC.

 

We're FINALLY ready to test this thing. Build and Deploy your project. Navigate to your SharePoint site, and under View Site Content, click Site Workflows. Start your pluggable workflow (should be named PluggableWorkflowServices - Workflow1). Navigate to the Announcements list and you should see a new announcement titled "Hello Event Handler!", as is seen in Figure 2:

 

Figure 2 The workflow wrote to an announcements lists, and an event receiver on that is will respond by calling back into the hydrated workflow instance.

 

Go back to Site Workflow and click the "Completed" status of the PluggableWorkflowServices - Workflow1 workflow. You should see the event receiver's response of "Hello Workflow!" (Figure 3).

 

Figure 3 The event receiver has called back into the hydrated workflow instance and the workflow logged the string message received from the event receiver.

 

 

Cheers!

 

Phil

June 22
Building SharePoint Event Receivers in 2010

If you need some action to occur when a user deletes a document, for example, you may right away think you need a workflow when in fact an event receiver will do. The main difference between the two is a workflow is typically long running, whereas an event receiver is immediate.

 

Why would you want an event receiver? What if all you wanted to do is execute a piece of code when a document is being deleted. Say you wanted with code to archive that document when a user goes to delete it. This is a great example where an event receiver is nice, because that deleting event can trigger your custom code. You could do this with a Visual Studio workflow that just has one activity in it, but that's a lot of overhead for something that an event receiver does with a lot less effort. Alternatively, you could use a SharePoint Designer workflow, but with SPD you can't move documents across the site collection boundary, so custom code is the only way to go. The table below shows more comparisons between workflows and event receivers:

 

Comparing a workflow with an event receivers

Event Receivers

 

Workflows

Immediate fire

Versus

"Long running"

Lives and dies (no state)

Versus

Maintains "state"

.NET code only

Versus

.NET or SharePoint Designer

No human interaction

Versus

Typically involves human interaction

Before or after events

Versus

Only After events

Executes on sites, features, lists, and list items

Versus

Executes on Sites, items, and content types.

 

 

You really can't say one is better than the other. It depends entirely on your business requirements. Besides when documents are deleted, there are many other events you can respond to. The events fall into six categories as shown in the table below. Each category has a few of the more common events shown in the second column, but note there are many more events available as well.

 

Six event receiver categories

List Events

Adding/ed a new list, field.
Updating/ed a field.

List Item Events

Adding/ed a new list item or document.
Document checking/ed in or out.
Adding/ed an attachment.
Deleting/ed an item or document.

List Email Events

A list received an email.

Web Events

Deleting/ed a site collection or site.
Creating/ed a new site collection or sub site.

Feature Events

Feature activating/ed or deactivating/ed.

List Workflow Events

A workflow is starting/ed, postponed, or completed.

 

 

You'll notice two things in this table. First is that there are a ton of events that you can have custom code respond to, and secondly that most events have a "before" and "after" (adding/added) event associated with it. As shown in Figure 1, before events happened before the change is committed to the SharePoint content database. This is helpful when you may want to cancel a change before it is saved. The event receiver for when a site is being deleted is a good example. Before the site is deleted, you could back it up first. On the other hand, there's a corresponding event receiver for after the site was deleted.

 

Figure 1 Events typically have a "before" and an "after" event, corresponding to when the event happens in relation to when the source is committed to the database.

 

To demonstrate how to build an event receiver, we're going to use the Workflow events as an example. The previous table shows how there are four events under the List Workflow Events category. We can respond to when a workflow is starting and when it has started, when a workflow is postponed, and when a workflow has completed. To keep the example simple, let's write an event receiver that creates an announcement when a new calendar event is created.

 

Start by creating a new project in Visual Studio 2010. You'll notice under the SharePoint tab that there's a new project template called Event Receiver (Figure 2). You can use this template to create any of the afore mentioned event receivers.

 

Figure 2 Visual Studio 2010 now has a new project template you can use to easily create new event receivers.

 

After you create the project, you'll get a dialog menu asking you to specify the URL of the site where you want to deploy and unit test your event receiver. Specify the URL and click next. Afterwards you'll be prompted to specify what type of event receiver you want to create (Figure 3). The drop down will contain the event types for each of the six categories except for the feature events category. Feature events are created by right clicking the feature from within the project after it's created. For the announcement example, select the List Workflow Events category:

 

Figure 3 There are six main event categories (five shown).

 

After you select the event category, you'll need to specify the event source. This tells the feature that is to be created what events you want to respond to. Notice in Figure 4 you can handle events from announcements, document libraries, and many others. Select the Calendar source:

 

Figure 4 You'll need to specify the event source that will raise the event and call your event receiver.

 

Next you want to specify what workflow event we want to handle. Specify the A workflow has started event. Now our code will execute any time a workflow is started on a calendar (Figure 5):

 

Figure 5 After you choose the event source, you need to specify the actual event you want to respond to. In this case we want to execute our code when a workflow has started.

 

After you create the project, you'll be sent to a method named WorkflowStarted. This is where we can add our code to create our announcement. Enter the code found in below to create the announcement:

 

 

if (properties.ActivationProperties.List.Title == "Main Calendar")

{

    string siteurl = properties.ActivationProperties.Site.Url;

 

    SPSecurity.RunWithElevatedPrivileges(delegate()

    {

        using (SPSite site = new SPSite(siteurl))

        {

            using (SPWeb web = site.RootWeb)

            {

                SPListItem item = web.Lists["Announcements"].Items.Add();

                item["Title"] = "The workflow has started!";

                item.Update();

            }

        }

    });

}

 

 

The first thing this code block does is look at the activation properties to determine which calendar the event came from. Remember that our source was "Calendar" which just means any calendar on the site will raise this event when a workflow is started. If we have more than one calendar on the site we'll want to determine which calendar the event came from.

 

Next we elevate the privileges of the running use to be the service account. We don't know for sure if the running user has Contribute rights on the announcements list, so we elevate their permissions to be safe. Next we create the announcement, assign a title value, and commit the announcement to the database.

 

With this code in place, all we need to do is deploy the solution. Right click the project name in Visual Studio, and click Deploy. This will deploy the feature and assembly on our before. Next go and create a new calendar entry and start a workflow on that event. Afterwards, a new announcement will show up in the announcements list on that site.

 

 

Phil

1 - 10Next

Sign In