Social Icons

twitter google plus linkedin rss feed

Pages

23.8.13

Scopes in a CAML Query

I have been working for quite a while now with CAML queries and the scope is always something very important to bear in mind. How many times my queries returned nothing when I was sure they should bring back something…

Basically we have two modifiers Recursive and All and nothing, could we call nothing a modifier? All will bring back folders and files. Recursive will repeat the query in all the folders under the one we are working with.

If you don’t set the scope to All it will only bring files. If you don’t set it to recursive it will only retrieve items from the folder you are at. There are not that many variants so let’s make an example of each.

Let’s imagine we have a SharePoint folder like this one and we want to query it:

CamlScopeTreeSample

I am not good at paint, I know but what I want to show here is a tree where we have a Root folder (the root of the queries) and two sub-folders with files. For each scope possible I’ll highlight what you can expect to retrieve.

Just before we start, allow me to remind you that the scope is set in the property ViewAttributes of the SPQuery item.

To add a bit more of clarity I have also painted the levels:

CamlScopeTreeSampleLevels

The green line marks what’s inside the root folder, the blue line marks the contents of SubFolder1 and the red line SubFolder2.

ViewAttributes left by default:

CamlScopeByDefault
This is just the files under the root folder.

ViewAttributes = "Scope='Recursive'"

CamlScopeRecursive

This means all the files in all the folders.

ViewAttributes = "Scope='All'"

CamlScopeAll

This scope will bring folders and files under root.

ViewAttributes = "Scope='RecursiveAll'"

CamlScopeRecursiveAll

And finally with RecursiveAll you can bring back everything under the root entity.

Good luck with your queries.

No comments:

Post a Comment

19.8.13

SharePoint Client Object Model Is Great

This is one of those things that you know they are there but never use because you already know a different way.

Just four years after I started working with SharePoint 2010 I thought “Why not giving the Client Object Model a go?” actually I had a requirement from a client. and I must say I am VERY impressed. The simplicity, the speed and the predictability is pretty good for the standards we are used to.

Even though the COM is very good I have created a series of methods, wrappers and extensions to help me deal with my most common functions. Some of them translated from the methods I use in the SharePoint Object Model and some of them new. Let’s begin.

It is really easy to connect using Windows authentication or Forms Based Authentication. Easier than anything else I have seen to date.

public static ClientContext GenerateClientContextWinAuth(string URL)
{
    return new ClientContext(URL);
}

public static ClientContext GenerateClientContextFBAAuth(string URL, string userName, string password)
{
    ClientContext ctx = new ClientContext(URL);
    ctx.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
    ctx.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo(userName, password);

    return ctx;
}

Windows auth 1 line of code FBA 3. Nice.

Do you remember how it was reading and writing files to SharePoint? Look how easy it is using the COM.

public static string ReadFile(ClientContext Context, string ListName, string FileName)
{
    List StorageList = Context.Web.Lists.GetByTitle(ListName);
    string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, FileName);

    FileInformation fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(Context, SharePointFilePath);

    using (fileInfo.Stream)
    {
        using (StreamReader sr = new StreamReader(fileInfo.Stream))
        {
            return sr.ReadToEnd();
        }
    }
}

public static void WriteFile(ClientContext Context, string LocalFilePath, string ListName, string SPFileName)
{
    List StorageList = Context.Web.Lists.GetByTitle(ListName);

    string SharePointFilePath = GetFilePathInSharePoint(Context, StorageList, SPFileName);

    using (FileStream fs = new FileStream(LocalFilePath, FileMode.Open))
        Microsoft.SharePoint.Client.File.SaveBinaryDirect(Context, SharePointFilePath, fs, true);
}

private static string GetFilePathInSharePoint(ClientContext ctx, List StorageList, string FileName)
{
    if (StorageList.RootFolder.ServerObjectIsNull != false)
    {
        ctx.Load(StorageList.RootFolder);
        ctx.ExecuteQuery();
    }

    string ListRootFolderURLDocuments = StorageList.RootFolder.ServerRelativeUrl;
    return Path.Combine(ListRootFolderURLDocuments, FileName);
}

Other than the small function to find the url of the item in the document list using folders it's as simple as it can be. I have also created a couple of methods for making easier to work with the StratexFramework.

If you are creating some program and want to use some of the code feel free. If you are not working with StratexPoint feel free to modify it to suit your environment.

Here's a method to bring back the root entity of the framework:
public static ListItem GetRootEntity(this List ComList)
{
    CamlQuery camlQuery = new CamlQuery();
    camlQuery.ViewXml = string.Format(@"<View>
                                            <Query>
                                                <Where>
                                                    <Eq>
                                                        <FieldRef Name='ContentType'/>
                                                        <Value Type='Choice'>Entity</Value>
                                                    </Eq>
                                                </Where>
                                                <RowLimit>1</RowLimit>
                                            </Query>
                                        </View>");

    ListItemCollection items = ComList.GetItemsExecuted(camlQuery);

    if (items.Count == 1)
        return items[0];
    else
        return null;
}

Nice and easy.

The query is a bit different to the usual we do in the SPQuery, but still very similar.

Then I have created a method that will help when you want to run a query under a folder regardless of the rest of the framework. First I will paste the helper query and then I will paste the samples on how to use it:

public static CamlQuery CreatePositionedQuery(this ListItem StartEntity)
{
    CamlQuery camlQuery = new CamlQuery();
    camlQuery.ViewXml = string.Format(@"<View Scope='RecursiveAll' >
                                            <Query>
                                                <Where>
                                                    <And>
                                                        <Eq><FieldRef Name='FileDirRef' /><Value Type='Text'>{0}</Value></Eq>
                                                        {1}
                                                    </And>
                                                </Where>
                                            </Query>
                                        </View>", GetChildrenFolder(StartEntity), "{0}"); //This is the folder url and the placeholder for the real query.


    return camlQuery;

}

private static string GetChildrenFolder(ListItem startItem)
{
    startItem.InitializeIfNeeded("FileDirRef");
    startItem.InitializeIfNeeded("FileLeafRef");

    return startItem["FileDirRef"] + "/" + startItem["FileLeafRef"];
}

With this we basically are saying SharePoint to execute the query under the folder of the item we are passing as a parameter. And we can use this code easily to position our queries in the folder tree.

First one function to get all the child entities under a given entity, and then another function to bring an item with a given title inside a given folder:

public static ListItemCollection GetChildEntities(ListItem StartEntity)
{
    List ComList = StartEntity.ParentList;

    CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
    camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
                                    @"<And>
                                        <Eq><FieldRef Name='State' /><Value Type='Choice'>Live</Value></Eq>
                                        <Eq><FieldRef Name='ContentType' /><Value Type='Choice'>Entity</Value></Eq>
                                        </And>");

    return ComList.GetItemsExecuted(camlQuery);
}

public static ListItem GetChildItem(this ListItem StartEntity, string Title)
{
    List ComList = StartEntity.ParentList;

    CamlQuery camlQuery = CreatePositionedQuery(StartEntity);
    camlQuery.ViewXml = string.Format(camlQuery.ViewXml,
                                    string.Format("<Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq>", Title));

    ListItemCollection result = ComList.GetItemsExecuted(camlQuery);

    if (result.Count > 0)
        return result[0];
    else
        return null;
}
Creating a new item is also easy. The hardest thing to do is deciding if it's a folder or a leaf:
public static ListItem CreateItemUnder(this ListItem ParentItem, string Title, string ContentType, bool isLeaf)
{
    ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
    itemCreateInfo.FolderUrl = GetChildrenFolder(ParentItem);
    itemCreateInfo.LeafName = Title;

    if (isLeaf)
        itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.File;
    else
        itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder;

    ListItem NewItem = ParentItem.ParentList.AddItem(itemCreateInfo);

    if (ContentType != null)
        NewItem["ContentTypeId"] = GetContentType(ParentItem.ParentList, ContentType).Id;

    return NewItem;
}

A small shortcut to execute queries:


public static ListItemCollection GetItemsExecuted(this List listToQuery, CamlQuery query)
{
    ListItemCollection childItems = listToQuery.GetItems(query);

    listToQuery.Context.Load(listToQuery);
    listToQuery.Context.Load(childItems);
    listToQuery.Context.ExecuteQuery();

    return childItems;
}

In some cases the field you want to read didn't come in the execution maybe because you were a bit too restrictive with the viewfields clause... You can try with this trick:


public static void InitializeIfNeeded(this ListItem item, string InternalName)
{
    if (!item.FieldValues.ContainsKey(InternalName))
    {
        item.Context.Load(item);
        item.Context.ExecuteQuery();
    }
}

Are you trying to get a content type from a List? Easy.


public static ContentType GetContentType(List list, string cTypeName)
{
    list.Context.Load(list.ContentTypes);
    list.Context.ExecuteQuery();

    foreach (ContentType ctype in list.ContentTypes)
    {
        if (ctype.Name == cTypeName) return ctype;
    }

    return null;
}

Are you trying to save a SPFieldUserValue in a SPFieldUser using the client object model or read it back? Complex, I would say unnecessarily complex, but I have one last trick:

public static string UserToString(object fieldUserValue)
{
    if (fieldUserValue == null) return string.Empty;

    FieldUserValue user = fieldUserValue as FieldUserValue;

    return string.Format("{0};{1}", user.LookupId, user.LookupValue);
}

public static FieldUserValue StringToUser(object fieldUserValueString)
{
    FieldUserValue user = new FieldUserValue();

    if (string.IsNullOrEmpty(fieldUserValueString.ToStringSafe())) return user;

    string[] tokens = fieldUserValueString.ToStringSafe().Split(';');

    user.LookupId = tokens[0].ToNullableInt() ?? 0;

    return user;
}

The client object model has been really powerful and really easy to work with for me so far. I really encourage you to give it a try if you haven't already.

No comments:

Post a Comment

9.8.13

The process wlms.exe has initiated the power off of computer

I was getting random shutdowns of one of my virtual machines hosted in azure... I thought it was something related to azure but no.

I had a look to the shutdown log and found out this:

Log Name:      System
Source:        USER32
Date:          08/08/2013 17:39:04
Event ID:      1074
Task Category: None
Level:         Information
Keywords:      Classic
User:          SYSTEM
Computer:      COMPUTERNAME.local
Description:
The process wlms.exe has initiated the power off of computer COMPUTERNAME on behalf of user NT AUTHORITY\SYSTEM for the following reason: Other (Unplanned)
 Reason Code: 0x0
 Shutdown Type: power off
 Comment: 
Event Xml:
<event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <system>
    <provider name="USER32">
    <eventid qualifiers="32768">1074</eventid>
    <level>4</level>
    <task>0</task>
    <keywords>0x80000000000000</keywords>
    <timecreated systemtime="2013-08-08T16:39:04.000000000Z">
    <eventrecordid>147067</eventrecordid>
    <channel>System</channel>
    <computer>COMPUTERNAME.local</computer>
    <security userid="S-1-5-18">
  </security></timecreated></provider></system>
  <eventdata>
    <data>wlms.exe</data>
    <data>COMPUTERNAME</data>
    <data>Other (Unplanned)</data>
    <data>0x0</data>
    <data>power off</data>
    <data>
    </data>
    <data>NT AUTHORITY\SYSTEM</data>
  </eventdata>
</event>

A fast googling told me that in order to stop the VM from shutting down I needed to activate it :)

No comments:

Post a Comment