January 2008 - Posts
This error has been pissing me off for a long time. I have an environment that works great for remote debugging most of the time. However, every couple of days when I show up for work, I encounter the following error.
Unable to connect to the Microsoft Visual Studio Remote Debugging Monitor named 'DOMAIN\USER@MACHINENAME'. The Visual Studio Remote Debugger on the target computer cannot connect back to this computer. Authentication failed. Please see Help for assistance.
If you do a Google search, you will find that this is because the remote computer cannot connect back to the desktop for some reason. By the error it's obviously an authentication issue. Most articles point to making sure that the account the remote debugger is on also has access to the client machine. I am using the same account which is local administrator on both, so that is not the issue.
So how do I resolve this issue when it occurs? Reboot the client machine. It's annoying but it does fix the problem. I have tried restarting Visual Studio and other things and that simply does not work. The only thing that works for me is rebooting. Also note that this occurs for me in both Visual Studio 2005 and 2008
I tend to write the most about things that I am currently working with. Today I have been working with the Business Data Catalog (BDC) again and I thought I might point out how to configure a default action because it isn't very well documented. A default action is the URL, that will be used when a user clicks for details on the item (either in a BDC webpart or Enterprise Search results). I personally discovered it because I saw an attribute in an example somewhere that had nothing to do with this topic. First, I'll go back and explain how to create an action to begin with. Most BDC tools will do this for you now, but what does it actually emit in the XML? Also of note, a lot of tools will let you create the action but not set it as a default. Here is what typical XML looks like. It goes in the Entity element.
<Actions>
<Action Position="1" IsOpenedInNewWindow="false" Url="{0}" ImageUrl="" Name="Default">
<ActionParameters>
<ActionParameter Name="SiteUrl" Index="0" />
</ActionParameters>
</Action>
</Actions>
The URL allows you to inject the value of a column similar to that of String.Format() using {0} etc. This can be useful if you need to specify a different protocol besides http (i.e.: file://) or if you needed to construct the URL using multiple columns. To set up a default action, you need to take a note of what you named the action (in this case I called it Default). This is how you tell it which action to use as a default.
Now that we have recapped how to create an Action, we need to specify that its a default action. This is actually pretty easy. Create a new Property element in the Properties element of the Entity. Give it an attribute of DefaultAction and specify the name of the Action we defined earlier. Here is how it looks.
<Property Name="DefaultAction" Type="System.String">Default</Property>
When you import the application definition, you will receive a warning like the following.
Could not create profile page for Entity MyEntity. The error is: Default action exists for application 'MyInstance', entity 'MyEntity'. Profile page creation skipped.
This really isn't an error. It is just telling you that since you specified a default action, it did not automatically generate a profile page for the entity. That's all there is to it. Hopefully, more tools will start supporting it out of the box soon.
So by now, I can assume everyone has been listening to me and you are using partial trust in all of your SharePoint environments right? Ok, probably not, but for the two of you in the world that are, this post is for you. If you decide to use LINQ to SQL in any of your development, I have discovered that
ReflectionPermission is needed by LINQ to SQL. I don't know the exact permissions it needs yet, but if you are in a bind to get it to work in your partial trust you can use a line like the following. I try to avoid giving any permission unrestricted access, but until you find out what the underlying class actually needs, it is dificult. To get you by, use a line like this.
<
IPermission class="System.Security.Permissions.ReflectionPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
Also some more obvious permissions LINQ to SQL will need is SqlClientPermission and probably ConfigurationPermission.
I have been working with document libraries quite a bit lately and I ran into yet another task that seemed like it should be pretty easy but in fact there is a complexity. I needed to add my own content type to the document library which was made up of multiple custom site columns. This seemed pretty easy and in fact, there is actually some info in the SDK on it.
http://msdn2.microsoft.com/en-us/library/aa543576.aspx
Adding my own content types was pretty easy. You just add a ContentTypeRef element to the ContentTypes section which includes refences to your own content types in the schema.xml of your List Template. The key thing to remember is to prefix the GUID (after stripping out spaces) with the base content type (i.e.: 0x0120 + 00). Check out my previous posts for more info on that. Here is an example.
<ContentTypes>
<ContentTypeRef ID="0x01010058B73A6A27334D3B9355D7AAB899D332">
<Folder TargetName="Forms/Document" />
</ContentTypeRef>
<ContentTypeRef ID="0x012000521AACBC415A478390B668D81308E454" />
</ContentTypes>
Now, after I added that, I deployed an instance of my list template, and discovered that it had my new content types included. Awesome. Things are looking good. Well, maybe not. My content types were there, but the new inherited content types it created did not have any columns. Now, I had always known that when you create a list with custom content types and site columns, it makes a copy of said items at the list level and adds them. This is apparent when you configure one of these using the UI. However, when you are dealing with CAML, this is just not the case. After quite a bit of Google searching and not finding much (big surprise), I noticed in the documentation of the same page linked above, the following paragraph.
When Windows SharePoint Services provisions a list, it provisions only those columns declared in the base type schema of the list or in the list schema. If you reference a site content type in the list schema, and that content type references site columns that are not included in the base type schema of the list or in the list schema, those columns are not provisioned on the list. You must also include those columns in the list schema for Windows SharePoint Services to provision them on the list.
Ok, well that's lame. I knew what I needed to do now. I needed to add my site columns to the list template, but did not yet know the syntax and where to put the needed elements. I figured it would do this automatically, but I again tried to search Google and again came up with nothing. Sometimes, I would just like to see a complete example of how to do something (I'll try to post a complete example of setting up a list or document library in this manner some time). I finally figured it out. It matches the syntax of what you use to define your fields to being with.
<Field ID="{E059529E-2D14-4e52-A853-994DA83B76FA}" Type="Text" Name="MyCustomColumn" DisplayName="My Custom Column" Group="Custom Column Group" />
This is yet another thing that I figured would be simple in SharePoint but isn't. Hopefully, this will help someone else down the road when they are trying to do this. I really figured, SharePoint would add the columns automatically, but it doesn't. It just seems like this can cause a ton of issues later, because you have to remember to update each list if you ever make changes to your site columns. I really wish it actually used the site column itself instead of making a copy of it.
The people over at SharePointBlogs.com have graciously allowed me to have this blog mirrored on their site. If you haven't checked it out before, SharePointBlogs.com aggregates a ton of blogs about SharePoint and I am always finding useful information there. Go check it out today.
SharePointBlogs.com
This begins the first in my series of posts on how to do stupid simple things in SharePoint. I find that some things in SharePoint are really pretty easy but there is not much out there on Google. Some this content may be new, some may be repeated, and some of this may already be buried in the SDK documentation somewhere. Anyhow, my goal is to contribute as much information as I can to help out new SharePoint developers.
Today's topic is how to add a link to the Top Navigation bar of a site in SharePoint. Typical places to do this is in a Feature Receiver where you are adding new pages. Assuming you already have a reference to the current site through a SPWeb object named currentSite, here is how to do it. Create a New SPNavigationNode object with the title you want in the link and a URL. Typically, you would just specify a relative URL (i.e.: MyCustomPage.aspx). Before adding the node, be sure that the page is created. If you don't, it will throw an exception.
SPNavigationNode navigationNode = new SPNavigationNode("My Page Title", "MyPage.aspx");
currentSite.Navigation.TopNavigationBar.AddAsLast(navigationNode);
In this case I am using AddAsLast which puts it at the end, there are also methods to Add after a specific node or at the beginning.
Removing a node is just as easy, but there are some limitations. I would have expected the TopNavigationBar object's Delete method to accept a title or url, but it does not. It requires either deleting by index or a reference to a SPNavigationNode object. So what you do is very similar to adding.
SPNavigationNode navigationNode = new SPNavigationNode("My Page Title", "MyPage.aspx");
currentSite.Navigation.TopNavigationBar.Delete(navigationNode);
I'll be posting more random things like this over the next few weeks.
I have been doing a lot of document library work lately and in my work I had a need to create a series of subfolders in the document library using a particular custom content type. This meant I was going to need to write a little code. I decided to keep the list of folders i needed to create in an XML document since this could easily represent the hierarchy I needed. I also need to set some properties on the content type as well as implement security on a per-folder level.
Well thinking about how my XML document was going to work out I started dreading navigating the XML document using the traditional .NET APIs. Let's face it. They are not fun to use. Looking for a better alternative, I decided to use LINQ to XML. I had heard about it but had not read anything on it yet, so I decided to look into it more. LINQ to XML gives you the power to get IntelliSense into your XML document using anonymous types. This turned out to make things a ton easier. To get started I used an XML document similar to the one below. My final version had some additional elements in it for my security settings, but I omited those for this example.
<DocumentLibrary>
<Folder Name="Folder 1" ParentFolderType="" FolderType="">
<Folder Name="Subfolder 1" ParentFolderType="Blah" FolderType="Blah"></Folder>
<Folder Name="Subfolder 2" ParentFolderType="Blah" FolderType="Blah"></Folder>
</Folder>
<Folder Name="Folder 2" ParentFolderType="" FolderType=""></Folder>
<Folder Name="Folder 3" ParentFolderType="" FolderType=""></Folder>
</DocumentLibrary>
This document is pretty simple. In this case I have a couple of folders with some nested subfolders. I also have some attributes that I was using to set custom properties on the content type. First, I am going to open up my XML file using the XDocument class. It has a constructor which takes a path to a file or URL among other things. I then use the Root property of the XDocument and call .Elements with a parameter of Folders. It naturally matches up all elements named Folder. Elements will return all elements of a given node. If you wanted all elements in the entire tree that matched you could call Descendants.
The next part of the LINQ query creates a new anonymous type. In my type, I copy over the values of a few attributes as well as get all child elemnts named Folder to get a list of subfolders. This is what I use to do recursion. Lastly I assign RoleMappings, but first I check to see if the value exists first using Any(). That along with the use of ? and : are the proper way to check for nulls. Also of note I am using the new var type to receive a collection of the anonymous type (again don't confuse these with the old VB6 variants). While debugging, you can see that this is actually an IEnumberable<XElement>.
// get the xml document from the feature folder
XDocument documentLibraryXml = XDocument.Load(folderStructureFilename);
// iterate through the root folder elements
var folders = from folder in documentLibraryXml.Root.Elements("Folder")
select new
{
Name = folder.Attribute("Name").Value,
FolderType = folder.Attribute("FolderType").Value,
ParentFolderType = folder.Attribute("ParentFolderType").Value,
SubFolders = folder.Descendants("Folder"),
RoleMappings = folder.Elements("Security").Any() ? folder.Element("Security").Elements("RoleMapping") : null
};
At this point I have a nice anonymous type which provides full IntelliSense access to my XML. Now I just need to iterate on it. Inside my foreach, I call my methods to create my new SPFolder, set permissions, and then recursively call a method to create the subfolders. Things like folder.Name, folder.SubFolders all show up via IntelliSense.
So far I can say LINQ to XML has saved me a ton of time. This is a brief overview, ScottGu has a bit of info on LINQ to XML to help get you started. I think it is really powerful for any scenario where you are going to be working with a lot of XML. I didn't include any of the details about how I set security or created the folders themselves, but I have posted info on some of that in the past. If you need more info, feel free to contact me.
I have been messing around with LINQ (both with SQL and XML) and am pretty familiar with the query syntax right now, but I wasn't sure at first how to get a single object. It's actually pretty simple using a lambda expression. Assume I have a simple table called Products and it has fields Name and Id and we're looking for a product with an id of 45. After dragging the table into a new LINQ to SQL class, you can begin the process of querying it. Here is how it works.
ProductsDataContext dataContext = new ProductsDataContext();
Product myProduct
= dataContext.Products.Single(p => p.Id == 45)
That's all there is to it. Get a reference to your data context and then you will have intellisense into the Products table. The method Single takes a lambda expression. In this case p can be named whatever (except the name myProduct), and it again provides you intellisene into the Products table. This makes it really simple to get a single item when you are cowboy coding data access with LINQ to SQL.
I have been working a lot with document libraries again and in this particular implementation, I had a need to create a pre-determined set of folders upon document library creation that were based upon a custom content type. I couldn't find a way to do it with CAML (maybe I am just a noob), so I decided to take the programatic approach. I ran into a couple of issues along the way so I thought I would point them out. When I first started out, I attempted to just treat the document library like a list, however when you go to create the item that way, you will get an error telling you to use SPFileCollection. In this case, I was creating a folder, so that didn't really make sense. So I started by going with a folder collection.
For this example, assume we are just working out of the FeatureActivated EHM. We need to get a reference to the current site and then to the list. Once we have the list, we can get a SPFolderCollection. Make sure to get the folder collection off of RootFolder, because things will not work if you don't.
27 SPWeb currentSite = (SPWeb)properties.Feature.Parent;
28 SPFolderCollection documentLibrary = currentSite.Lists["My Document Library"].RootFolder.SubFolders;
Once you get a folder collection, creating the folder is pretty much the same except that you specify a ContentTypeId of your custom folder content type. Here is how it looks.
102 SPFolder folder = documentLibrary.Add("My Folder Name");
103
104 // set the content type id and update it, so that the proper attributes are present
105 folder.Item["ContentTypeId"] = "0x012000521AACBC415A498390B668D81308E454";
106 folder.Update();
The main thing here is to set the ContentTypeId. As a reminder the base type for a folder is 0x0120 which you have to follow by 00 and a GUID. Most likely, you already have that set up when you built the custom content type, so you can just cut and paste it. This may seem like a simple topic, but as usual with Google, there was not much out there on it. Hopefully, this will help others as they try to do this.
Am I the only one that is annoyed with the lack of ability to export things out of SharePoint? Sure you can hack an export using code, or something but these are things that should be there out of the box to simplify deployment. The first thing I can think of is List Templates. So SharePoint gives you this great user interface to build custom lists, make use of your own content types and site columns, but there is no good way to export it. Yes, you can export it into some freaky binary .stp file, but I want to be able to export it out into something I can deploy as a feature and store in source control. Site Columns and Content Types are the same way. You have an interface to create them, but you can't export them in a decent manner. So after you create them in the UI, you have to go back and start writing CAML and hope you get it built the same way. Before you comment, I know there are ways to get around these things, but my point is that it is just way too dificult.
Another issue I have commented on in the past is that of exporting Enterprise Search settings. There is no way out of the box to do this. You either have to do a backup/restore on a SSP, use a 3rd party utility like SSSPPC, or do it manually. I am sorry, but I don't want to recreate 20 content sources manually every time, I deploy a server in a new environment.
SharePoint is decent product to work with, but Microsoft has really dropped the ball on making these type of things easy to deploy. Luckily the SharePoint community has picked up the slack in a lot of these areas and come out with some useful tools to do these type of things.
The other day, I had a need to read an XML file during the FeatureActivated event of a SharePoint FeatureReceiver. I needed to know what the path of the feature was so that I could read the file. There are probably multiple ways to do this, but here is one that I stumbled upon that actually worked. I made use of the SPUtility class that has some helper methods to get paths. Here's the code.
string featurePath = string.format(@"{0}"\FEATURES\{1}",
SPUtility.GetGenericSetupPath("Template"),
properties.Definition.DisplayName);
That seems to work for me so far. The SPUtility class gets the path to the template folder and the DisplayName property off of Definition returns the folder name of the feature. If anyone else has a better way to do it, be sure and leave a comment.
I have found a lot of people still don't know too much about this, so I thought I would post on it. Setting aside the reason why you are using stored procedures, if you run into a case where you need to have one with optional parameters that end up affecting a WHERE clause, here is how you do it. In the past, I've seen many examples where people end up creating strings with ad-hoc SQL and then executing them with an EXEC command. Although ad-hoc isn't considered a terrible thing necessarily any more by some groups, it is defintiely not as clean, and is more prone to run-time errors. The COALESCE operator works by returning the first argument that is not null.
So lets assume we had an optional parameter called @ProductId. You would construct your where clause like the following.
SELECT ProductName FROM Products
WHERE ProductId = COALESCE(@ProductId, ProductId)
So what happens here is that if @ProductId is null, the COALESCE returns ProductId, so you would have ProductId = ProductId, which always evaluates true. Since its true it does not affect the ultimate output of the SELECT statement. If @ProductId isn't null, it would return ProductName that matched that one particular id.