April 2008 - Posts

After you query something with LINQ, you may want to know if any results were returned before your start enumerating or working with the EnumerableRowCollection (that's most likely the type behind that var you are using in most cases).  Consider the following example.

DataTable myDataTable = new DataTable();

 

var queryResults = from queryResult in myDataTable.AsEnumerable()

                   where queryResult.Field<DateTime>("ItemDateTime") < DateTime.Now

                   select new

                   {

                       intColumn1 = queryResult.Field<int>("IntColumn1"),

                       stringColumn1 = queryResult.Field<string>("StringColun1")

                   };

At first you might try something like this.

if (queryResults != null)

However this will just tell you if the collection is null (and it seems that the collection always has some value regardless of the results of the query).  There are two ways that I can think of off the top of my head to check for this.  First, you can use the Count() method.

if (queryResults.Count() > 0)

If you don't like that, you can also use the more eloquent Any() method.  I've mentioned this method in the past as a way to check for nulls.  Any() can be used to check to see if just about anything exists including elements, attributes, and rows.  Here is what it looks like.

if (queryResults.Any())

{

    var result = queryResults.First();

 

    Console.WriteLine("Results: {0}", result.intColumn1);

}

The reason I bring this up is that, you have to check if something exists in the collection before calling the First() method.  Although, you really don't have to check to see if rows exists before enumerating this collection, you do have to use it before calling First() because it will throw an exception if no rows exist.  Btw, First() returns the first item in the EnumerableRowCollection if that wasn't obvious already.

with no comments
Filed under: ,

The other day, I had an issue importing User Profiles and Properties from Active Directory.  In this case my MOSS server is on a child domain and it could import the child domain's objects, but it could not import the objects from the parent domain.  Checking the log, I saw an error like the following.

spsimport://domain?$$dl$$

The specified domain either does not exist or could not be contacted. (Exception from HRESULT: 0x8007054B)

I knew I had an issue with how the connections to the Active Directory was configured.  On the Manage Connections page, I had one entry for the child domain and one for the parent domain as expected.  After examining the parent domain's connection carefully, I noticed that it auto discovered the domain name incorrectly.  In this scenario, the NT domain name is actually something like DOMAIN.COM and the Active Directory name for the domain is domain.local.  The name MOSS populated via Auto Discovery was just domain.  To resolve this, I created a new connection (because you can't modify the domain of an existing one).  In this new connection, I simply added domain.local as my domain name and used default values for everything else.  I deleted the original connection and did the import again.  After checking the logs, I saw that everything imported correctly.

with no comments
Filed under: ,

If you are like me, you go to the SharePoint logs folder often in an attempt to try and resolve various errors you may encounter when working with SharePoint.  I noticed on some of my servers, that I was receiving an entry similar to the one below in my log file.

04/17/2008 10:45:52.99 wsstracing.exe (0x06BC)                        0x2498  ULS Logging                              Unified Logging Service              uls1      Monitorable       Tracing Service lost trace events.  Current value 13.         

I did a quick Google search and was unable to find an answer.  I did however notice that people did encounter this error after upgrading to SP1.  This was definitely also the case with these particular servers.  I got lucky and the solution to solving this is simple.  I just restarted the Windows SharePoint Services Tracing and the Windows SharePoint Services Administration services.  Once I did that, a new log was created and I was getting all of my trace information again.

with 1 comment(s)
Filed under: , ,

When you deactivate a feature that deployed a web part, it is a good idea to remove that web part from the gallery.  This makes things much more likely to work when you upgrade said web part later.  Like everything in SharePoint, this task is more complicated than it needs to be.  You would think you could just call myWeb.WebParts.Delete("mywebpart.dwp"), but of course that is not the case.  That would be too easy.

To work with the web parts in the gallery, you first have to know where they are.  The Web Part Gallery is just a list, so it can be manipulated just like any other list.  To get the list, we use the GetCatalog method on the SPWeb object.  It takes an enum parameter of SPListTemplateType.WebPartGallery.

SPList webPartGallery = currentSite.GetCatalog(SPListTemplateType.WebPartCatalog);

Now we need to delete the web part from the gallery.   Unfortunately, there is neither an indexer that uses the web part's name, nor a find method.  This means we get to loop through the collection until we find the item we are looking for.  This provides for another great place to use an Extension Method.  I have found these to be highly useful to make up for the shortcomings in the SharePoint API.

public static class ExtensionMethods

{

    public static void Remove(this SPList myList, string key)

    {

        for (int i = 0; i < myList.Items.Count; i++)

        {

            if (string.Compare(myList.Items[i].Name, key, true) == 0)

            {

                myList.Items[i].Delete();

                return;

            }

        }

    }

}

The code is simple.  Just loop through the collection and compare the name property to a key parameter on the method.  The key parameter will contain the name of the web part's filename (i.e.: mywebpart.dwp).  To use the extension method, it would be called as in the sample below.

using (SPWeb currentSite = SPContext.Current.Web)

{

    SPList webPartGallery = currentSite.GetCatalog(SPListTemplateType.WebPartCatalog);

    webPartGallery.Remove("mywebpart.dwp");

}

This seems like a lot of code to be able to do something that should only take one line of code, but that is what is required.  My collection of SharePoint Extension Methods is getting huge. 

with 2 comment(s)
Filed under: ,

Recently, I wrote my own control to allow drilldown on Facets for one of our Enterprise Search pages, and someone pointed out to me that it never returns more than 50 results.  It was then I remembered seeing a RowLimit property on the KeywordQuery and FullTextSqlQuery classes.  I first checked the SDK to see if it mentioned a default.  It of course had not mention of anything.  I decided to set a value on the RowLimit property and in fact it did allow more results to be returned.  This is easy enough to fix, I just wish this had been documented somewhere (or maybe it is and I just never found it).

In my last post, I talked about how to assign a permission level to a SharePoint group.  However, you may want to create your own custom permission level to suit your needs.  For example, you may want a permission that allows used to add, edit, and view items, but not delete them.  To create a custom permission level, first remember that it is the SPRoleDefinition class that we will use.  You then just need to get a reference to the site you want to add the permission level to and then use the default constructor.  Take a look at the code below.

using (SPWeb currentSite = SPContext.Current.Site.RootWeb)

{

    // create a new role definition and set base permissions

    SPRoleDefinition roleDefinition = new SPRoleDefinition()

    {

        Name = "Custom Permission Level",

        BasePermissions = SPBasePermissions.AddListItems | SPBasePermissions.OpenItems |

            SPBasePermissions.ViewListItems | SPBasePermissions.EditListItems

    };

 

    // add the role definition

    currentSite.RoleDefinitions.Add(roleDefinition);

}

After using the default constructor, I am using property initializers to set the name of the permission level and the rights.  The BasePermissions property is an enum of type SPBasePermissions.  This enum uses a flag attribute, so you simply add permissions using a pipe delimiter.  You can assign them manually like this or in my case I read the value out of an XML file as a ulong and cast it to the type of the enum.  Once you have everything set on your role definition, add it to your site using the RoleDefinitions collection on the SPWeb object.

Deleting a permission level is also pretty easy.  Just call the delete method with the name of the permission level.

currentSite.RoleDefinitions.Delete("Custom Permission Level");

with 3 comment(s)
Filed under:

Earlier this week, I covered how to add a SharePoint Group to a Site Collection, but I did not cover how, you set the permission level of that group.  It's one thing to get the group added, but of course the people in that group are going to need some sort of permission to the site or site collection.  The first thing we need to do is translate one of the terms that we see in the UI to what they are called in the API.  The first thing is Permission Level.  This defines the set of permissions that can be assigned to a user or group on the site (i.e.: Read, Contribute, Full Control, etc.).  It represents a set of permssions, not just a single permission.  You can create your own permission levels as well, which I will cover in a future post.  In the API, this is called a RoleDefinition.  You can view what RoleDefinitions have been defined for your site by going to Site Settings -> Advanced Permissions -> Settings -> Permission Levels. 

To assign a permission level, we have to make use of the SPRoleAssignment class.  You get a SPRoleAssignment class by passing it a user or group  in its constructor.  This class has a collection called RoleDefinitionBindings to which you add the RoleDefinitions.  Once you assign the permission levels (or RoleDefinitions) to this RoleAssignment class, you update the site.  Here is what the code looks like.

SPRoleAssignment roleAssignment = new SPRoleAssignment(currentSiteCollection.RootWeb.SiteGroups["My Site Group"]);

 

roleAssignment.RoleDefinitionBindings .Add(currentSiteCollection.RootWeb.RoleDefinitions["Read"]);

 

currentSiteCollection.RootWeb.RoleAssignments.Add(roleAssignment);

currentSiteCollection.RootWeb.Update();

In this case, I am assigning the read permission level to the group.  The reason I cover this today is that the way you assign the permission level to a group is not entirely obvious at first.  You would think you would just get a reference to the group and add the permissions to a collection.

with 4 comment(s)
Filed under:

Debugging a feature activation is relatively easy (even remotely), you simply attach to the w3wp.exe process that is running your particular SharePoint instance.  However, I have found that if you need to debug a feature that is activated with stsadm.exe, there is an extra step required.  The reason is that you need to not only attach to the w3wp.exe process but you also need to attach to stsadm.exe as well.  Of course, if you execute your stsadm command, then jump into Visual Studio and try to attach to the process, stsadm has probably already completed by the time you get there.  How do you get around this?  Well I can think of two ways.  One is cheesy but effective and one is a little more elegant. 

The Pause/Break Key

Yes, this old key from back in the DOS days (well probably earlier), can be your friend here.  First start out in Visual Studio and attach to the appropriate w3wp.exe process.  Next, go to your command prompt, run your feature activation command and quickly press the Pause/Break key.  The stsadm command will be paused.  Now, go back into Visual Studio and attach to the stsadm.exe process.  Once you are attached, go back to the command prompt and press any key and you will be good to go.  This is pretty cheesy as I said, but it does work pretty well.

System.Diagnostics.Debugger.Launch()

If you want a more elegant solution, you can launch the debugger via code in your feature receiver.  Simply, add the following code to get started.

System.Diagnostics.Debugger.Launch();

When you activate the feature, a Visual Studio dialog will pop up asking you where you want to debug your process, just choose the Visual Studio project that contains your source code (assuming you already have Visual Studio open with that project).  This is also the technique I use when I need to debug custom stsadm commands.  Once you are done debugging, you will of course want to remove the line launching the debugger.

with 8 comment(s)
Filed under: ,