Tuesday, August 25, 2009

This application requires a newer version of the Microsoft .NET Compact Framework than the version installed on this device

This is the second time I ran into this error in last couple months. I better write it down so I don’t forget again.

I was creating a .NET Compact Framework Application. I want it to be able to run on older devices, so I choose .NET Framework 2.0 thinking the project will be .NET 2.0 project(see picture 1). However, when I tried to run the application on a smart phone, I got the error “This application requires a newer version of the Microsoft .NET Compact Framework than the version installed on this device.” You have to specify the version of .NET Compact Framework on the second dialog box(see picture 2).

image

Picture 1

image

Picture 2

Monday, August 17, 2009

SharePoint Analysis and Design Tips.

Object-Oriented Analysis and Design is a relatively mature technique. We call it Object-Oriented because the technique is to implement various requirements with objects in code. Likewise, In SharePoint Analysis and Design, we map requirements to SharePoint components such as web parts, lists, document libraries, views, site columns, content types, page layouts, info path forms, application pages, excel services, business catalogs, user profiles, my sites, site maps and so on.

The article is not to establish a methodology. It tries to explain a couple of experiences that may help in SharePoint Analysis and Design.

1. Map business requirements to technical solution as early as possible.

In object-oriented design, separating business domain requirements and technical solutions is a good practice. Technical solutions should not impact collecting business requirements if possible. However, this separation of requirement and solution is what SharePoint Analysis and Design should avoid. Considering technical solution at early stage will provide the following benefits:

  • Make business users aware what cannot be done with SharePoint's out-of-box features. Business users expect SharePoint to do Anything through configuration, but custom development is inevitable for most of the enterprise-level MOSS applications. Custom development demands more efforts, and we need to inform those efforts to customers as early as possible.

  • Give SharePoint developers the opportunities to explain the out-of-box features that will meet or exceed the same business goals with different business processes. For example, the business goal of sharing knowledge and experience among employees can be archived by using out-of-box wiki library. Therefore, the original use cases collected from customers for achieving the same business goal will be altered according to the wiki libraries' behavior.

2. Strive for configurable/customizable solutions.

SharePoint allows end users to customize solutions without going through development cycles. End users may customize lists, document libraries, site pages, site master pages, define security groups and permission levels, etc; They may publish InfoPath forms and excel sheets, and create ad-hoc reports and dashboards using PerformancePoint. To take advantage of those features while designing SharePoint solution, consider separate the responsibilities of developers and end users. Both end users and developers have the responsibilities to contribute to SharePoint solutions. For example, in an InfoPath form, developers implement the web services for InfoPath to connect to and configure the SSO application while end users are responsible to create the InfoPath connections to the web services using SSO authentication. The relationship between developers and end users is very much like how frameworks' developers are related to the developers who use the frameworks. Basically, developers should deal with more difficult tasks and expose their work through interfaces to end users. The interfaces in SharePoint context, which is very different from the .NET interfaces, are in the formats of InfoPath connection, SSO, Content Type, Page Layout, etc.

Sunday, August 9, 2009

SharePoint Workflow - Use IListItemService to manipulate list items other than the workflow list items.

SharePoint workflows can use OnWorkflowItemChanged and OnWorkflowItemDeleted activities to capture the "changed" and "deleted" events for the list items associated with the SharePoint workflow instances. To capture the "changed" and "deleted" events for the list items not associated with SharePoint workflow instances, we need to use SharePoint workflows' IListItemService.

IListItemService is one of four ExternalDataExchange services - IListItemService, ISharPointService, ITaskService and IWorkflowModificationService - registered by SharePoint runtime. IListItemService provides the methods and events to handle any list items in the SharePoint Site Collection where a workflow instance is running. To study the interface, I developed two workflows to illustrate two different scenarios using IListItemService.

Scenario 1: A workflow needs to capture the OnItemChanged event of a pre-existing list item.

To test the scenario, I created two SharePoint lists:

  • workflowlist: it has a workflow association with the testing workflow and one item called "workflow item".
  • anotherlist: the changed events of its items should be captured by the testing workflow associated with workflowlist above. The list's listid is 7c455073-56ce-4ecd-bfa8-2923fc521b9e and it has one item called "another list item". The id of the item is 1.

The following is the design view of the testing workflow:

clip_image002

The workflow includes three activities:

1. OnWorkflowActivated activity is required by all the SharePoint workflows.

2. InvokeIntializeForEvent is a subclass of CallExternalMethodActivity with the following parameters:

clip_image004

The activity will invoke the InitializeForEvent method defined in the IListItemService interface. The signature of the method is:

[CorrelationParameter("id"), CorrelationParameter("itemId"), CorrelationParameter("listId"), ExternalDataExchange, SharePointPermission(SecurityAction.LinkDemand, ObjectModel=true), SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel=true)]
public interface IListItemService
{
......
void InitializeForEvent(Guid id, Guid listId, int itemId);
}
















































Parameter Name



Parameter Value



Description



id



f1bb0cd5-e470-4e70-90ea-e37525a3662a



A GUID generated at development time



itemId



1



The id for the “another list item” list item  in the "anotherlist" SharePoint list



listId



7c455073-56ce-4ecd-bfa8-2923fc521b9e



The id of the "anotherlist" SharePoint list







3. handeItemChangedEvent is a HandleExternalEvent activity with the following parameters:



clip_image006



This activity will wait on the OnItemChanged event defined in IListItemService interface. It should use the same Correlation Token, newtoken, for the activity prior to it.



We can test the workflow with the following steps:



1. Start the workflow for the "workflow item” list item in the "workflowlist" SharePoint list. The workflow should stop at "in progress" status since it is blocked and waiting for the OnItemChanged event.



clip_image008



2. Modify the "another list item" list item in the "anotherlist" list and change its title to "another list item changed".



clip_image010



3. Go back to the workflow list. Its workflow status is changed to complete since step 2 modifies the list item and trigger OnItemChanged event.



image



The solution demonstrates the following key points:




  • IListItemService.InitializeForEvent should be invoked to establish the correlation token for a list item before you can capture the event for the list item. (You may use IListItemService.UpdateListItem, CheckInListItem, etc instead. ).


  • listId and itemId for InitializeForEvent must match the ids of the SharePoint list and list item.


  • id for InitalizeForEvent can be any uniquely generated GUID. It does not need to match any ids.



Scenario 2: A workflow needs to create an new list item and wait on its changed events.



In this case, you need to use IListItemSevice.CreateListItem to create the list. However, you cannot use the correlation token for CreateListItem because the "itemId" parameter is unknown while invoking the method.  You can get the itemId as a "returnvalue" in your workflow instead.



So, here is the solution:



1. Add a CallExternalMethod activity to invoke the "CreateListItem" method. The activity should include an event handler for MethodInvokded event. In the event handler, get the return value and assign the value to a member variable to the workflow.




protected override void OnMethodInvoked(EventArgs e)
{
this.ItemId = (int)base.ParameterBindings["(ReturnValue)"].Value;
}







2. Add an InitializeForEevnt activity to invoke the "InitializeForEvent" method. Make sure use the ItemId returned by CreateListItem. This activity should create a new token, which is different from the one for "CreateListItem". The token for "CreateListItem" is basically not used any where.



3. Add an HandleExteranlEvent activity as in Scenario 1.

Wednesday, August 5, 2009

Get LINQ to SQL Connection String from config file

When using Visual Studio 2008 to create LINQ to SQL DBML file, it automatically generates database connection information for us. However most of the time, we want to get the connection string from a config file. Here is how to do that:

  • Add ConnectionString to config file
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="DotNetIdeasConnectionString"
connectionString="Data Source=MyServer;Initial Catalog=DotNetIdeas;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>






  • Open the DBML file in designer mode. Clear the “Connection String” in properties



image




  • Add partial class, for example:




using System.Configuration;
namespace DotNetIdeasData
{
partial class DotNetIdeasDataContext
{
public DotNetIdeasDataContext()
: base(ConfigurationManager.ConnectionStrings["DotNetIdeasConnectionString"].ToString(), mappingSource)
{
OnCreated();
}
}
}









One thing need to be pointed out here is that every time you modify the DBML file, you have to clear that “Connection String” in properties again. Because when you drag/drop tables to the designer, it re-generate everything!