SharePoint CSOM Document Title throws error - sharepoint

In my first outing with csom and the document library, I encountered a difficult error when attempting to access a document library object property as file.File.Title using the following code.
The error is
Object reference not set to an instance of an object on server.
The code shown is what "works" for my exercise.
What step am I missing to be able to use file.File.Title instead of file.FieldValues["Title"].
The code snippet is a very primitive attempt to get a list of files in the default Document library folder. In a following iteration, I need to update the caml to retrieve a specific file.
var lib = ctx.Web.DefaultDocumentLibrary();
ctx.Load(lib);
ctx.ExecuteQuery();
var files = lib.GetItems(CreateAllFilesQuery());
ctx.Load(files);
ctx.Load(files, items => items.Include( item => item.File.Title ));
ctx.ExecuteQuery();
foreach(var file in files )
{
if(!(file.FieldValues["Title"] == null) )
{
string FileName = file.FieldValues["Title"].ToString();
if (FileName == DocumentName)
return true;
}
}
public static CamlQuery CreateAllFilesQuery()
{
var qry = new CamlQuery();
qry.ViewXml = #"<View Scope=\'FilesOnly\'>
<Query></Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ContentType' />
<FieldRef Name='DocIcon' />
</ViewFields>
</View>";
return qry;
}

After further research, I discovered that my caml query was at fault. The syntax error was placing slashes around the Scope value. The corrected query allows me to reference file.File.Title as expected. The additional benefit is that I am only getting back files rather than files and folders.
qry.ViewXml = #"<View Scope='FilesOnly'>
<Query></Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ContentType' />
<FieldRef Name='DocIcon' />
</ViewFields>
</View>";

Related

Why is my CAML query failing when it contains <Query>?

I have a list with a lot of documents (> 5000). I need to retrieve one particular document from it. I use this query:
#"<View>
<Query>
<Where>
<Contains>
<FieldRef Name=""FileLeafRef""/>
<Value Type=""Text"">MyDocumentName</Value>
</Contains>
</Where>
</Query>
<RowLimit>1</RowLimit>
</View>"
And I get a ServerException stating that this operation is forbidden because it goes over the list treshold.
But if I remove the <Query> tag:
#"<View>
<RowLimit>1</RowLimit>
</View>"
It works, and retrieve a single file, which sadly isn't the one I wanted, obviously.
So I'm puzzled here. Why is it behaving like that?
Try
Value Type=File
#"<View>
<Query>
<Where>
<Contains>
<FieldRef Name=""FileLeafRef""/>
<Value Type=""File"">MyDocumentName</Value>
</Contains>
</Where>
</Query>
<RowLimit>1</RowLimit>
</View>"
If you don't like building the query by hand then a couple of good tools are:
- U2U CAML Query Builder which gives you an UI for building the query.
- CAML.NET which is a library for safely building CAML queries
After eventually finding the right keywords to ask Google, I found this question.
The problem is that no field in my library is indexed, therefore when I try to add a query with conditions, all the library has to be read through in order to find the document(s) I need. So obviously, if there is too many documents, I get the exception.
<RowLimit> doesn't prevent this behavior, it only acts on the result of the query.
That being said, there is one field that is automatically indexed: ID. So we can change our query a bit to manually page the documents whose ID are between two values, and simply put the query in a while loop, incrementing the current ID paging each time, like so:
ListItemCollection items = null;
const int paging = 2000;
int currentPaging = paging;
while (items == null || items.Count == 0 || currentPaging < 100000)
{
var query = new CamlQuery
{
ViewXml = $"<View><Query><Where><And><Contains><FieldRef Name='FileLeafRef' /><Value Type='File'>MyDocumentName</Value></Contains><And><Leq><FieldRef Name='ID' /><Value Type='Counter'>{currentPaging}</Value></Leq><Gt><FieldRef Name='ID' /><Value Type='Counter'>{currentPaging - paging}</Value></Gt></And></And></Where></Query></View>"
};
items = list.GetItems(query);
ctx.Load(items);
ctx.ExecuteQuery();
currentPaging += paging;
}
Here's the formatted XML used in the query (query.ViewXml), for better readability:
<View>
<Query>
<Where>
<And>
<Contains>
<FieldRef Name='FileLeafRef' />
<Value Type='File'>MyDocumentName</Value>
</Contains>
<And>
<Leq>
<FieldRef Name='ID' />
<Value Type='Counter'>{currentPaging}</Value>
</Leq>
<Gt>
<FieldRef Name='ID' />
<Value Type='Counter'>{currentPaging - paging}</Value>
</Gt>
</And>
</And>
</Where>
</Query>
</View>

The given key is not present in the dictionary

Hi I am trying to fetch the accounts from CRM 2011. I am fetching the data in the EntityCollection . But when I am trying to read or access data from entityCollection it displayed first record but throwing an error after that record. Kindly have a look to below code and suggest me.
string fetch2 = #"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='account'>
<attribute name='name' />
<attribute name='address1_city' />
<attribute name='primarycontactid' />
<attribute name='telephone1' />
<attribute name='accountid' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='accounttype' operator='eq' value='01' />
</filter>
</entity>
</fetch>";
try
{
EntityCollection fxResult = _service.RetrieveMultiple(new FetchExpression(fetch2));
foreach (var e in fxResult.Entities)
{
Console.WriteLine("Id:{0},Name:{1},City:{2}", e.Attributes ["accountid"].ToString(), e.Attributes["name"].ToString(), e.Attributes["address1_city"].ToString());
// Console.WriteLine("Id:{0},Name:{1},City:{2}", e.ToEntity["accountid"]);
}
}
catch (Exception e)
{
Console.WriteLine("Error:==" + e.Message);
}
Before access an attribute you need to ask if it is in the context:
e.Attributes.Contains("address1_city")
If the collection contains the attribute, then you can access it safe.
string accountid = (string)e.Attributes["address1_city"]
The reason the attribute doesn't come in the collection it's because it is null or you are not retrieving it. In this case maybe, one of your attributes is null. Maybe address1_city.
When retrieving attribute values of late-bound Entity objects, the recommended approach is to use method getAttributeValue<T>. When the attribute is not present in the entity's attribute collection, it returns default(T).
The primary key ('id') of the record is always present when it is returned by the OrganizationService.
So your code should look like this:
EntityCollection fxResult = _service.RetrieveMultiple(new FetchExpression(fetch2));
foreach (var e in fxResult.Entities)
{
Console.WriteLine(
"Id:{0},Name:{1},City:{2}",
e.Id,
e.GetAttributeValue<string>("name"),
e.GetAttributeValue<string>("address1_city"));
}
You can safely use the item selector when you need to assign a value to an attribute, regardless if it is already present or not.
E.g. the following code line is valid:
e["name"] = "Demo Accountname";

Caml Query- Order By- SharePoint 2013-CSOM

I am new to caml query and have been struggling for this.
I need the last modified List Item. Only one Item.
That means it should be orderby 'modified' and rowlimit should be 1.
But only rowlimit part of my query is working. Not the orderby part.
This is my Query :
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name=\'Modified\' Ascending="FALSE"/></OrderBy><RowLimit>1</RowLimit></Query></View>";')
I dont know where I am going wrong.
I even tried removing the Query tags in the above mentioned query.
The query is working, its getting only one record. orderby isnt working i believe.
This is in jQuery. I have written in a function and am calling that function in my Ready function.
Please help me.
Thanks.
In fact, in your example the query returns all results since query contains some errors.
The line:
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name=\'Modified\' Ascending="FALSE"/></OrderBy><RowLimit>1</RowLimit></Query></View>";')
should be replaced with:
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name="Modified" Ascending="FALSE"/></OrderBy></Query><RowLimit>1</RowLimit></View>');
Example: how to get the last item
function getLastItem(listTitle,Success,Error){
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var query = new SP.CamlQuery();
query.set_viewXml('<View><Query><OrderBy><FieldRef Name="Modified" Ascending="FALSE"/></OrderBy></Query><RowLimit>1</RowLimit></View>');
var items = list.getItems(query);
ctx.load(items);
ctx.executeQueryAsync(
function() {
if(items.get_count() > 0) {
var item = items.getItemAtIndex(0);
Success(item);
}
else
{
Success(null);
}
},
Error
);
}
getLastItem('Contacts',function(item){
console.log(item.get_item('Modified'));
},function(sender,args){
console.log(args.get_message());
});
Rowlimit must be put outside the query. Actually I'm using SharepointPlus (a JavaScript API to deal with Sharepoint) that creates automatically the query for me :-)
So the XML code sent to the server should look like this:
<GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<listName>Your list</listName>
<viewName></viewName>
<query>
<Query>
<OrderBy>
<FieldRef Name="Status" Ascending="false"></FieldRef>
</OrderBy>
</Query>
</query>
<rowLimit>1</rowLimit>
<viewFields>
<ViewFields Properties="True">
<FieldRef Name="ID"></FieldRef>
</ViewFields>
</viewFields>
<queryOptions>
<QueryOptions>
<DateInUtc>False</DateInUtc>
<Paging ListItemCollectionPositionNext=""></Paging>
<IncludeAttachmentUrls>True</IncludeAttachmentUrls>
<IncludeMandatoryColumns>False</IncludeMandatoryColumns>
<ExpandUserField>False</ExpandUserField>
<ViewAttributes Scope="Recursive"></ViewAttributes>
</QueryOptions>
</queryOptions>
</GetListItems>
Here there is a full sample, Ascending='True' or Ascending='False' it's case sensitive
<View>
<Query>
<Where>
<Eq>
<FieldRef Name='OrderNum' />
<Value Type='Number'>90696</Value>
</Eq>
</Where>
<OrderBy>
<FieldRef Name='Modified' Ascending='False'/>
</OrderBy>
</Query>
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='ID' />
</ViewFields>
<RowLimit>10</RowLimit>
Sharepoint tips
AymKdn is correct, the OrderBy clause should be outside of the Query clause in your Caml. Here's an example:
caml.set_viewXml('<View><OrderBy><FieldRef Name="Modified" Ascending="False"/></OrderBy><RowLimit>1</RowLimit></View>');
The Query clause is used for filtering (Where clause).

Determine Largest Files In a Windows Sharepoint Services Site Collection

Is there any way to determine which files stored in my site collection that are the largest? I would like to identify them somehow and then delete them to free up some space.
Thanks for any ideas.
You could use a site data query to find all documents that are bigger than a certain size:
SPWeb web = SPContext.Current.Web;
SPSiteDataQuery query = new SPSiteDataQuery();
// restrict to document libraries
query.Lists = "<Lists ServerTemplate=\"101\" />";
query.ViewFields = "<FieldRef Name=\"File_x0020_Size\" />";
query.RowLimit = 20;
query.Query = "<Where><Geq><FieldRef Name=\"File_x0020_Size\" /><Value Type=\"Number\">{size in bytes}</Value></Geq></Where><OrderBy><FieldRef Name=\"File_x0020_Size\" Ascending=\"FALSE\" /></OrderBy>";
DataTable bigFiles = web.GetSiteData(query);
Query in readable:
<Where>
<Geq>
<FieldRef Name="File_x0020_Size" />
<Value Type="Number">{size in bytes}</Value>
</Geq>
</Where>
<OrderBy>
<FieldRef Name="File_x0020_Size" Ascending="FALSE" />
</OrderBy>
You have to use SharePoint API and iterate through all items in all lists in all sites in your site collection.

filter items by Site name and List Name using SPSiteDataQuery

I am listing files from multiple folders using the below code:
SPSiteDataQuery q = new SPSiteDataQuery();
q.Lists = "<Lists BaseType='1'/>";
q.Query = "<Where> <Where>
<And><Neq><FieldRef Name='FSObjType'/><Value Type='Lookup'>1</Value></Neq>
<Contains><FieldRef Name='ProjectProperty.Title' /><Value Type='Text'>Site_Name_Value_From_TextBox</Value></Contains>
</And>
</Where></Where>";
q.Webs = "<Webs Scope='Recursive' />";
q.ViewFields = "<FieldRef Name='ID' /><FieldRef Name='LinkFilename' /><FieldRef Name='File_x0020_Type' /><FieldRef Name='Title' /><FieldRef Name='FileRef' /><ListProperty Name='Title' /><ProjectProperty Name='Title' />";
the code is working fine for listing items, but i need to filter items by Site name and Library Name, i used
<Contains><FieldRef Name='ProjectProperty.Title' /><Value Type='Text'>Site_NAme</Value></Contains>
but i get no results, when i use Title or FileRef or any other field filter is working fine, what should i use instead of "ProjectProperty" and "ListProperty "??
Try
<FieldRef Name="SiteName" /><Value Type='Text'>Site_Name</Value>
or
<FieldRef Name="ows_SiteName" /><Value Type='Text'>Site_Name</Value>

Resources