The Graph API team is pleased to announce that Azure AD Graph has a new client library (version 2.0). This was released recently at TechEd Europe 2014, as part of the exciting new Office 365 REST APIs and SDKs, which allows developers to easily build applications for .Net, Android and iOS. You'll find our new Graph libraries have the same consistent signature as the Office 365 client libraries, and are supported on the same platforms. Visual Studio developers can write applications that use Graph and Office 365 API using C# code, targeting Windows Phone, Windows Store, and MVC web apps. Additionally through Xamarin, that same code can now target Android and iOS. But wait there’s more! Through Microsoft Open Technologies we even have support for integrated development environments outside of Visual Studio! Android developers, you can now preview connecting your applications to cloud services like Graph and Office 365, using IntelliJ or Android Studio, just like .Net developers do with Visual Studio.
Major benefits of the 2.0 library
Platform reach: With this release of the Graph and Office 365 API SDKs, we have:
- NET SDK portable client library builds many types of apps using Visual Studio
- .NET Windows Store (and Universal Store) Apps
- Windows Forms Application
- WPF Application
- ASP.NET MVC Web Application
- ASP.NET Web Forms Application
- Xamarin Android and iOS Applications (build Android and iOS apps within VS)
- Multi-device Hybrid Apps (Cordova)
- Windows Phone 8.1 Apps (version 1.1.728 onwards)
- Microsoft Office 365 API Tools for Visual Studio 2013 to easily create and connect apps to O365 and Graph cloud service APIs through the client libraries.
- Native Android SDK (GA) & iOS SDK
- Includes native Android and iOS Graph client libraries and samples
- IntelliJ/Android Studio for Microsoft Services (preview)
- Mobile developers can connect Android Apps to O365 and Azure Mobile Services through Android development environments
Consistent APIs: Client libraries have a consistent signature across Graph and Office 365 client libraries
Async support: Now call Graph asynchronously
Simplified query support: Query Azure AD graph using LINQ constructs (rather than complex filter expressions)
A closer look at the .Net client library
For this post we will focus on the .Net client library. If you’re more interested in the native Android and iOS client SDKs, please check out the Native Android SDK (GA) & iOS SDK blog, which link to the SDKs on github.
You can find the latest Graph client library 2.0 on nuget.org. When you clone any of our samples from Github that use the new client library (using Visual Studio), and build the samples, this will automatically resolve references to our newest nuget package and pull it into your solution. We have 2 samples available on github that use the new client library, and we’ll add more over time:
For the rest of this section we’ll walk you through some of the key calling patterns and scenarios.
Instantiating a graph client
Since Graph API is secured by Azure AD, you'll also need to make sure that your solution contains the AD Authentication Library (or ADAL), which is also available as a nuget package. NOTE: ADAL is also available natively for iOS and Android.
The following code snippet defines a helper function that returns an ActiveDirectoryClient.
public static ActiveDirectoryClient GetActiveDirectoryClientAsApplication()
{
Uri servicePointUri = new Uri(”https://graph.windows.net”);
Uri serviceRoot = new Uri(servicePointUri, ”contoso.com”);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await AcquireTokenAsyncForApplication());
return activeDirectoryClient;
}
Client creation also requires a valid authorization token which in this case is accomplished through a helper function that makes use of ADAL methods. In this case the client is associated with a confidential_client flow token (application token). Client creation can also be associated with a code flow token ("delegation" or "application + user" token).
Once the client is instantiated, the fun can begin and we can start calling the Graph! The client object model now exposes all the entities, underlying complex types, properties, links, actions and custom service actions.
Async model
The new Graph Client Library also has support to make asynchronous calls to query Azure AD Graph. Async calls can help the overall responsiveness of your application and mitigate performance bottlenecks.
To retrieve an entity asynchronously:
AzureActiveDirectoryClient activeDirectoryClient;
Task<IPagedCollection<{Entity}>> getGraphObjectsTask = activeDirectoryClient.{Entity}.ExecuteAsync();
...
IPagedCollection<{Entity}> graphObjects = await getGraphObjectsTask;
For an example, to retrieve users asynchronously:
AzureActiveDirectoryClient activeDirectoryClient;
Task<IPagedCollection<IUser>> getGraphObjectsTask = activeDirectoryClient.Users.ExecuteAsync();
...
IPagedCollection<IUser> graphObjects = await getGraphObjectsTask;
Create, update and delete operations can also all be executed asynchronously, as well as in batch mode (but more about that later!).
Using LINQ to query users and paging results
Previously, creating filter queries, ordering and paging results was a challenging task requiring the construction of a filter expression. With the new client library this is simplified through the use of the LINQ whereclause. Here are some examples:
Get users by UPN or objectId
Here’s how to search for a user by UPN (user principal name). It returns a list, which will only have one member.
List<IUser> users = activeDirectoryClient.Users.Where(user =>
user.UserPrincipalName.Equals(“jon@contoso.com”)).
ExecuteAsync().Result.CurrentPage.ToList();
You can also search based on objectId. ObjectId is the unique identifier for users, groups, devices, applications, and service principals – i.e. most Graph objects. In general this unique identifier should be used for authorizing principals, so this kind of call should be relatively common in your applications:
IUser user = activeDirectoryClient.Users.GetByObjectId(uObjectId).ExecuteAsync().Result;
Get a page of the top 4 users, ordered by display name:
List<IUser> users = activeDirectoryClient.Users.OrderBy(user =>
user.UserPrincipalName).Take(4).ExecuteAsync().
result.CurrentPage.ToList();
Search for users through a people picker, and page through the results:
IUserCollection userCollection = activeDirectoryClient.Users;
IPagedCollection<IUser> searchResults = userCollection.Where(user =>
user.UserPrincipalName.StartsWith(searchString) ||
user.DisplayName.StartsWith(searchString) ||
user.GivenName.StartsWith(searchString) ||
user.Surname.StartsWith(searchString)).
ExecuteAsync().Result;
usersList = searchResults.CurrentPage.ToList();
if (usersList != null && usersList.Count > 0) {
do
{
usersList = searchResults.CurrentPage.ToList();
foreach (IUser user in usersList)
{
Console.WriteLine("User DisplayName: {0} UPN: {1}",
user.DisplayName, user.UserPrincipalName);
}
searchResults = searchResults.GetNextPageAsync().Result;
} while (searchResults != null && searchResults.MorePagesAvailable);
}
Find groups starting with “US”, and page through the results:
Group retrievedGroup = new Group();
string searchString = "US";
List<IGroup> foundGroups = null;
try
{
foundGroups = activeDirectoryClient.Groups.Where(group =>
group.DisplayName.StartsWith(searchString))
.ExecuteAsync().Result.CurrentPage.ToList();
}
catch (Exception e)
{
Console.WriteLine("\nError getting Group {0} {1}",
e.Message, e.InnerException != null ? e.InnerException.Message :"");
}
if (foundGroups != null && foundGroups.Count > 0)
{
retrievedGroup = foundGroups.First() as Group;
}
else
{
Console.WriteLine("Group Not Found");
}
Common Create, Update and Delete operations
Create:
{IEntity} entity = new {Entity}();
Then set entity properties and save it.
await activeDirectoryClient.{Entity}.Add{Entity}Async();
For example, to create a user:
IUser userToBeAdded = new User();
userToBeAdded.DisplayName = " Demo User";
userToBeAdded.UserPrincipalName = "demoUser@" + defaultDomain.Name;
userToBeAdded.AccountEnabled = true;
userToBeAdded.MailNickname = "DemoUser";
userToBeAdded.PasswordProfile = new PasswordProfile
{
Password = "TempP@ssw0rd!",
ForceChangePasswordNextLogin = true
};
userToBeAdded.UsageLocation = "US";
await activeDirectoryClient.Users.AddUserAsync(userToBeAdded);
Update:
await {entityObject}.UpdateAsync();
To update an entity, change its properties you want to update and call UpdateAsync.
For example, to update a user:
user.City = “Updated City”;
user.Country = “New Country”;
await user.UpdateAsync();
Add a member to a group:
{groupObject}.Members.Add({entityObject} as DirectoryObject);
So for example, to update a group with a new user member:
myGroup.Members.Add(userToBeAdded as DirectoryObject);
await myGroup.UpdateAsync();
NOTE: The same construct can be used to add users to a DirectoryRole object. Groups and users may be added to a group, however, for now, only users can be added to a DirectoryRole.
Delete:
await {EntityObject}.DeleteAsync();
For example, to delete a user:
await user.DeleteAsync();
Other operations
Take a look at the Graph samples on github (see links earlier in the blog) for more examples of calling the Azure AD Graph through the client library. Below we have more details on groups, batch operations and schema extensions support. Currently differential query is not supported through the client library, and we'll provide updates on this when it's available. For now you'll need to do this by going directly to the Graph REST APIs.
Batch requests:
There are two ways batch requests can be performed: one where you “queue up” or "defer" the transactions and then execute them all at once by calling SaveChanges, and the other where you explicitly form the batch and execute. NOTE:
Currently Graph API limits the number of requests to 5 in a batch.
- Queuing up requests: This is good for creates, updates and deletes. For example, if you create an entity as
activeDirectoryClient.{Entity}.Add{Entity}Async({entityObject}, true);
Then the transaction will be deferred. When you’re done creating entities, you can then execute all the deferred transactions in a single batch, by saving the changes.
await activeDirectoryClient.Context.SaveChangesAsync(SaveChangesOptions.BatchWithIndependentOperations);
The same approach can be done for any asynchronous operations exposed by an entity (such as updates, deletes and service actions):
2. Form the batch and then execute: This approach is suited to querying objects in a single batch.
await activeDirectoryClient.Context.ExecuteBatchAsync(query1, query2, queryN);
For example, to get users (whose names start with "Dan") and all groups in a batch request:
string searchString = "Dan";
IReadOnlyQueryableSet<User> userQuery = activeDirectoryClient.DirectoryObjects.OfType<User>()
.Where(user =>
user.UserPrincipalName.StartsWith(searchString) ||
user.DisplayName.StartsWith(searchString) ||
user.GivenName.StartsWith(searchString) ||
user.Surname.StartsWith(searchString));
IReadOnlyQueryableSet<Group> groupsQuery = activeDirectoryClient.DirectoryObjects.OfType<Group>();
IBatchElementResult[] batchResult = await activeDirectoryClient.Context
.ExecuteBatchAsync(userQuery, groupsQuery).Result;
The batch result can then be iterated through to the process response of each query.
Directory schema extensions:
Manipulating schema extensions is fully supported through the client library. We'll show you how to register an extension for your application. Once registered, that extension attribute may be set and queried, just like any other entity attribute. For more on schema extensions please see our MSDN topic on schema extensions.
Register an extension:
Firstly create the extension and then register it through your application entity. For example, let's register a LinkedInId extension on the user object.
ExtensionProperty linkedInUserId = new ExtensionProperty
{
Name = "linkedInUserId",
DataType = "String",
TargetObjects = { "User" }
};
myApp.ExtensionProperties.Add(linkedInUserId);
await myApp.UpdateAsync();
Once updated, the extension property client side object will also be updated - the Name attribute will now contain the extension attribute's reference name that will appear on the target object once a value is written. We'll use that next to set the attribute value on a user object.
Set an extension value
Here we'll set the LinkedIn Id on a user object, using the linkedInIdUserId object from earlier:
myUser.SetExtendedProperty(linkedInUserId.Name, "dan@dan.com");
await myUser.UpdateAsync();
Get an extension value
You can get a object's extension properties through a dictionary. The general pattern is to retrieve the:
{entityObject}.GetExtendedProperties()[{keyValue}];
So for example:
string extensionValue = myUser.GetExtendedProperties()[linkedInUserId.Name];
Query for an object using an extension attribute value
Currently this is not supported through the client library. We'll update this blog with details when it's available.
Find all registered extensions
The following query will allow you to get back all the schema extensions that have been registered in the tenant (when the tenant either consents to applications that have registered extensions) or when you instantiate service principals in your tenant from applications that register extensions.
IEnumerable<IExtensionProperty> availableExtensionProperties = activeDirectoryClient.GetAvailableExtensionPropertiesAsync(false).Result;
Set the argument above to true if you want to get back extensions that were declared as syncing from on-premises.(through directory synchronization). You can inspect each extension property returned to find out its name, type and the target objects that it applies to.
It's also possible to retrieve the extensions registered by an application, by querying the application itself:
IEnumerable<IExtensionProperty> availableAppExtensions = <Application Entity>.ExtensionProperties;
So for our example:
IEnumerable<IExtensionProperty> availableAppProperties = myApp.ExtensionProperties;
Unregistering a directory extension
If you decide to unregister a registered extension, here's how you can do it, using our example:
myApp.ExtensionProperties.Remove(linkedInUserId);
await myApp.UpdateAsync();
.Net Samples
Again, we currently have 2 samples available on github that use the new client library, and we’ll add more over time:
.Net Graph Client version 1.0
Graph client library version 1.0 is now deprecated, but will be supported for another 24 months from December 22nd, 2014. We strongly encourage app developers to move to the newer v2 client library as soon as possible.
Graph API client library source code
We hope to make the Graph client library source code available very soon. We'll update this post with a link when it's available
Feedback
We'd love to hear what you think about the new client library, any issues you see and any suggestions you might have for improvements or new features.
Thanks,
The Azure AD Graph team.