Having problem using update User with the google Admin SDK for C#.
https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/update
This method supports patch semantics, meaning you only need to include the fields you wish to update. Fields that are not present in the request will be preserved, and fields set to null will be cleared.
This differs from the Patch as patch won't clear fields that are null but only update fields that have a value.
Problem is that I have to pass a full Google.Apis.Admin.Directory.directory_v1.Data.User class to the function which will contain null of even properties i do not want to clear.
example:
public User UpdateUser(Google.Apis.Admin.Directory.directory_v1.Data.User gUser)
{
UsersResource.UpdateRequest userUpdateRequest = _service.Users.Update(gUser, gUser.Id);
User updatedUser = userUpdateRequest.Execute();
return updatedUser;
}
Is there any way of modifying the Body in UpdateRequest before executing it?
Edit:
The UpdateRequest has a ModifyRequest Property that looks like this
I just have no Idea how to use it, any ideas?
public Action<HttpRequestMessage> ModifyRequest { get; set; }
As far as updating things to the concept of Null that is not something that can be done with PATCH. I recommend setting it to an empty string.
You should also not be sending the full user object if thats what you are currently doing. I am going to assume that you have done a users.list to find the user you want to update and change something in that user, say the name. Then you have simply submited the full user object to your method
UpdateUser(Google.Apis.Admin.Directory.directory_v1.Data.User gUser)
This wont work as some of the fields you have sent as part of the update/patch are not actually writeable.
What you should do instead would be to create a new user object change what ever it is you want
public User MakeUserAdmin(Google.Apis.Admin.Directory.directory_v1.Data.User gUserId)
{
var updateFields= new Google.Apis.Admin.Directory.directory_v1.Data.User();
change.IsAdmin = true;
change.Addresses = ""; // will set it to empty yes not null but the best you can do with this api.
UsersResource.UpdateRequest userUpdateRequest = _service.Users.Update(updateFields, gUserId);
User updatedUser = userUpdateRequest.Execute();
return updatedUser;
}
Notice how you just need to create a new object and update only the fields you need then send that.
Dont try to update every field, just update the ones that you know have changed. Dont include the id in the object that is not writeable either.
Related
I am using the Microsoft.Graph library which I got off of Nuget. I have a problem regarding change tracking using deltas. Suppose
I am getting changes to users using something like the code below:
var usersDeltaRequest = client
.Users
.Delta()
.Request(usersDeltaLink == null ? new Option[0] : new []
{
new QueryOption("$deltatoken", usersDeltaLink)
});
var users = await usersDeltaRequest.GetAsync();
foreach (var user in users)
{
//code that updates the user goes here
}
My problem is that in this case, what gets returned is a User object. However since this is a delta, not all the fields in the object get populated. Only the ones that have been changed are guaranteed to be populated.
Now were I to parse the JSON returned manually, it would be easy to see which fields have actually been included in the response, since only those will be included in the JSON.
However, the library returns a User object and leaves the fields which haven't been returned as null. In this case, it does not seem possible to disambiguate between a field which simply hasn't been returned in the delta vs a field that actually does contain a null value.
Is there something I'm missing in how the library should be used? Because as it stands, it appears as if the library does lose some critical information, because I can't rely on the returned User object to reliably update my database, because a changed field containing a null value and a field that hasn't changed both result in a null value in the returned .Net object.
This obviously also applies to other types of resources, I just chose Users for the example.
As I read the API docs at https://developer.microsoft.com/en-us/graph/docs/concepts/delta_query_users it sez:
The optional $select query parameter is included in the request to
demonstrate how query parameters are automatically included in future
requests.
I haven't tried this. Did you include the properties you want to track changes for on your original request? Or perhaps try $select=* to return everything? The API sez:
By default, only a limited set of properties are returned
(businessPhones, displayName, givenName, id, jobTitle, mail,
mobilePhone, officeLocation, preferredLanguage, surname,
userPrincipalName).
I present a simple model:
public class UserDocument
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string DisplayName { get; set; }
public List<string> Friends { get; set; }
}
I am using the latest C# driver which has the ability to replace a document using a C# object which will automatically update all its fields. Problem is I want to update all fields except for the user friends, because it's a field containing the object relations to other documents. Of course I can manually update each field of the ones I want to get updated, which here are just two.
But this example is simple just to make my point. In reality the fields are much more and it would be harder to update each field. That would require a single line for each one to use the Set operator. Also, newly-added fields would have to be supported in the same way as opposed to updating to automatically just works.
Is there a way to achieve that - automatically update all fields with just specifying a list of excluded fields?
There is no way, using the provided builders to have a "blacklist" update which excludes only specific fields.
You can query the old document, copy the old values of these fields to the new instance and then replace it entirely in the database.
You can also generate such an update command by iterating over the fields using reflection.
But the MongoDB driver doesn't offer such a query built in.
I figured out a way to do this with MongoDB using Javascript/NodeJS, but maybe the logic can translate to C#?
I wanted to update all fields without having to actually explicitly state them (all fields except for one, it turned out).
Attempted update of all document fields:
await examCollection.findOneAndUpdate(
{_id: new ObjectID(this.examId)},
{$set: this.data}
)
...except, this.data happened to have _id in it as well, which I didn't want to update. (In fact, it gave me an error, because _id is immutable.)
So, for my workaround, I ended up "deleting" all fields on the object that I didn't want to update (i.e. _id).
Successful update of all non-specified document fields:
// (1) specify fields that I don't want updated (aka get rid of them from object) (similar option in C#?)
delete this.data._id
//delete this.data.anotherField
//delete this.data.anotherField2
//delete this.data.anotherField3
// (2) update MongoDB document
await examCollection.findOneAndUpdate(
{_id: new ObjectID(this.examId)},
{$set: this.data}
)
This was much easier than explicitly stating all the fields I did want to update, because there were A LOT, and they could potentially change in the future (new fields added, fields deleted, etc.).
Hopefully this strategy can help!
Note: In reality, I did my "field specifying" earlier in another file, rather than immediately before updating like it shows in the example, but same effect.
I've created a custom user control for the back-end of my Umbraco site that allows administrators to quickly update certain fields on nodes without having to navigate through the content tree.
So far my code is working as expected: I can update simple true/false properties without a problem. However now I'm trying to update a property that's of a custom data type and I'm running into difficulties.
The data type itself is just a simple drop down that lists a series of availability statuses ie. Available, Unavailable, Sold and Reserved. The datatype is storing the text values.
Here's the code I have that allows me to update my true/false properties:
public void ChangeInteractiveStatus(string nodeId, bool chkValue)
{
var cs = ApplicationContext.Current.Services.ContentService;
var apartment = cs.GetById(Convert.ToInt32(nodeId));
apartment.SetValue("displayOnInteractive", chkValue);
cs.SaveAndPublish(apartment);
}
This works absolutely fine as the data type of this property is a regular true/false data type.
Here's the code I'm using to change the value of my custom dropdownlist data type:
public void ChangeAvailabilityStatus(string nodeId, string status)
{
var cs = ApplicationContext.Current.Services.ContentService;
var apartment = cs.GetById(Convert.ToInt32(nodeId));
apartment.SetValue("status", status);
cs.SaveAndPublish(apartment);
}
As you can see there's very little difference and yet this code isn't working.
In order to check what was happening when I was updating the properties with the above code, I checked the umbraco.config file only to find that the property in question was displaying as follows:
<status><![CDATA[]]></status>
However when I change the value in the content tree (without using my admin control) the value gets saved properly as:
<status><![CDATA[Sold]]></status>
So for whatever reason, when I try to update the value it's being rejected and I can't work out why.
FYI I tried entering the value as:
"<![CDATA[" + status + "]]>"
Yet that made no difference.
Does anyone know how I can fix this? How can I get the property to update correctly?
Thanks
Okay I've figured out what the problem was. It seems the values were being stored as name-value pairs, so the actual value getting stored in the database was an integer. Once I updated the code to insert the integer id it all worked as expected! Hooray.
I am updating an entity in a form, for simplicity, lets call it CompanyCar. My objective is to check and see if its assigned owner has changed, and if so, send an email to the old and new owner.
public Car SaveExistingCar(Car car)
{
var original = _CarRepository.LoadCarById(Car.Id);
var carReturn = _CarRepository.SaveOrUpdateCar(Car) //Error here
//pseudo: if carReturn.Owner != original.Owner
// Send Email
return carReturn;
}
Unfortunately it gets mighty angry about this.
a different object with the same identifier value was already associated with the session
I can understand whats making that happen... sure, okay its confused about the real object. However, that doesn't do much to help me solve it. Is there something I can do to tell it that the 'original' is bogus?
ISession.Evict will remove the original car from the session and 1st level cache and you should be able to save the new car.
This is not so much a question as to how to do something, but rather how I can implement something better or think about the problem differently.
I have a winforms application that allows the user to select multiple rows in a grid. These rows represent accounts and when the user selects the accounts and hits a button, a boolean property on the objects will change to whatever the selected value is regardless of it's existing state. However, if a validation method fails, a message is sent to the user and the boolean property needs to be set back to it's original state.
public void ModifyAccounts(List<DemoAccount> accts, bool updateIsSpecial)
{
// Dictionary that will hold the account ID along with the booleans original state
Dictionary<int, bool> originalState = new Dictionary<int, bool>();
foreach(var acct in accts)
{
// Add the current state to the dictionary
originalState.Add(acct.Id, acct.IsSpecial);
acct.IsSpecial = updateIsSpecial;
}
// Send the list to another method that loops through each account and checks
// for specific validation rules. Returns a collection of tuples. The tuple
// contains the account for item1 and a bool validated flag for item2
var valAccounts = ValidateAccounts(accts);
var failedAccounts = from obj in valAccounts
where !acct.Item2
select new
{
account = obj.Item1,
isValid = obj.Item2
};
if (failedAccounts.Count() > 0)
{
// Alert the user with custom msg box method that the accounts failed
// Do Custom Method
// Reset the values of the failed accounts to their previous state.
// It is possible that some accounts passed validation and were saved,
// only the failed accounts should be reset.
foreach (var obj in failedAccounts)
{
bool originalFlagState = false;
originalFlagStates.TryGetValue(obj.account.Id, out originalFlagState);
var origAccount = accts.Where(x => x.Id == obj.account.Id).FirstOrDefault();
origAccount.IsSpecial = originalFlagState;
}
}
}
I hope this isn't too confusing. I only have ~3 years of dev experience which is not a lot. However, I feel it's enough to understand when working on something that if it feels like there is a better way then I am probably not doing it correctly or efficiently. Modifying the account flag changes the object in the accounts list. Obviously adding the object to a new list will just create a reference to that object. So I can't do something like holding 2 collections one for modification and the other for original state. I also can't do a deep copy because the account class is not marked serializable. I cannot change this because of the type of object.
Thanks to anyone who can provide some advice or insight!
I do not understand why you want to validate after changing, and not beforehand.
But if you really have to validate and use undo afterwards there is a design pattern which supports this behavior
Have a look at the command pattern. Here is a code project link that describes how to implement it
http://www.codeproject.com/Articles/8303/Using-the-Command-pattern-for-undo-functionality
In your particular case I would iterate through the command stack and undo all commands which are used on failed accounts
It could look something like this
foreach (var obj in failedAccounts)
foreach ICommand command in commandStack
If(command.element.Equals(obj))
command.undo;
command.element should contain the element you want to change with your command.
if you dont want two foreach u could use System.Linq operations