February 2010 - Posts
I attended the monthly Microsoft Solution Advocate call this morning and during the demo of some cool code you use in your Windows 7 applications, they pointed out a new feature in Visual Studio 2010 that I hadn’t seen before. When you press Ctrl+Comma (,), it brings up the new Navigate To window.
This window allows you to do a quick search for classes and methods that are in your solution. If you have Resharper installed, I think this key combination is already bound, so this may not work. Typing in a few letters, gives you results that look this.
As you can see it brings up methods that are in my solution. You can also type class names and various other things. Now before you start, I am sure this was already in Resharper years ago, but it’s still kind of a neat feature. Try it out.
I’ve been talking a lot about external lists lately as you may know. They are so easy to create with SharePoint Designer, but you wouldn’t really deploy them to production that way would you? Of course not! We need a feature. At first, you might think generating all of that CAML might be too difficult, but remember we can use the Save Site as Template page to generate a .wsp file for us. Using this we can import it into Visual Studio and learn what CAML we need to use.
We’re going to start with the same external list from yesterday. You can build your own using SharePoint Designer as described in my BCS post.
Once you are happy with your list, go to Site Settings –> Save Site as Template. Fill in the fields and it will save a solution package in your solution gallery of your site collection. Save the .wsp file to disk and we’re ready to begin. Once you have your solution package, open Visual Studio 2010, create a new project, and use the Import SharePoint Solution Package project template. You will be prompted for what site to use and where the package file is. On the next step, you will be prompted for what you want to import.
The solution package has every site column, content type, page, list, etc, on the site so there is a lot to sift through. However, the only thing we really need is the list instance in question (in my case Products). You will want to unselect everything else as it will take forever to import if you don’t.
Quick Tip: There isn’t a select all button, so press Ctrl+A, and then unclick a checkbox to deselect everything.
On the next step you will get a warning about dependencies. Go ahead and just say yes and your project will import. Your project will likely have a bunch of stuff you don’t really care about. All that you really need is what it is created in List Instances. Your list instance might look something like this.
At this point, I created a new project because I want to keep things clean. Create a new project and create a new List Definition. At this point, you will have a schema file and two new elements files. The elements.xml file with the list template can be deleted since I don’t want users creating new versions of this list. Before we start digging into the CAML that is required to make an external list work, let’s go back and look at SharePoint Designer so we can remember what our application definition looked like.
The reason I show you this is because these values we’re going to see when we take a look at our elements.xml file. Here is the file that came from the site export.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance FeatureId="{00bfea71-9549-43f8-b978-e47e54a10600}" TemplateType="600" Title="Products"
Description="" Url="Lists/Products" CustomSchema="Files\Lists\Products\Schema.xml"
HyperlinkBaseUrl="http://sp2010/Test" RootWebOnly="FALSE"
xmlns="http://schemas.microsoft.com/sharepoint/">
<DataSource>
<Property Name="Entity" Value="Products" />
<Property Name="EntityNamespace" Value="http://sp2010/test" />
<Property Name="LobSystemInstance" Value="bcs_test" />
<Property Name="SpecificFinder" Value="Read Item" />
</DataSource>
</ListInstance>
</Elements>
A couple of things to note here. First, the TemplateType is 600. We can only presume this is the list template id for an external list. The other thing of note is the new DataSource element. This was not in previous versions of SharePoint. As you might notice here, these values correspond to what we see on the external content type. This is what you will change should you decide to rename the entity in or change the LobSystemInstance name. This XML is highly useable in our own feature, but I am going to remove some of the unnecessary attributes such as FeatureId and CustomSchema. I’m also going to give it a new Title so that we know this is a different list. Here is what my new elements.xml looks like.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance TemplateType="600" Title="Products - Deployed by Feature" Description=""
Url="Lists/ProductsDeployedByFeature" HyperlinkBaseUrl="http://sp2010/Test"
RootWebOnly="FALSE" xmlns="http://schemas.microsoft.com/sharepoint/">
<DataSource>
<Property Name="Entity" Value="Products" />
<Property Name="EntityNamespace" Value="http://sp2010/test" />
<Property Name="LobSystemInstance" Value="bcs_test" />
<Property Name="SpecificFinder" Value="Read Item" />
</DataSource>
</ListInstance>
</Elements>
Honestly, I think I can get rid of HyperLinkBaseUrl as well. It doesn’t seem to matter though, I deployed it to a few other sites and it seems to work fine. Now let’s take a look schema.xml. It’s actually pretty small which is nice compared to the files we were used to in the past.
<?xml version="1.0" encoding="utf-8"?>
<List Title="Products" Direction="none" Url="Lists/Products" BaseType="0" Type="600"
FolderCreation="FALSE" DisableAttachments="TRUE" Catalog="FALSE" RootWebOnly="FALSE"
SendToLocation="|" ImageUrl="/_layouts/images/itgen.gif" xmlns:ows="Microsoft SharePoint"
xmlns:spctf="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"
xmlns="http://schemas.microsoft.com/sharepoint/">
<MetaData>
<ContentTypes>
<ContentType ID="0x01" Name="Item" Group="List Content Types" Description="Create a new list item." FeatureId="{695b6570-a48b-4a8e-8ea5-26ea7fc1d162}">
<Folder TargetName="Item" />
<FieldRefs>
<FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" />
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
</FieldRefs>
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Display>ListForm</Display>
<Edit>ListForm</Edit>
<New>ListForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
</ContentType>
</ContentTypes>
<Fields>
<Field DisplayName="BDC Identity" Hidden="FALSE" Name="BdcIdentity" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="BdcIdentity" Type="Text" />
<Field DisplayName="Name" Hidden="FALSE" Name="Name" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Name" Type="Text" />
<Field DisplayName="Id" Hidden="FALSE" Name="Id" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Id" Type="Integer" />
<Field DisplayName="Color" Hidden="FALSE" Name="Color" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Color" Type="Text" />
<Field DisplayName="Description" Hidden="FALSE" Name="Description" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Description" Type="Text" />
<Field DisplayName="Price" Hidden="FALSE" Name="Price" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Price" Type="Decimal" />
</Fields>
<Forms />
<Views>
<View DisplayName="Products Read List" DefaultView="TRUE" BaseViewID="1" Type="HTML" MobileView="TRUE" MobileDefaultView="TRUE" ImageUrl="/_layouts/images/generic.png" XslLink="main.xsl" WebPartZoneID="Main" WebPartOrder="1" Url="Read List.aspx" SetupPath="pages\viewpage.aspx">
<XslLink>main.xsl</XslLink>
<Method Name="Read List" />
<Query>
<OrderBy>
<FieldRef Name="Name" />
</OrderBy>
</Query>
<ViewFields>
<FieldRef Name="Name" ListItemMenu="TRUE" LinkToItem="TRUE" />
<FieldRef Name="Id" />
<FieldRef Name="Color" />
<FieldRef Name="Description" />
<FieldRef Name="Price" />
</ViewFields>
<RowLimit Paged="TRUE">30</RowLimit>
<Aggregations Value="Off" />
</View>
</Views>
</MetaData>
</List>
As you can see it’s pretty clean. It again uses the Type of 600. In the ContentTypes section, it does define a regular list item, but it doesn’t actually add any of the fields from the external list there. The Fields element has a field defined for each field in my external content type. As you can see there is nothing special about the way they are defined. The only one of note is the BdcIdentity field which I assume is required to keep track of the ID that ties back to the BCS itself. The View is surprisingly clean as well. The XslLink and Method elements are new. I assume we can use XslLink to customize how the view is rendered, but I didn’t actually see a main.xsl file generated in the solution package anywhere. The Method element I can only assume corresponds to the name of the finder method Read List which we have seen above. The rest is pretty simple. It just has a FieldRef for each column in my external content type.
I can pretty much use the Schema.xml file as is. I did change the Title and Url attributes at the top but that is it. At this point my new feature to deploy this is ready to go. Here is what it looks like in Visual Studio.
Deploy your solution and have it activate your feature. The new list won’t show up automatically in the navigation on the side, but it will be there. Just go to the URL directly or view the lists on your site to get there. Here it is on my new site.
As you can see it’s really not that hard to deploy an external list to another site properly using a feature. Now, you might have noticed there are a few other elements.xml files generated when we did the site export. One stores specific values in the property bag of the list. This we did not want because we don’t really want to copy those internal properties. The other deals with the forms of the list. It uses the BinarySerializedWebPart which scares me a bit, so I haven’t messed with it much. If you are just using default forms you don’t need to worry about it. In a future post, I’ll try seeing if I can deploy some InfoPath forms along with the list, but I figured that deserves its own post.
Also, one other thing to note. If you are deploying your external content type to another server, you can do that in the same manner as you did in SharePoint 2007. Just export the application definition and import it on the new server. You can also use the Export Application Model button in SharePoint designer on the External Content Types list.
I hope this helps when you look to move lists into production. This technique of exporting and importing will also work with regular lists of course. Just remember, friends don’t let friends deploy lists without a feature.
Let’s face it. Nowadays, management absolutely loves BI. Especially when there are lots of pretty charts and graphs. The thing is I’m not a BI guy. Cubes scare me to death. Luckily, there is the new snazzy Chart Web Part in SharePoint 2010 that gives you some BI like capabilities. It doesn’t allow you to drill down and pivot and do all that fancy stuff, but it does let you make some nice graphs and charts that any ordinary developer can do.
To get started, first you need an Enterprise version of SharePoint 2010. Unfortunately, I used the key that was later determined to be the wrong one which led me to reinstall SharePoint with a new key. If you don’t have any Enterprise options available, then you will be reinstalling. Assuming, you do have an Enterprise key, you then need to activate the SharePoint Server Enterprise Site Collection Features.
This adds the chart web part (among other things). Then edit any page and add a Chart Web Part. You can find it under Miscellaneous as of Beta 2 (they may have found it a new home in later versions).
When you add it to the page, it uses some dummy data and displays a simple bar chart.
At this point, you might be asking yourself “What kind of data can I bind this to?”. By clicking Data & Appearance, you will see the following screen which leads you to links to customize the appearance or bind to data.
We’ll start by going to Connect Chat To Data. Look at these great options we have to choose from.
We can connect to another web part, a list, an external content type (looks like they need to update the BDC wording they have there), and to Excel Services. I tried going straight to an external content type, but I got a yellow screen. Apparently that is broken on my version, but I’m sure it will work in RTM. There is a work around though. You can pick an external list using the Connect to a List option. This is exactly what I am going to do.
Remember that external list I created on my BCS blog post? I’m going to use that list and extend it some. What I did is I created a new custom list to contain Monthly Sales information. I used an External Data field to allow the user to pick a product from the external content type and enter in some sales data in a field.
This is the list we are going to use on our chart. On the next step of the wizard, we are allowed to pick a list. Note that it also allows you to choose other sites in the collection as well.
The next step allows you to filter your data first if you are so inclined.
The Chart Web Part has a ton of configurable options. On this last step is where you will start seeing some of them. The main thing to set here is your X and Y fields. You can also specify something to group by as well.
Once you finish this last step, you’ll get something that looks like this.
So, what we have here is a chart bound to data coming from a database (via external content type) and a SharePoint list. Pretty cool, right? I think so. It gets better though. What if your boss doesn’t like bar charts? No problem. There are tons of chart types to choose from. Just click on Data & Appearance again and then Customize Your Chart. Look at all of these built in chart types you have.
On the next step, you can further customize the chart you choose. It has some nice pre-built color themes and you can customize the size and what not. It also gives you a live preview as you change settings.
Here is what my new chart looks like.
You can customize things even more by going to Advanced Properties. It would probably take me a week to show you everything that this web part does, so I recommend you go try it out for yourself. The charts and graphs this thing produces are so cool, you should have no issue getting your boss to sign off on the Enterprise license. :)
If you are already familiar with SharePoint 2010, you already know how easy it is to build and deploy a web part now. However, this post is for those that don’t keep up with SharePoint as some of us do and may not realize how the development experience has improved so much. My post How to Build and Deploy a Web Part is by far the most popular post on DotNetMafia.com. I wanted to make today’s post just as a point to show you how much less work is involved in deploying a web part. I am going to group this post into sections in a similar manner as I did the post for the WSS3 post.
Environment
There can be entire talks about what the best way to develop is now, but we’ll start with the simplest. Although you can install SharePoint on Windows 7 and directly develop on it, most people are going to say stick with a virtual machine and run Windows Server 2008 R2. It’s certainly simpler to get all of the prerequisites installed if you stick with Windows Server. The benefits to developing directly on a machine with SharePoint on it are so great now that I would recommend against remote debugging (although you still can). The SharePoint Root (or the 12 hive as you called it) is now the 14 hive and is located at the predictable path below.
C:\Program Files\Common Files\microsoft shared\Web Server Extensions\14
Coding the Web Part
Here is where things start to change. Instead of creating a class library and adding references to the SharePoint DLLs, we simply use one of the new included SharePoint project templates as you can see here.
Start by using the Empty SharePoint Project template. Also make sure you have it set to .NET Framework 3.5 as SharePoint does not run under .NET Framework 4.0 (don’t get me started). You’ll notice you have many different project templates to choose from. Most of these can also be used once you create an empty project. On the next dialogue, pick farm solution. I’ll go into the difference between sandboxed and farm solutions, but more than likely you are going to use farm solutions every time. You also need to specify the URL to your server. You can change that if you want but the default value will probably work for you in this case.
This gives us a solution that looks like this.
Now we are ready to build our new web part. If you bring up the add new item context menu, you will see a number of choices for the types of new SharePoint Project Items (SPIs) that you can create. We’re going to choose Web Part in this case.
What is the Visual Web Part you ask? That’s just a user control which relates directly to my second most popular post on How to Deploy a User Control. Now we’re finally ready to add some code. We’re just going to take our code from the WSS3 post and use it here.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
namespace SharePointProject1.TestWebPart
{
[ToolboxItemAttribute(false)]
public class TestWebPart : WebPart
{
public TestWebPart()
{
}
protected override void CreateChildControls()
{
base.CreateChildControls();
Controls.Add(new Label() { Text = "My Test SharePoint 2010 Web Part (Hello World)!" });
}
protected override void RenderContents(HtmlTextWriter writer)
{
base.RenderContents(writer);
}
}
}
The only line of code I added here was the line to add the label and set its text. Everything else came from the template.
Describing the Web Part
In my WSS3 post, this is where I talked about building a .webpart file. Well, you don’t need to worry about that any more as Visual Studio creates it for you. Here is what solution explorer looks like after you add your first web part.
As you can see the .webpart file is already there as well as an elements.xml file for a feature to deploy the web part. The WSS3 post went on to talk about all of the things you need to know about building a feature. This is still good stuff to know, but its already taken care of for you. If you want to edit the basic feature information, just open it up in solution explorer and you get a nice new interface that looks like this.
I’m not going to waste space showing you the insides of the files it creates for you. Just know it creates them for you and it saves you a ton of time.
Deploying via Solution Package
In my WSS3 post, I explained how to create a cab.ddf and manifest.xml file. Well guest what? That is taken care of for you now as well. The Package.package file in the solution explorer provides another nice editor which allows you to choose with files go into the package. You don’t have to keep track of a thing any more, it just builds the package and takes care of it for you.
At this point, Visual Studio has created the .webpart file, the feature, and the solution package. However, we still need to deploy it and if we could debug it that would be even cooler right? Take a look at our new options in the Build menu.
We can build and rebuild just like any other project, but notice the options for Deploy, Package, and Retract. Those are all SharePoint functions. In this case, I want to deploy my solution. Choosing deploy, we see the following in the output window.
------ Build started: Project: SharePointProject1, Configuration: Debug Any CPU ------
SharePointProject1 -> C:\Code\SharePointProject1\bin\Debug\SharePointProject1.dll
Successfully created package at: C:\Code\SharePointProject1\bin\Debug\SharePointProject1.wsp
------ Deploy started: Project: SharePointProject1, Configuration: Debug Any CPU ------
Active Deployment Configuration: Default
Run Pre-Deployment Command:
Skipping deployment step because a pre-deployment command is not specified.
Recycle IIS Application Pool:
Recycling IIS application pool 'SharePoint - 80'...
Retract Solution:
Skipping package retraction because no matching package on the server was found.
Add Solution:
Adding solution 'SharePointProject1.wsp'...
Deploying solution 'SharePointProject1.wsp'...
Activate Features:
Activating feature 'Feature1' ...
Run Post-Deployment Command:
Skipping deployment step because a post-deployment command is not specified.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========
From inspecting the text of the log, you can see that Visual Studio compiled, created a package, reset my Application Pool, Added the Solution, Deployed the Solution, and activated the feature. Let’s check SharePoint and see if it’s really there.
Checking the web part gallery, we see our .webpart file. Let’s add it to a page and see how it looks. Edit any page and use add a web part and you will see the new interface for choosing a web part. It puts it in the Custom group by default.
One you hit and add finish editing, we see the web part working correctly.
You have to admit this is quite a bit easier than deploying a web part in SharePoint 3. What if you want to debug though? No problem. Just set a breakpoint and choose debug from the build menu like you would any other type of project.
As I mentioned earlier, if you are familiar with SharePoint 2010, this is nothing new to you. However, my point today is for those who shied away from SharePoint in the past because the development experience was far from optimal. Try it for yourself and you will see how easy it is to get up and running with your code. Even with pictures this post is half the size of the WSS3 post. That’s because it really is just that easy. I really think Visual Studio 2010 will open the way for a new round of SharePoint developers. Try it out today.
Office Web Apps is one of my favorite new parts of SharePoint 2010, but there are a number of steps you need to follow to ensure that they actually work. To start with, be sure and download the Installation guide and run any scripts mentioned in there as necessary. Also, you will want to run the script mentioned in Jie Lie’s post. You also want to verify that the Word Viewer Service application is started. However, if you are like me, you may still find that you get an error like the one below.
An unexpected error has occurred.
Yes, it’s good to know that useful error message hasn’t gone away. However, we do gain something here. You will notice that I have the Correlation Id highlighted. This corresponds to an actual error message in the ULS logs in the 14 hive. What is great, is you can use the handy ULS Viewer tool, to make troubleshooting this error even easier. Simply search for that Correlation Id and you will find out what the actual error was. In my case, the answer was really quite simple as you can see from the error message.
Microsoft.Office.Web.Common.BadCanaryConfigurationException: This feature is not activated for the site collection.
If you want to use the Word Viewer, you actually need to activate the feature on the Site Collection. To resolve this, I simply activated the Office Web Apps feature.
Strangely enough, my Excel Viewer was working the entire time without this activated. If you haven’t seen Office Web Apps, here is what the Word Viewer looks like running in Chrome using Silverlight.
I’ll also mention that in the past I have gotten a yellow screen instead of an error message. This makes it difficult to determine a Correlation Id, but you can still browse the ULS logs using the ULS viewer pretty easily.
It looks like the answer is no. When you click the Include Content checkbox on the Save as Template page, it looks like only the latest version of documents and lists items are included in the .wsp file. I ran a few tests and here is what I saw. Consider the following document with its history from the source site.
After I export the site, install and activate the solution on a new site collection and then use the Create Site menu to create a new instance of my site template, only the latest version of the document is there. Now, I’m not complaining as this is already a great step in the right direction and it really does make moving things around between servers easier. In the screenshot below though, you can see that only the latest version is present.
I’m not really sure why it creates two versions of the document (with different file sizes even), but I am sure there is a reason. If you haven’t use Save Site as Template, be sure and check it out. It does export content (including documents). Your documents are packaged up in the .wsp and use the module element to copy them via feature to your document library. Here is what your package looks like in Visual Studio 2010.
As you can tell from my string of posts lately, I am really excited about the Save Site as Template functionality. We all would have killed to have it in WSS3. It really is a great way to see how things are built behind the scenes to using CAML.
When the NDA dropped, I mentioned how you could use the new PropertyBag element in a feature to write values into the property bag of a site. I already thought that was going to be incredibly useful, but it turns out the power of this new element is even greater than I first realized. At the time I noticed that the PropertyBag element had a Url attribute but I didn’t understand what it was used for at the time. Now I do. As I mentioned yesterday, I have been experimenting with exporting sites as .wsp files and I discovered that you can use this to write to properties of an existing list item. Take a look at the following example, that I grabbed from the file it generated when exporting my site. This applies properties to default.master.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<PropertyBag Url="_catalogs/masterpage/default.master" ParentType="File" RootWebOnly="FALSE" HyperlinkBaseUrl="http://sp2010/Test" xmlns="http://schemas.microsoft.com/sharepoint/">
<Property Name="vti_charset" Value="utf-8" />
<Property Name="vti_candeleteversion" Value="true" />
<Property Name="vti_modifiedby" Value="SHAREPOINT\system" />
<Property Name="vti_cachedneedsrewrite" Value="false" />
<Property Name="vti_level" Value="1" />
<Property Name="vti_rtag" Value="rt:7CDE768C-FCE1-46E8-9165-FDA564419D9A@00000000002" />
<Property Name="vti_cachedbodystyle" Value="<body scroll="yes" onload="BLOCKED SCRIPTif (typeof(_spBodyOnLoadWrapper) != 'undefined') _spBodyOnLoadWrapper();">" />
<Property Name="vti_title" Value="<asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server"/>" />
<Property Name="UIVersion" Value="3" />
<Property Name="vti_parserversion" Value="14.0.0.4536" />
<Property Name="vti_cachedhastheme" Value="false" />
<Property Name="vti_foldersubfolderitemcount" Value="0" />
<Property Name="vti_hasdefaultcontent" Value="true" />
<Property Name="vti_setuppathversion" Value="4" />
<Property Name="vti_metainfoversion" Value="2" />
<Property Name="vti_canmaybeedit" Value="true" />
<Property Name="vti_generator" Value="Microsoft SharePoint" />
<Property Name="vti_etag" Value=""{7CDE768C-FCE1-46E8-9165-FDA564419D9A},2"" />
<Property Name="vti_cachedtitle" Value="<asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server"/>" />
<Property Name="vti_contentversion" Value="0" />
<Property Name="vti_progid" Value="SharePoint.WebPartPage.Document" />
<Property Name="vti_timelastmodified" Value="2010-02-10T16:12:34Z" />
<Property Name="vti_sourcecontrolversion" Value="V1.0" />
<Property Name="vti_timecreated" Value="2010-02-10T16:12:34Z" />
<Property Name="vti_folderitemcount" Value="0" />
<Property Name="vti_docstoretype" Value="0" />
<Property Name="ContentTypeId" Value="0x01010500CE60EDAC92E8C942BFB82AF8AFC4A5F4" />
<Property Name="vti_filesize" Value="29039" />
<Property Name="vti_sourcecontrolcookie" Value="fp_internal" />
<Property Name="vti_author" Value="SHAREPOINT\system" />
<Property Name="vti_setuppath" Value="global\default.master" />
<Property Name="vti_metatags" Value="GENERATOR Microsoft\ SharePoint progid SharePoint.WebPartPage.Document HTTP-EQUIV=Content-Type text/html;\ charset=utf-8 HTTP-EQUIV=Expires 0" />
<Property Name="vti_replid" Value="rid:{7CDE768C-FCE1-46E8-9165-FDA564419D9A}" />
<Property Name="vti_docstoreversion" Value="2" />
<Property Name="vti_cachedcustomprops" Value="vti_title" />
</PropertyBag>
</Elements>
As you can see the relative URL of the file is specified along with a separate attribute indicating the base URL it came from. You also need to specify something for ParentType. For now I know that you can specify File or Folder there (although I am guessing it might take Item or something like that).
Now for the most part all of these properties listed above are internal properties that you would never care about setting, but as you guessed you can specify your own properties as well. The export site as template functionality does its best to export everything it can and that includes internal properties as well. This is pretty interesting feature and I think it could remove the need to write a lot of code in my future. @SPKyle might still want to write code to store properties on list items, but I will take a pass. :)
One of my favorite new features in SharePoint 2010 is the ability to have lists relate to each other and the fact that you can maintain referential integrity. Now, I am sure there are at least 50 posts out there already that show you how to do this using the UI. The UI is great, but you are a developer and that means you might want to actually deploy these lists elsewhere someday. Now I have to admit, using the save site as template feature is great for moving sites and content around but you may want a more granular deployment of a pair of lists in a single feature. The cool thing is the save site as template feature is extremely useful for discovering the underlying CAML to build things in SharePoint which is how I discovered relational lists work today. This post has some great screenshots of how to export a site template as a wsp.
For today’s example, I have a list of categories and a list of products. The products list has a lookup column which references the categories list. Here is what the products list looks like.
On this view, you can see that I am linking to the Categories list and I am displaying the Id and Description from that list. Here is how my lookup column is defined.
Creating a list with CAML in SharePoint 2010 is really not much different than in previous versions. In order to set up this lookup column, we need to look at the Schema.xml of the Products list. It turns out that the column that the user chooses is still a lookup column but so are the additional fields. They just have additional attributes defined. Let’s look at the definition of the lookup column the user interacts with first.
<Field Type="Lookup" DisplayName="Category" Required="FALSE" EnforceUniqueValues="FALSE" List="Lists/Categories" ShowField="Title" UnlimitedLengthInDocumentLibrary="FALSE" Indexed="TRUE" RelationshipDeleteBehavior="Cascade" ID="{c59b5e18-54c2-408a-a231-b88c3e939e90}" SourceID="{$ListId:Lists/Products;}" StaticName="Category" Name="Category" RowOrdinal="0" />
I got this schema by doing an export as mentioned above. There are a few things you might notice here. The list name and SourceID refer to the lists by URL instead of a hardcoded Id. The value of Indexed is also set to true. This is because relational lists require indexed columns. You also get prompted to index the column when you set it up through the UI. The last thing to note is the RelationshipDeleteBehavior attribute. It can have a value of Cascade, Restrict, or none. This corresponds to relationship behavior you see in the UI screenshot above.
The additional fields from the parent list are also lookup fields. They just have a few key differences.
<Field Type="Lookup" DisplayName="Category:ID" List="Lists/Categories" WebId="8c918dd2-ef1f-4822-b3d2-f587d622d203" ShowField="ID" FieldRef="c59b5e18-54c2-408a-a231-b88c3e939e90" ReadOnly="TRUE" UnlimitedLengthInDocumentLibrary="FALSE" ID="{2264d6b5-7001-40e1-b295-236b9965c833}" SourceID="{$ListId:Lists/Products;}" StaticName="Category_x003a_ID" Name="Category_x003a_ID" />
<Field Type="Lookup" DisplayName="Category:Description" List="Lists/Categories" WebId="8c918dd2-ef1f-4822-b3d2-f587d622d203" ShowField="Description" FieldRef="c59b5e18-54c2-408a-a231-b88c3e939e90" ReadOnly="TRUE" UnlimitedLengthInDocumentLibrary="FALSE" ID="{a0ab273e-5989-42ba-950d-3add1c1025e0}" SourceID="{$ListId:Lists/Products;}" StaticName="Category_x003a_Description" Name="Category_x003a_Description" />
First, the FieldRef attribute has the Id of the first lookup field we defined. This is what ties them together. The second thing is that the ShowField attribute refers to the name of the field in the lookup list. The last thing to note is that they are marked as readonly. One more thing to point out is that the static name is encoded with x003a which is the color you see in the DisplayName. This is also used in the FieldRef elements you will also have on any ConentType element thats uses these fields in your schema.xml. For example.
<FieldRef ID="{c59b5e18-54c2-408a-a231-b88c3e939e90}" Name="Category" Required="FALSE" />
<FieldRef ID="{2264d6b5-7001-40e1-b295-236b9965c833}" Name="Category_x003a_ID" ReadOnly="TRUE" />
<FieldRef ID="{a0ab273e-5989-42ba-950d-3add1c1025e0}" Name="Category_x003a_Description" DisplayName="Category:Description" ReadOnly="TRUE" />
You might not see much value in this post yet, but I think you might in the future. The tools to work with SharePoint have improved greatly, but you still will need to work with some CAML from time to time I believe. You may not be building it from scratch any more, but it is still good to know your way around it. As a developer I often get frustrated when I run into posts showing how to do something through the UI when I really need to know how to build something declaratively or programmatically. Hopefully this will be helpful.
One of my favorite features in SharePoint 2010 is External Lists. If you haven’t heard by now, the MOSS 2007 Business Data Catalog (BDC) has become Business Connectivity Services (BCS) in SharePoint 2010. The ever better part is that it is included in SharePoint Foundation and does not require SharePoint Server. One new concept that we get from this is the External List. To the end user, these look like regular lists in SharePoint, however they really are reaching out to external systems such as databases and web services. Whereas the BDC only “officially” supported read operations, the BCS supports full CRUD operations on your external data source.
The title of the article mentions how easy it is to set up. As you will see shortly, it really is quite easy using SharePoint Designer. In fact, if you have a database table, you can have it exposed in SharePoint in under 10 minutes. If you worked with the BDC before, you know that we had to manipulate huge XML files that represented the application definition. We relied on third party tools to make this easier, but it was never a clean experience until now. Before we get started with SharePoint Designer though, first make sure that you have the Business Data Connectivity (likely to be renamed) Service Application started. To verify this go to Central Administration –> Manage Service Applications.
You can tell this is SharePoint Foundation since there are only a few Service Applications (SA). Ok, admittedly it could be server with very few installed, but you get the point. If you click the Manage button, you can manage any application definitions you have created already. Although, we’ll skip this since we’ll be using SharePoint Designer. If the SA is not started for some reason, go to Central Administration –> Services on Server and click Start next to the appropriate service. Also remember if you are using Windows Server 2008 R2, you will need to have the WCF hotfix installed before any SA works.
Now let’s take a look at our table. In my case I have a table of products with a few columns. I want to expose this table to SharePoint and make it editable.
Now open SharePoint Designer 2010 and connect to your SharePoint site. My server is called sp2010, so I would click open site and then enter http://sp2010. SharePoint Designer is redesigned and has a new item on the left-hand tab called External Content Types. This is where we want to go. Click the External Content Type button in the New section of the ribbon.
It will take a minute, but then you will get some details about your new External Content Type. Here is what mine looks like after I gave it a name of Products.
We’re then going to click the link Click here to discover external data sources and define operations. This is where we pick that we want to pull data from my database table. We can also use this to connect to a web service or talk to a custom .NET object.
I’m just going to go with the defaults on the next screen for my data connection. I gave it my database name of bcs_test as well as my SQL Server name. You can also configure out which identity is used here to talk to the database. Remember that you may need to grant permissions on the SQL Server itself for the appropriate user. It will then iterate your data source and display it to you. Pick out the table you want, and then right click on it. This gives you a list of operations you can add as you can see below.
As you can see, you can add individual operations such as Read Item, Read List, Create, Update, and Delete. You can also create an association if you have multiple tables related to each other. However, I want everything, so I am going to choose Create All Operations. This starts a wizard. The most important part for you will be the Parameters configuration. Here you set what field to show in a picker control as well as what the id is. Usually, you don’t need to configure much. Here I am setting that the name field should show up in the picker.
It automatically detects my primary key and maps it to the identifier for me.
You can also specify an Office Property to allow you to map things into existing types such as an Outlook Contact or Task. This lets you use Office to edit things directly in the BCS. I won’t cover that today though since that is a little bit more involved. The next step in the wizard allows you to do some filtering. In my case, I am skipping it.
When that is finished, go back to the main tab for your External Content Type and you should see something like this. It has the operations you have chosen as well as what fields are in the type.
Now, we want to save out External Content Type, by pressing the Save (Disk Icon) in the top left corner. On to the fun part. Let’s use SharePoint Designer to create an External List by using the Create Lists & Form button in the ribbon.
Clicking it you will get the following screen asking about your list. I’m calling my lists Products and I just use default values for the rest. If you are using server, you can click the Create InfoPath Form checkbox and you will be able to customize the InfoPath form for the list right there. Since we are using Foundation today, I’ll leave that unchecked.
You can also create the list directly through SharePoint as well. Once the list is created, go to your SharePoint site, navigate to the list, and you should see something that looks like this.
Comparing it to my table from the SQL Server, you can see that it matches. You can see that it looks a lot like a regular list in SharePoint. Now, what if I want to change the price on the Plush Bear? Not a problem. Click on the item and select edit, change the value in the form and save it.
Now what does the data in SQL look like?
It has the new price of course. You can also add and delete rows and perform bulk operations, but screenshots really don’t do it justice. It’s this simple to set up, you should go out and try it for yourself. One reminder, I will give you is that even though this looks like a list, it doesn’t always quite act like a list. You won’t find it in SPWeb.Lists for example. There are a also a number of other things that do and don’t work. Be on the lookout for a follow-up post on what you can and can’t do with an external list. This is a great new feature in SharePoint 2010 and I hope you will like it as much as I do.
One thing is a lot of confusing to new SharePoint developers is that there are so many ways to use the API to get to the same data. When it comes to getting or setting the value of a field on a list item, there is at least 2 ways I can think of immediately (we all know there is more). One way to do this is use SPListItem.Properties. This is a Hashtable that allow you to get and set values as you please. If all of your properties are strings, you are pretty safe to use this, however where you might start running into issues is when you start dealing with other types. If I remember correctly from an error I received once, when assigning to this, you can only use a string, int32, or DateTime. The problem is you never know what underlying type is going to be there when you try to assign it.
I ran into this problem again recently in an event receiver which was copying data from one list item to another. The code was for some simple metadata inheritance if you were curious. We had no issue copying strings. However, the source item thought property MyField was a string and the destination item thought property MyField was an int32. Here is what it looked like.
destionationItem.Properties["MyField"] = sourceItem.Properties["MyField"];
Now you might be thinking that you can just cast it or that I have some other issue because they should be the same type. This really wasn’t the case though. In my case, I had stored a value of 2, but when I retrieved the value I received back “2.0000000000”. This makes things quite a pain, since int.Parse can not handle that. All of this was just simply not worth the hassle.
The one thing you need to remember is that using the indexer on SPListItem is not the same as using SPListeItem.Properties. To repeat.
SPListItem[“MyField”] != SpListItem.Properties[“MyField”]
Both will return a type of object by default, but when you retrieve the value using the indexer you actually get the type of double (in my case above). Better yet, the type matches on both the source and the destination field. I then can simplify my code to copy the properties as shown below.
destinationItem["MyField"] = sourceItem["MyField"];
Now I am no expert on the SharePoint API by any means. I’m sure there might be a reason you might prefer using the Hashtable (maybe it performs better), but for now I am going to stay away from it. At the minimum, I’ll avoid it when I am copying data.