I am new to Sharepoint and currently I want to write a function where I can read and write data to Sharepoint pages. This is the structure of the URL I want to connect to read and write data: https://mycompany.sharepoint.com/sites/SubSite/SubSite1/SitePages/Home.aspx
Then, I open the connection to it by using:
ClientContext clientContext = new ClientContext("https://mycompany.sharepoint.com/sites/SubSite/SubSite1/");
clientContext.Credentials = new SharePointOnlineCredentials("user#mycompany.com", password);
I use SecureString for the password
Then, I load site pages in to a list and read each field to examine:
List colList = clientContext.Web.Lists.GetByTitle("Site Pages");
FieldCollection colfield = colList.Fields;
IEnumerable<Microsoft.SharePoint.Client.List> resultCollection = clientContext.LoadQuery(
clientContext.Web.Lists.Include(
list => list.Title,
list => list.Id));
clientContext.Load(colList);
clientContext.Load(colfield);
clientContext.ExecuteQuery();
Then I loop through every field to examine
for(int i = 1; i <= 4; i++){
ListItem lst = colList.GetItemById(i);
foreach (Field field in colfield){
clientContext.Load(field);
clientContext.ExecuteQuery();
var val = lst[field.Title];
}
}
I figured out that there are some fields in colList, such as: DataSource, etc. containing this exception:
The property or field has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested
My question is how can I properly connect to Sharepoint site pages to read and write data in them?
You are iterating through all the fields of colList but including only Id & Title fields in
clientContext.Web.Lists.Include(
list => list.Title,
list => list.Id));
That's why you are getting above exception.
You have to Include all the fields you want to retrieve OR just load the list clientContext.Load(colList); and don't Include anything if you want to retrieve all the fields.
Related
I need to create an item with Person/People field.
I am able to populate fields like Title, DateTime, TextField as seen below, but I am not able to populate Person field at all.
I have tried sending Display Name, User ID and UPN.
var fieldValueSet = new FieldValueSet();
fieldValueSet.AdditionalData = new Dictionary<string, object>();
fieldValueSet.AdditionalData.Add("Title#odata.type", "Edm.String");
fieldValueSet.AdditionalData.Add("Title", task.Title);
fieldValueSet.AdditionalData.Add("Plan#odata.type", "Edm.String");
fieldValueSet.AdditionalData.Add("Plan", plan.Title);
fieldValueSet.AdditionalData.Add("Assigned#odata.type", "Edm.String");
fieldValueSet.AdditionalData.Add("Assigned", assignee.UserPrincipalName);
fieldValueSet.AdditionalData.Add("DueDate#odata.type", "Edm.String");
fieldValueSet.AdditionalData.Add("DueDate", task.DueDateTime);
var listItem = new ListItem
{
Fields = fieldValueSet
};
await graphServiceClient.Sites["SPURL:/sites/ITOddeleni:"].Lists["ListName"].Items
.Request()
.AddAsync(listItem)
Does your user principal name look like an email? (e.g. some.person#yourtenant.onmicrosoft.com). If that is the case then you should probably try to convert it to a sharepoint-claim version: i:0#.f|membership|some.dude#yourtenant.onmicrosoft.com.
If you have an authenticated ClientContext in your app, then you could resolve the username by calling the SharePoint utility:
Utility.ResolvePrincipal(context, context.Web, userEmail, PrincipalType.User, PrincipalSource.All, null, true);
If you don't have a context then you can try and combine strings (not as robust, but should work as well).
This answer suggests that you should try and set the LookupId value for the field (sorta the same way you set user values using the SharePoint's REST api):
var assigneeLookupId = // Get the assignee's user id on the site (wssid)
fieldValueSet.AdditionalData.Add("AssignedLookupId#odata.type", "Edm.Int32");
fieldValueSet.AdditionalData.Add("AssignedLookupId", assigneeLookupId);
The following schema would allow you to insert multiple person values:
SOLUTION
After help from comments i got this answer which worked
{
"AssignedLookupId#odata.type" : "Collection(Edm.Int32)"
"AssignedLookupId": [1,2,3]
}
This approach is in line with the way MS Graph returns values for Lookup fields.
I have a list called "Books" with columns 'Name','AuthorName','ISBN' with type as text. Now I have another list called "BillTokenStore" and i want to add lookup column 'AuthorName' in "BillTokenStore". Below is what i have done.
using (ClientContext context = new ClientContext(webFullUrl: siteUrl))
{
context.Credentials = new SharePointOnlineCredentials(userName, GetPassWord());
Web web = context.Web;
ListCollection listCollection = web.Lists;
List list = listCollection.GetByTitle("BillTokenStore");
string schemaLookupField = #"<Field Type='Lookup' Name='InStock' StaticName='InStock' DisplayName='InStock' List = 'Books' ShowField = 'Title' /> ";
Field lookupField = list.Fields.AddFieldAsXml(schemaLookupField, true, AddFieldOptions.DefaultValue);
context.ExecuteQuery();
}
When i run this code, i am getting the error "value does not fall within the expected range sharepoint 2013". What is wrong here? Thanks in Advance.
Note: I am able to achieve the same thing thorough UI. I am also able to add other type of fields like choice,boolean and all through code.
You need to explicitly load the list and the fields of that list.
Also, we need to pass the GUID of the lookup column list.
Please try the below modified code:
using (ClientContext context = new ClientContext(webFullUrl: siteUrl))
{
context.Credentials = new SharePointOnlineCredentials(userName, GetPassWord());
Web web = context.Web;
List booksList = context.Web.Lists.GetByTitle("Books");
List list = context.Web.Lists.GetByTitle("BillTokenStore");
context.Load(list, l => l.Fields);
context.Load(booksList, b => b.Id);
context.ExecuteQuery();
string schemaLookupField = #"<Field Type='Lookup' Name='InStock' StaticName='InStock' DisplayName='InStock' List='"+ booksList.Id +"' ShowField = 'Title' />";
Field lookupField = list.Fields.AddFieldAsXml(schemaLookupField, true, AddFieldOptions.DefaultValue);
lookupField.Update();
context.Load(lookupField);
context.ExecuteQuery();
}
In SharePoint Online, I'm having problems setting a site Managed Metadata column default value using CSOM (C#). For brevity, I've not included exception handling in my code. Here's what I have:
// Code snippet for what I'm trying to achieve
using (ClientContext ctx = NewCtx(SiteInfo.Url)) // NewCtx is just a static member I use to return a ClientContext object
{
Field taxColumn = (Field)ctx.Web.Fields.GetByTitle("myMMColumnName");
ctx.Load(taxColumn);
ctx.ExecuteQuery();
TaxonomyFieldValue termField = new TaxonomyFieldValue();
termField.Label = "My MM Term";
termField.TermGuid = "b269aef7-6f47-4b02-bf80-7edfb7166a30";
termField.WssId = -1;
taxColumn.DefaultValue = value;
// Place holder for added code (see below)
taxColumn.Update();
ctx.Load(taxColumn);
ctx.ExecuteQuery();
}
The value appears in the UI under Site Columns, but no associated item is created in the TaxonomyHiddenList, which makes sense because setting the lookup WssId to "-1" doesn't resolve and therefore, the default value doesn't have a lookup and appears empty in the list- and library-level column.
The closest resolution I found was an article (albeit not CSOM and linked below) suggested creating a "dummy" list item (against a list that was consuming the MM column). This would trigger the creation of the item in the TaxonomyHiddenList but the article suggests not committing the "dummy" item creation. I've tried this too, but seems to have no effect. This is the code I added to the place holder mentioned in the previous code block:
List myList = ctx.Web.Lists.GetByTitle("My List");
ctx.Load(myList);
ctx.ExecuteQuery();
TaxonomyField taxField = ctx.CastTo<TaxonomyField>(myList.Fields.GetByTitle("myMMColumnName"));
ListItemCreationInformation newItemInfo = new ListItemCreationInformation();
ListItem newItem = myList.AddItem(newItemInfo);
newItem["myMMColumnName"] = termField;
taxField.SetFieldValueByValue(newItem, termField);
The article uses the .SetFieldValue method, which isn't available in CSOM so I may well have assumed incorrectly that .SetFieldValueByValue is comparable.
Thanks for reading!
References:
Article - http://sharepointificate.blogspot.com/2014/04/setting-managed-metadata-column-default.html
SetFieldValue Method belongs to TaxonomyField class, it means the line:
Field taxColumn = (Field)ctx.Web.Fields.GetByTitle(taxFieldTitle);
needs to be replaced with:
var taxColumn = ctx.CastTo<TaxonomyField>(ctx.Web.Fields.GetByTitle(taxFieldTitle));
or
TaxonomyField taxColumn = ctx.CastTo<TaxonomyField>(ctx.Web.Fields.GetByTitle(taxFieldTitle));
Once TaxonomyField is initialized, the default value could be set as shown below:
//get taxonomy field
var taxColumn = ctx.CastTo<TaxonomyField>(ctx.Web.Fields.GetByTitle(taxFieldTitle));
ctx.Load(taxColumn);
ctx.ExecuteQuery();
//initialize taxonomy field value
var defaultValue = new TaxonomyFieldValue();
defaultValue.WssId = -1;
defaultValue.Label = termLabel;
defaultValue.TermGuid = termId.ToString();
//retrieve validated taxonomy field value
var validatedValue = taxColumn.GetValidatedString(defaultValue);
ctx.ExecuteQuery();
//set default value for a taxonomy field
taxColumn.DefaultValue = validatedValue.Value;
taxColumn.Update();
ctx.ExecuteQuery();
Note: TaxonomyField.GetValidatedString method is utilized for
validation of taxonomy field value which in turn includes the
resolving of WssId value
Update
Use Field.UpdateAndPushChanges method to propagate changesto all lists that use the field
So, replace:
taxColumn.Update();
ctx.ExecuteQuery();
with:
taxColumn.UpdateAndPushChanges(true);
ctx.ExecuteQuery();
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();
I'm sure this is possible, I just haven't got the foggiest how or where to start.
So, I have an option set created in MS Dynamic CRM which gives me a list of countries, MyCountryOptionSet. Lovely.
However, I have a number of other .net, c# applications where users can enter free text. This is not so lovely.
As such, I would like to tie these down so only countries in present in MyCountryOptionSet can be used.
Therefore, I would like to bind the countries from MyCountryOptionSet to a drop down list in my .net applications.
How would I go about doing this?
Look into RetrieveAttributeRequest and IOrganizationService. This will allow you to get the countries defined in your option set. By casting the AttributeMetadata property on the response to PicklistAttributeMetadata.
The binding to controls will be UI technology specific so post more info.
use the crm apis to get option values as follows...
_serviceProxy = new OrganizationServiceProxy(serverConfig.OrganizationUri, serverConfig.HomeRealmUri,
serverConfig.Credentials, serverConfig.DeviceCredentials);
_serviceProxy.EnableProxyTypes();
_service = _serviceProxy;
RetrieveAttributeRequest retrieveAttributeRequest =
new RetrieveAttributeRequest
{
EntityLogicalName = EntityLogicalName,
LogicalName = optionSetLogicalName,
RetrieveAsIfPublished = true,
};
// Execute the request.
RetrieveAttributeResponse retrieveAttributeResponse =
(RetrieveAttributeResponse)_service.Execute(
retrieveAttributeRequest);
// Access the retrieved attribute.
PicklistAttributeMetadata retrievedPicklistAttributeMetadata =
(PicklistAttributeMetadata)
retrieveAttributeResponse.AttributeMetadata;
// Get the current options list for the retrieved attribute.
OptionMetadata[] optionList =
retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
//Dictionary<int,string> LocalizedLabelDic = new Dictionary<int,string>();
List<ListItem> OptionSetItems = new List<ListItem>();
foreach (OptionMetadata o in optionList)
{
OptionSetItems.Add(new ListItem(o.Label.LocalizedLabels.FirstOrDefault(e => e.LanguageCode == 1033).Label.ToString(), o.Value.Value.ToString()));
}
then bind the listitem generic collectoin to ur asp.net drop down
You can query the CRM database directly for the values using a query like this:
select Value, AttributeName
from StringMap
where AttributeName = 'New_MySet' --schema name of the global option set
and ObjectTypeCode = 1 --changes based on entity type field is associated with
Once you have the results you can bind them to the list in your application.