.NET Graph SDK Updating Sharepoint Online List Item Values - c#

I'm trying to add values to a custom column on a list item after uploading the list item to the list. I can get the item into the list, and I can query the list and get back the item's data, but when I try to add the data for the extra field I get the following Microsoft.SharePoint.Client.InvalidClientQueryException error:
A value without a type name was found and no expected type is available.
When the model is specified, each value in the payload must have a type which can be either
specified in the payload, explicitly by the caller or implicitly inferred from the parent value.
I'm not sure what value or model the error message is referring to. This is my code:
var item = await graphClient
.Drives[driveId]
.Root.ItemWithPath(fileName)
.ListItem.Request()
.Select("WebURL,Fields,SharepointIds")
.Expand("Fields")
.GetAsync();
var fieldVals = await graphClient
.Sites[SPUrl + ":"]
.Sites[SpPath + ":"]
.Lists[libId]
.Items[item.SharepointIds.ListItemId]
.Fields
.Request()
.GetAsync();
fieldVals.AdditionalData.Add("Phase",
JsonConvert.SerializeObject(tags));
await graphClient
.Drives[driveId]
.Root
.ItemWithPath(fileName)
.ListItem
.Fields
.Request()
.UpdateAsync(fieldVals);
Originally when I was doing fieldVals.AdditionalData.Add() I had "Phase" and a List(string) and that caused an error about the OData field type not being set but I haven't found anywhere in the documentation that says what expected OData field values are. I tried setting it to microsoft.graph.fieldValueSet but that didn't work.
I'm trying to update a Choice column that allows multiple choices as checkboxes.

For multi-choice field type, indeed, the presence of odata.type annotation is mandatory in request payload, here is an example how to specify it:
PATCH https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items/{item-id}/
{
"fields": {
"<ChoiceFieldName>#odata.type": "Collection(Edm.String)",
"<ChoiceFieldName>":["<val1>","<val2>"]
}
}
where
ChoiceFieldName - choice field name
val1, val2 - field values
Example
Assuming a List contains a choice field named Categories, then the following example demonstrates how to update list item via msgraph-sdk-dotnet:
var choiceVals = new []{ "Cat1", "Cat2"};
await graphClient.Sites[siteId].Lists[listId].Items[itemId].Request().UpdateAsync(new ListItem()
{
Fields = new FieldValueSet
{
AdditionalData = new Dictionary<string, object>
{
{ "Categories#odata.type", "Collection(Edm.String)" },
{ "Categories", choiceVals }
}
}
});
References
Update an item in a list
Entity Data Model: Primitive Data Types

Related

.NET Graph SDK Updating Choice Column values in Sharepoint Online

I would like to update the value of a "choice column" but when I call the UpdateAsync method it throws an exception with the following message "Code: invalidRequest - Message: Invalid request".
In previous versions of Sharepoint, the values of the choice columns were separated by ";#" characters, but with Microsoft Graph and Sharepoint Online it seems that this requirement has changed to an array of values. At least I think so...
Any ideas on how to solve this problem?
I am using the following code:
var fieldValueSet = new FieldValueSet
{
AdditionalData = new Dictionary<string, object>()
{
{"Field1", "Test1"},
{ "Field2", ["Test2-A", "Test2-B", "Test3-C"]}
}
};
await graphClient
.Sites["{site-id}"]]
.Lists["{list-id}"]]
.Items["{listItem-id}"]
.Fields
.Request()
.UpdateAsync(fieldValueSet);```
You need to add a field to specify that the value for Field2 is an array of strings. Collection(Edm.String).
Also send the array of values in string "[\"Test2-A\",\"Test2-B\",\"Test3-C\"]"
var fieldValueSet = new FieldValueSet
{
AdditionalData = new Dictionary<string, object>()
{
{"Field1", "Test1"},
{"Field2#odata.type", "Collection(Edm.String)"},
{"Field2", "[\"Test2-A\",\"Test2-B\",\"Test3-C\"]"}
}
};

botbuilder v 4, dynamic adaptive card with dropdown and capturing values on prompt

I'm using ms botbuilder v 4
I'm using webcontrol, webchat.js, latest, react
Case is pretty trivial:
I want to show list of possible values in dropdown, values will be dynamic (comes from API, i need Titles and Values (Ids) there. Then when user selects some item and clicks OK i want to get value (Id) and work further with that.
As i got it for now only way to show dropdown is using adaptive cards, in v3 there was an option to use adaptive cards in prompts and it also planned for next version: https://github.com/Microsoft/botbuilder-dotnet/issues/1170
But for now only woraround for that is exaplained here:
https://github.com/Microsoft/botbuilder-dotnet/issues/614 , with just list of string everything's working fine, but if i want to store keyvalue pairs (for IDs) i'm not able to do that cos Choices in PromptOptions only accepts list of string (will show below). So only workaround i'm using now is to store whole collection of values and after getting the result go and find it's id. Is there more convinient solution for that?
Here's the code:
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Value}).ToList();
var card = new AdaptiveCard
{
Version = new AdaptiveSchemaVersion(1, 0),
Body =
{
new AdaptiveTextBlock("Select a team to assign your ticket"),
new AdaptiveChoiceSetInput
{
Choices = choicesInputs,
Id = "setId",
Style = AdaptiveChoiceInputStyle.Compact,
IsMultiSelect = false
}
},
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Ok",
Type = "Action.Submit"
}
}
};
signInPhoneState.Teams = _teams;
return await stepcontext.PromptAsync(
"SelectGroupCardDialog",
new PromptOptions
{
Choices = ChoiceFactory.ToChoices(_teams.Select(pair => pair.Value).ToList()),
Prompt = (Activity) MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card))
})
},
cancellationtoken);
// . . .
var selectedTeamId = signInPhoneState.Teams.FirstOrDefault(pair => pair.Value == sel).Key;
Quick side question (but related in terms i'm using it for workaround):
What is the easiest way to persist some variable though dialog? If i remember correectly In v3 it was as simple as marking a value as public and marking dialog as serializable and that's it, now as i get it you need to create special accessor for each dialog, dublicate property there and manage the state of it, is it correct?
Thanks
You have a dictionary with team ID's as keys and team names as values. You are using the team names as the values for an adaptive choice set that's being used in a prompt, and in the turn after the prompt you're extracting the team ID from the dictionary using the team name. You want a more convenient option.
Option 1: If you're okay with your current setup of keeping the dictionary available
When accessing the data in a dictionary, it is more efficient to access a value using a key than the other way around. That is what dictionaries are for, after all. So instead of using the team names as values in your choice set, you could use team ID's.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Key }).ToList();
// . . .
signInPhoneState.Teams.TryGetValue(sel, out string selectedTeamName);
This would mean that if the dictionary is being drawn from some external source that's subject to change, the team name would be as up-to-date as possible.
Option 2: If you don't want to depend on the dictionary for the next turn
You could store both the team ID and the team name in the choice's value.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = JsonConvert.SerializeObject(s) }).ToList();
// . . .
var pair = JsonConvert.DeserializeObject<KeyValuePair<string, string>>(sel);
var selectedTeamId = pair.Key;
var selectedTeamName = pair.Value;
This would mean if the underlying data changes between the first turn of the prompt and the second, the choice would still be valid.

Getting the "Media Created" date of a video file in UWP

I want to get the date of creation of a video file, commonly known as Media Created property (not to be confused with the File Creation Date)
I'm trying with this code:
var clip = await MediaClip.CreateFromFileAsync(x);
var encodingProps = clip.GetVideoEncodingProperties();
var props = encodingProps.Properties.ToList();
Inside the props reference I'm getting a list of Guids and values, but I'm lost there.
You can use Extended properties to get the specific property you need:
var dateEncodedPropertyName = "System.Media.DateEncoded";
var propertyNames = new List<string>()
{
dateEncodedPropertyName
};
// Get extended properties
IDictionary<string, object> extraProperties =
await file.Properties.RetrievePropertiesAsync(propertyNames);
// Get the property value
var propValue = extraProperties[dateEncodedPropertyName];
if (propValue != null)
{
Debug.WriteLine(propValue);
}
Note I am using the System.Media.DateEncoded property in the example. If you need a different property, check out the full list of supported properties with their exact names in documentation.

Getting the profileid for a userid

In our company we created a custom Issues app. Additionally to using this app in the web interface, we also want to be able to change the state of an issue (new, acknowledged, test, resolved, ...) automatically via git commit hooks. The basics are working fine (ie change state, add notes, ...), but we also want to change the responsibility for the current item to a specific user. In that special case, it's the creator if this item.
My first try was the following:
var appid = 1234; var itemid = 1;
var item = podio.ItemService.GetItemByAppItemId(appid, itemid);
var update = new Item {ItemId = item.ItemId};
var creator = item.CreatedBy.Id;
var resp = update.Field<ContactItemField>("responsibility");
resp.ContactIds = new List<int>{creator.Value};
//change some other fields as well
podio.ItemService.UpdateItem(update);
This throws an "Object not found" exception, because in the resp.ContactIds one must not set the UserId but the ProfileId.
I then tried to get the ProfileId of the item-creator via
podio.ContactService.GetUserContactField(creator.Value, "profile_id");
but this also throws an exception "(Authentication as app is not allowed for this method").
So how can I get an appropriate profile id for the user when I use authentication as app?
OK, I found a workaround for it, not sure, if this is possible for other scenarios, but it works for the current case.
Instead of using the C# interface for setting the ContactIds for the ContactItemField, I set the json values directly.
var appid = 1234; var itemid = 1;
var item = podio.ItemService.GetItemByAppItemId(appid, itemid);
var update = new Item {ItemId = item.ItemId};
var creator = item.CreatedBy.Id;
var resp = update.Field<ContactItemField>("responsibility");
resp.ContactIds = new List<int>(); // set to an empty list, so that resp.Values is initialized to an empty JArray
var u = new JObject { {"value", new JObject { {"type" , "user" }, {"id", creator } } } };
responsibleField.Values.Add(u); //add the new user to the Values of the field
//change some other fields as well
podio.ItemService.UpdateItem(update);
And if I set the value with type user I can use the known userid and the API on the server takes care of the lookup.

MongoDB: update only specific fields

I am trying to update a row in a (typed) MongoDB collection with the C# driver. When handling data of that particular collection of type MongoCollection<User>, I tend to avoid retrieving sensitive data from the collection (salt, password hash, etc.)
Now I am trying to update a User instance. However, I never actually retrieved sensitive data in the first place, so I guess this data would be default(byte[]) in the retrieved model instance (as far as I can tell) before I apply modifications and submit the new data to the collection.
Maybe I am overseeing something trivial in the MongoDB C# driver how I can use MongoCollection<T>.Save(T item) without updating specific properties such as User.PasswordHash or User.PasswordSalt? Should I retrieve the full record first, update "safe" properties there, and write it back? Or is there a fancy option to exclude certain fields from the update?
Thanks in advance
Save(someValue) is for the case where you want the resulting record to be or become the full object (someValue) you passed in.
You can use
var query = Query.EQ("_id","123");
var sortBy = SortBy.Null;
var update = Update.Inc("LoginCount",1).Set("LastLogin",DateTime.UtcNow); // some update, you can chain a series of update commands here
MongoCollection<User>.FindAndModify(query,sortby,update);
method.
Using FindAndModify you can specify exactly which fields in an existing record to change and leave the rest alone.
You can see an example here.
The only thing you need from the existing record would be its _id, the 2 secret fields need not be loaded or ever mapped back into your POCO object.
It´s possible to add more criterias in the Where-statement. Like this:
var db = ReferenceTreeDb.Database;
var packageCol = db.GetCollection<Package>("dotnetpackage");
var filter = Builders<Package>.Filter.Where(_ => _.packageName == packageItem.PackageName.ToLower() && _.isLatestVersion);
var update = Builders<Package>.Update.Set(_ => _.isLatestVersion, false);
var options = new FindOneAndUpdateOptions<Package>();
packageCol.FindOneAndUpdate(filter, update, options);
Had the same problem and since I wanted to have 1 generic method for all types and didn't want to create my own implementation using Reflection, I end up with the following generic solution (simplified to show all in one method):
Task<bool> Update(string Id, T item)
{
var serializerSettings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
var bson = new BsonDocument() { { "$set", BsonDocument.Parse(JsonConvert.SerializeObject(item, serializerSettings)) } };
await database.GetCollection<T>(collectionName).UpdateOneAsync(Builders<T>.Filter.Eq("Id", Id), bson);
}
Notes:
Make sure all fields that must not update are set to default value.
If you need to set field to default value, you need to either use DefaultValueHandling.Include, or write custom method for that update
When performance matters, write custom update methods using Builders<T>.Update
P.S.: It's obviously should have been implemented by MongoDB .Net Driver, however I couldn't find it anywhere in the docs, maybe I just looked the wrong way.
Well there are many ways to updated value in mongodb.
Below is one of the simplest way I choose to update a field value in mongodb collection.
public string UpdateData()
{
string data = string.Empty;
string param= "{$set: { name:'Developerrr New' } }";
string filter= "{ 'name' : 'Developerrr '}";
try
{
//******get connections values from web.config file*****
var connectionString = ConfigurationManager.AppSettings["connectionString"];
var databseName = ConfigurationManager.AppSettings["database"];
var tableName = ConfigurationManager.AppSettings["table"];
//******Connect to mongodb**********
var client = new MongoClient(connectionString);
var dataBases = client.GetDatabase(databseName);
var dataCollection = dataBases.GetCollection<BsonDocument>(tableName);
//****** convert filter and updating value to BsonDocument*******
BsonDocument filterDoc = BsonDocument.Parse(filter);
BsonDocument document = BsonDocument.Parse(param);
//********Update value using UpdateOne method*****
dataCollection.UpdateOne(filterDoc, document);
data = "Success";
}
catch (Exception err)
{
data = "Failed - " + err;
}
return data;
}
Hoping this will help you :)

Categories