SharePoint CAML Query: Value does not fall into expected range - c#

I've created the following method in a class which is intended to fetch all items in a SharePont list and load them into my entity:
public List<ItemEntity> FetchItems(SPList list)
{
// build the CAML query of field names that we wish to retreive
var query = new SPQuery
{
ViewFields = string.Concat("<FieldRef Name='Modified' />",
"<FieldRef Name='Modified By' />",
"<FieldRef Name='Created' />",
"<FieldRef Name='Created By' />")
};
SPListItemCollection items = list.GetItems(query);
return (from SPListItem item in items
select Load("", // item id
"", // content type
"", // display name
"", // name
"", // title
"", // url
"", // author
"", // editor
Convert.ToDateTime(item["Modified"]), // date time modified
item["Modified By"].ToString(), // modified by
Convert.ToDateTime(item["Created"]), // date time created
item["Created By"].ToString() // created by
)).ToList();
}
For some reason that I don't understand it's throwing the following error:
Value does not fall within the expected range.
I thought this could be something to do with the results returned by my CAML query, but even then I restricted it down to meta data fields (which I believe should exist on every file) and unfortunately I'm still receiving the error. Where am I going wrong?

I believe you are missing the correct internal names of some built-in fields.
Try to use:
item["Author"].ToString() instead of item["Created By"].ToString() and
item["Editor"].ToString() instead of item["Modified By"].ToString()
For a full reference of sharepoint 2010 internal field names I usually consult the following link:
http://sharepointmalarkey.wordpress.com/2010/10/12/sharepoint-internal-field-names-sharepoint-2010/

Related

Argument Exception - Value does not fall within the expected range

I am trying to get a column value from a SharePoint list and populate it to an ASP text box. I used SPQuery to filter and get the value. I even mentioned view fields and increased the List View Lookup Threshold. But when I am trying to assign the text box value with the column field, I am getting this exception:
Argument exception was unhandled by user- value does not fall within the expected range.
Is there any workaround for this? Code sample:
SPQuery qrySeriesDesc = new SPQuery();
qrySeriesDesc.Query = "<Where><Eq><FieldRef Name='Series'/><Value Type='Text'>" + SeriesNumber + "</Value></Eq></Where>";
qrySeriesDesc.ViewFields = "<FieldRef Name='Series Description'/>";
SPListItemCollection itemCol = list.GetItems(qrySeriesDesc);
foreach (SPListItem item in itemCol)
{
if (item != null)
{
tboxSeriesDescription.Text = item["Series Description"].ToString();
}
}
I am getting the mentioned exception at:
tboxSeriesDescription.Text = item["Series Description"].ToString();
Try to get it from field, not from item:
SPField fieldSerDesc = item.Fields.GetFieldByInternalName("SeriesDescription"); //internal name of your fields. Usually eq StaticName.
tboxSeriesDescription.Text = item[fieldSerDesc.Id].ToString();
Or, if your field is lookup for example, you can do it like this:
SPFieldLookup fieldSerDesc = (SPFieldLookup)item.Fields.GetFieldByInternalName("SeriesDescription");
tboxSeriesDescription.Text = fieldSerDesc.GetFieldValueAsText(item[fieldSerDesc.Id]);
}
You get the error because the field do not exist or is misspelled.
Please note that if you select a column that does not exist SharePoint does not raise any error.
Try to check the field's name using a tool like Sharepoint Manager and use ALWAYS the internal name

SharePoint get ContentType Name

I am trying to get All Folders and Files from a SharePoint library, executing a single Request.
CamlQuery query = new CamlQuery();
query.ViewXml = "<View Scope='RecursiveAll' />";
var libraryName = "Specific Documents";
ListItemCollection itemsRaw = clientContext.Web.Lists.GetByTitle(libraryName).GetItems(query);
clientContext.Load(itemsRaw);
clientContext.ExecuteQuery();
This code works well, and as result I have a list of All Folders and Files within the specified library.
It seems that the files details are loaded in a lazy manner. Only the first level from details hierarchy. But I don't know how, the FieldValues collection is filled with Data.
I see that the ListItem ContentType.Name is not initialized.
Is it possible somehow to update the query in a manner which will load the data for ContentType in this single call.
Or the only possibility is to iterate through all files and do a load of ContentType for the specific file?
I did this in the following way:
foreach(var listItem in listItemCollection)
{
context.Load(listItem, k => k.ContentType);
context.ExecuteQuery();
var contentTypeName = listItem.ContentType.Name;
}
But I am going to get this information in a single call, If it is possible, without iterating in the collection and starting multiple calls to ClientContext.
P.S.: I am new to SharePoint programming. I just want to fix a bug.
Thanks!
As you correctly noticed in SharePoint Client Side Object Model (CSOM) ClientRuntimeContext.Load Method does not retrieve all the properties for client object.
ClientRuntimeContext.Load Method has the following syntax:
public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject
where retrievals parameter is intended for specifying properties that have to be retrieved.
Secondly, since SharePoint CSOM supports Request Batching, your example could be modified to this one:
foreach (var item in items)
{
ctx.Load(item, i => i.ContentType);
}
ctx.ExecuteQuery();
Note: request is submitted to the server only once in this example
But still the provided example requires two requests to the server:
retrieve list items
retrieve content type for list items
and it could be improved from performance perspective by reducing requests to the server till one.
Final example
The example demonstrates how to retrieve list items and specify explicitly which properties to retrieve:
var listTitle = "Documents";
var query = new CamlQuery();
query.ViewXml = "<View Scope='RecursiveAll' />";
var items = ctx.Web.Lists.GetByTitle(listTitle).GetItems(query);
ctx.Load(items,icol => icol.Include(
i => i.ContentType,
i => i.FieldValues));
ctx.ExecuteQuery();
The final example does not work (in SP2010).
There is an exception "The query expression is not supported"
If you explicitly states all required fields then the solution below works.
var listTitle = "Documents";
var query = new CamlQuery();
query.ViewXml = "<View Scope='RecursiveAll' />";
var items = ctx.Web.Lists.GetByTitle(listTitle).GetItems(query);
string[] fieldsToMigrate = new string[] { "Title", "FieldA", "FieldB" };
ctx.Load(items, a => a.Include(b => b.ContentType, b => b["FileRef"]));
foreach (var f in fieldsToLoad) {
ctx.Load(items, includes => includes.Include(a => a[f]));
}
ctx.ExecuteQuery();

Sharepoint list Dynamic Linq query

I need to query a list in SharePoint where the columns may be added in the future.
For instance at the moment I have the following columns
Name, Job, interests, address
I want to be able to query this string dynamically using a parameter from the browser so if columns are added in the future I don’t have to change the code but just the parameter.
The address may look like this www.contoso.com/sites/mypage.aspx?property=Interests
And the code something on the line of this:
var SiteParameter = Request.QueryString["property"];
var ItemsFromList = from item in ListItems where item[try to put the parameter in here] select item;
I use SPmetal to get the list details, so if I press item. Visual Studio2010 will return the columns within the list.
This may be easier without SPMetal.
var qy = new SPQuery();
qy.Query =
"<Where><Eq>" +
"<FieldRef Name=`" + siteParameter + "'/>" +
// You may have to worry about the type of the field here, too.
"<Value Type='Text'>" + desiredValue + "</Value>" +
"</Eq></Where>";
var itemCollection = myList.GetItems(qy);

GetListItems times out when I include a query

I'm trying to call Lists.GetListItems with a CAML query. It works fine when I don't include a query parameter, but I get a time-out error when I include the query. Assuming that my web service is called TestWebService, code is:
public static void GetListItemsTest()
{
try
{
string listName = "TestList";
string[] fields = { "ID", "Title" };
string queryStr =
"<GroupBy collapse='true'>" +
"<FieldRef Name='" + fields[1] + "' />" +
"</GroupBy>" +
"<OrderBy>" +
"<FieldRef Name='ID' Ascending='False' />" +
"</OrderBy>";
XmlDocument xmlDoc = new System.Xml.XmlDocument();
XmlElement query = xmlDoc.CreateElement("Query");
XmlElement viewFields = xmlDoc.CreateElement("ViewFields");
query.InnerXml = queryStr;
viewFields.InnerXml = "";
TestWebService.Lists lists = new TestWebService.Lists();
lists.Url = "http://wss/sites/TestWebService/_vti_bin/lists.asmx";
lists.Credentials = System.Net.CredentialCache.DefaultCredentials;
XmlNode responseNode = lists.GetListItems(listName, null, query, viewFields, "1000000", null, null);
Console.WriteLine("ran successfully");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
Console.ReadLine();
}
}
The call to GetListItems hangs, then throws a WebException with the message "The operation has timed out." If I change the query.InnerXml assignment to:
query.InnerXml="";
then GetListItems successfully returns a value for responseNode. So it must have something to do with the CAML query fragment.
On further investigation, I found that the GroupBy element is the source of the problem; with the OrderBy element commented out, the query times out, and with the GroupBy element commented out, the query succeeds. I'm wondering if GroupBy can't handle large data sets, or it handles them too slowly; this set has about 20,000 elements, with generally not more than 5 or 6 duplicates of each Title value. Would LINQ be more applicable in this situation? Or is there a way to do this with straight CAML?
With 20,000 items on a SharePoint 2010 system you might risk getting an SPQueryThrottledException. A typical user is limited to 5000, and a few privileged users to 20,000 items returned by a query.
Have you set up an index on the Title column? This might improve performance on the GroupBy. Otherwise I suspect that the query forces the retrieval of all items, and then groups them. Try grouping them, and retrieving them in pages, rather than all at once.
I don't know it it's entirely relevant, but I did just blog about queries on large lists.

How do I access the XML data in my column using LINQ to XML?

I have this XML in a column in my table:
<keywords>
<keyword name="First Name" value="|FIRSTNAME|" display="Jack" />
<keyword name="Last Name" value="|LASTNAME|" display="Jones" />
<keyword name="City" value="|CITY|" display="Anytown" />
<keyword name="State" value="|STATE|" display="MD" />
</keywords>
I'm getting a record out of that table using LINQ to SQL via this:
GeneratedArticle ga = db.GeneratedArticles.Single(p => p.GeneratedArticleId == generatedArticleId);
That works, I get my GeneratedArticle object just fine.
I'd like to walk through the data in the ArticleKeywords field, which is XML. I started doing this:
var keywords = from k in ga.ArticleKeywords.Elements("Keywords")
select k;
foreach (var keyword in keywords)
{
//what goes here?
}
I'm not 100% sure that I'm getting that data correctly. I need help with the proper syntax to get the value and display out of my XML field.
Once again I am amazed that people don't even try their answers and people still up vote when they don't work.
The .Elements will get the list of elements at the current root, which is not a keyword.
Also the one using .Attributes["X"] does not even compile you need to use () of course again it would be operating on each instance of "keywords" not "keyword"
You could use
var keywords = from kw in ga.ArticleKeywords.Element("keywords").Elements()
or
var keywords = from kw in ga.ArticleKeywords.Element("keywords").Elements("keyword")
or (this will get all the keyword elements regardless of level)
var keywords = from kw in ga.ArticleKeywords.Descendants("keyword")
Here is a sample code:
To read keywords we need to call Elements("keyword") not Elements("keywords") since keywords is a root node.
// IEnumerable sequence with keywords data
var keywords = from kw in ga.ArticleKeywords.Elements("keyword")
select new {
Name = (string)kw.Attribute("name"),
Value = (string)kw.Attribute("value"),
Display = (string)kw.Attribute("display")
};
foreach (var keyword in keywords)
{
var kw = "Name: " + keyword.Name +
" Value: " + keyword.Value +
" Display: " + keyword.Display;
Console.WriteLine(kw);
}
You can get attribute value using foo.Attribute("bar").Value, but this method would throw exception if attribute is missing. Safe way to get attribute value is (string)foo.Attribute("bar") - it will give you null if attribute is missing
I would think something like this would work
var keywordData = from k in ga.ArticleKeywords.Elements("Keywords")
select new { Value = k.Attributes["value"].Value,
Display = k.Attributes["display"].Value};
This would give you an IEnumerable of an anonymous type containing a Value and a Display property. Depending on what your doing you could easily replace the anonymous type with a concrete type.

Categories

Resources