Azure File Share REST API for Xamarin - azure

In Xamarin client app, I want to access Azure Files using SAS token with Portable Class Library. It seems I can not do it using latest WindowAzure.Storage nuget package as it may only works with Blob, Table,... and it requires lots of dependencies.
Is there anyway to accomplish this?

I ended up with using Azure File Storage REST API.
Basically we request SAS token generated from Azure Share first then using that SAS token in URL to send http request to Azure Files Storage:
https://[yourshare].file.core.windows.net/[yourdirectory]/[yourfile]?[your_sas_token]
I have created a class to help client doing some basic operations as below (it is portable class so can use anywhere in client side):
public class AzureFileREST
{
private AzureSASToken _azureShareToken;
public AzureFileREST(AzureSASToken azureShareToken)
{
_azureShareToken = azureShareToken;
}
public async Task CreateIfNotExist(string directoryName)
{
var existed = await CheckDirectoryExists(directoryName);
if (!existed)
{
await CreateDirectory(directoryName);
}
}
public async Task<bool> CheckDirectoryExists(string directoryName)
{
using (var client = new HttpClient())
{
//Get directory (https://msdn.microsoft.com/en-us/library/azure/dn194272.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.GetAsync(azureCreateDirUrl).ConfigureAwait(false);
return (response.StatusCode != System.Net.HttpStatusCode.NotFound);
}
}
public async Task CreateDirectory(string directoryName)
{
using (var client = new HttpClient())
{
//Create directory (https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx)
var azureCreateDirUrl = _azureShareToken.Url + directoryName + _azureShareToken.SASToken + "&restype=directory";
var response = await client.PutAsync(azureCreateDirUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
public async Task UploadFile(string fileName, byte[] fileBytes)
{
using (var client = new HttpClient())
{
//Create empty file first (https://msdn.microsoft.com/en-us/library/azure/dn194271.aspx)
var azureCreateFileUrl = _azureShareToken.Url + fileName + _azureShareToken.SASToken;
client.DefaultRequestHeaders.Add("x-ms-type", "file");
client.DefaultRequestHeaders.Add("x-ms-content-length", fileBytes.Length.ToString());
var response = await client.PutAsync(azureCreateFileUrl, null).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
//Then upload file (https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx)
var azureUploadFileUrl = azureCreateFileUrl + "&comp=range";
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("x-ms-write", "update");
client.DefaultRequestHeaders.Add("x-ms-range", String.Format("bytes=0-{0}", (fileBytes.Length - 1).ToString()));
var byteArrayContent = new ByteArrayContent(fileBytes);
response = await client.PutAsync(azureUploadFileUrl, byteArrayContent).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
}
In server side, use the following function to generate SAS token from Share:
public AzureSASToken GetSASFromShare(string shareName)
{
var share = _fileclient.GetShareReference(shareName);
share.CreateIfNotExists();
string policyName = "UPARSharePolicy";
// Create a new shared access policy and define its constraints.
var sharedPolicy = new SharedAccessFilePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddDays(15),
Permissions = SharedAccessFilePermissions.Read | SharedAccessFilePermissions.Write
};
// Get existing permissions for the share.
var permissions = share.GetPermissions();
// Add the shared access policy to the share's policies.
// Note that each policy must have a unique name.
// Maximum 5 policies for each share!
if (!permissions.SharedAccessPolicies.Keys.Contains(policyName))
{
if (permissions.SharedAccessPolicies.Count > 4)
{
var lastAddedPolicyName = permissions.SharedAccessPolicies.Keys.Last();
permissions.SharedAccessPolicies.Remove(lastAddedPolicyName);
}
permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
share.SetPermissions(permissions);
}
var sasToken = share.GetSharedAccessSignature(sharedPolicy);
//fileSasUri = new Uri(share.StorageUri.PrimaryUri.ToString() + sasToken);
return new AzureSASToken ()
{
Name = shareName,
Url = share.StorageUri.PrimaryUri.ToString() + "/",
SASToken = sasToken
};
}
Finally using class like this:
var azureFileRest = new AzureFileREST(sasToken);
await azureFileRest.CreateIfNotExist(directoryName);
await azureFileRest.UploadFile(directoryName + "/" + fileName, bytes);

Related

Migrate from Azure to SharePoint using Migration API - "Must specify IV in Manifest or blob metadata'"

I am trying to use the Migration API with C# to migrate from the default free Azure Cloud (given to all sharepoint accounts) to SharePoint.
I can successfully generate a cloud container, and upload files, but the migration fails. Here is my code:
private void MigrateToAzureThenSharePoint()
{
//Provision The default Azure Containers and Migation Queue
var migrationContainers = destinationCtx.Site.ProvisionMigrationContainers();
var migrationQueue = destinationCtx.Site.ProvisionMigrationQueue();
destinationCtx.ExecuteQuery();
var containerInfoList = migrationContainers.Value;
var dataContainerUri = containerInfoList.DataContainerUri;
var metadataContainerUri = containerInfoList.MetadataContainerUri;
var queueInfo = migrationQueue.Value;
//Instansiate Cloud Queue
var cloudQueue = new CloudQueue(new Uri(queueInfo.JobQueueUri));
//Instansiate CloudBlobContainer
CloudBlobContainer dataContainer = new CloudBlobContainer(new Uri(dataContainerUri));
CloudBlobContainer manifestContainer = new CloudBlobContainer(new Uri(metadataContainerUri));
//Test files to upload
var testfiles = new[]
{
new SourceFile
{
Filename = "test.txt",
LastModified = DateTime.Now,
Contents = Encoding.UTF8.GetBytes("Hi, this is a test text-file"),
Title = "Title of file 1"
},
new SourceFile
{
Filename = "test2.txt",
LastModified = DateTime.Now.AddDays(-1),
Contents = Encoding.UTF8.GetBytes("Tesfile2"),
Title = "Second title"
}
};
//Upload Test Files to Cloud
foreach (var testfile in testfiles)
{
var blobReference = dataContainer.GetBlockBlobReference(testfile.Filename);
blobReference.UploadFromByteArray(testfile.Contents, 0, testfile.Contents.Length, null);
blobReference.CreateSnapshot();
}
var manifestPackage = new ManifestPackage(destOrganisationList.Id,
destinationCtx.Web.Id,
destinationCtx.Web.Title,
destOrganisationList.Title,
#"\",
destOrganisationList.RootFolder.UniqueId,
destOrganisationList.RootFolder.ParentFolder.UniqueId
);
//Create XML Files for Azure Migration (An easier way here would be great? I manualy write them)
var filesInManifestPackage = manifestPackage.GetManifestPackageFiles(testfiles);
//Uploaded manually created XML files
foreach (var migrationPackageFile in filesInManifestPackage)
{
var blobReference = manifestContainer.GetBlockBlobReference(migrationPackageFile.Filename);
blobReference.UploadFromByteArray(migrationPackageFile.Contents, 0, migrationPackageFile.Contents.Length, null);
blobReference.CreateSnapshot();
}
//Start Migration
var result = destinationCtx.Site.CreateMigrationJobEncrypted(destinationCtx.Web.Id,
containerInfoList.DataContainerUri,
containerInfoList.MetadataContainerUri,
queueInfo.JobQueueUri,
new EncryptionOption()
{
AES256CBCKey = containerInfoList.EncryptionKey
});
destinationCtx.ExecuteQuery();
//Check for messags
while (true) {
var msg = cloudQueue.GetMessage();
if (msg == null)
{
Task.Delay(TimeSpan.FromSeconds(1));
continue;
}
var message = JsonConvert.DeserializeObject<EncryptedMessage>(msg.AsString);
cloudQueue.DeleteMessage(msg);
//Decode encrypted message
var decoded = JsonConvert.DeserializeObject<UpdateMessage>(Decrypt(message.Content, containerInfoList.EncryptionKey, message.IV));
//Error
}
}
There are a few errors, but they all are similar to this:
"Unable to download SystemData.xml with exception 'Must specify IV in
Manifest or blob metadata'"
I believe I may have to encrypt the files when I upload from this method:
blobReference.UploadFromByteArray
There is an option:
var blobRequestOptions = new BlobRequestOptions();
blobRequestOptions.EncryptionPolicy =
But I don't know how to instantiate the encryption policy (and I may be going down a rabbit hole)
Any help would be greatly appreciated, as documentation online is shocking minimal.

How to stream Video that are stored in Azure storage Blob using Xamarin

I have uploaded video files to Azur Blob(Containers),I want to access them in the mobile app via Streaming.I have files with extention .mp4 . I have done code to download from blob and Store in local drive then play using default player , But I want to give user a option to stream instead of download.
I have used this method
var credentials = new StorageCredentials("myaccountname", "mysecretkey");
var account = new CloudStorageAccount(credentials, true);
var container = account.CreateCloudBlobClient().GetContainerReference("yourcontainername");
var blob = container.GetBlockBlobReference("yourmp4filename");
var sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),//Set this date/time according to your requirements
});
var urlToBePlayed = string.Format("{0}{1}", blob.Uri, sas);//This is the URI which should be embedded in your video player.
Problem:-
If I browse the Url(Blob Url) , It downloads the file instead of directly playing it .But in App , Nothing appears. Blank screen.
I am using
<WebView Source="{Binding VideoUrl}" HeightRequest="200" WidthRequest="200"/>
In Vm:
VideoUrl=url;
First Change content Type : like #Zhaoxing Lu - Microsoft said
public async Task ChangeContentTypeAsync()
{
try
{
UserDialogs.Instance.ShowLoading();
BlobContinuationToken blobContinuationToken = null;
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("videos");
var results = await container.ListBlobsSegmentedAsync(null, blobContinuationToken);
blobContinuationToken = results.ContinuationToken;
BlobResultSegment blobs = await blobClient
.GetContainerReference("videos")
.ListBlobsSegmentedAsync(blobContinuationToken)
;
foreach (CloudBlockBlob blob in blobs.Results)
{
if (Path.GetExtension(blob.Uri.AbsoluteUri) == ".mp4")
{
blob.Properties.ContentType = "video/mp4";
}
//// repeat and resume
await blob.SetPropertiesAsync();
}
UserDialogs.Instance.HideLoading();
}
catch (Exception ex)
{
var m = ex.Message;
}
}
Then Use this method :
private async Task StreamVideo(string filename)
{
try
{
UserDialogs.Instance.ShowLoading();
await azureBlob.ChangeContentTypeAsync();
var secretkey = "xxxx";
var credentials = new StorageCredentials("chatstorageblob", secretkey);
var account = new CloudStorageAccount(credentials, true);
var container = account.CreateCloudBlobClient().GetContainerReference("videos");
var blob = container.GetBlockBlobReference(filename);
var sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),//Set this date/time according to your requirements
});
var urlToBePlayed = string.Format("{0}{1}", blob.Uri, sas);//This is the URI which should be embedded in your video player.
await Navigation.PushAsync(new VideoPlayerPage(urlToBePlayed));
UserDialogs.Instance.HideLoading();
}
catch (Exception ex)
{
var m = ex.Message;
}
}

CSOM CreateSPAsyncReadJob stays in Queue state

I am referring to the Migration Asynchronous Read API that allows creating a read job on SharePoint using CSOM. I am able to create the read job successfully but unfortunately, the job stays in a queue state since long.
The function returns the Object that includes UniqueJobID, AzureContainerManifestUri, AzureQueueReportUri and EncryptionKey
By using clientContext.Site.GetMigrationJobStatus method I am able to check the read job status that always returns Queued
Here is the sample code for reference:
using (var clientContext = new ClientContext(siteUrl))
{
clientContext.Credentials = new SharePointOnlineCredentials(userName, password);
var result = clientContext.Site.CreateSPAsyncReadJob($"{siteUrl}/List/MyList", new AsyncReadOptions { });
clientContext.ExecuteQuery();
MigrationJobState state;
do
{
var status = clientContext.Site.GetMigrationJobStatus(result[0].JobId);
clientContext.ExecuteQuery();
state = status.Value;
} while (state == MigrationJobState.Queued);
}
I have also tried to connect to the AzureQueueReportUri queue that contains the message with encrypted content. I am not sure how we can decrypt the content to make it human readable. Here is the sample message:
{
"Label": "Encrypted",
"JobId": "079ece4a-cfd2-4676-a27d-2662beb5bb0a",
"IV": "RYc+ZA2feX1hnAcVWR1R+w==",
"Content": "qbjTBbb2N+DkNumLoCJSAAfwj8etDLgjxp+b2T9k03L9WfRJKlFBIZO457q+CbHA+8DHJS7VbPzVMoW6ybo2GxgteTYVP+yVUOPPvz57VGQJyzg2gss+Bsjn73GTWWUfwC/W+oWnEpt8PawZysCjSNf6A4HKZKewkskCshN/pND8ZpevrGt2qq0dTt0NkTIkuYv5AvIP7DSWjdl7nN/W5x4c2nR0sPFqKYom41a4tIqrruzwCDEEjWLFtuXAQ+UN2TMV9PWabRFe9n/P1RHrAJaNU+JjJiJm+lE1dQChz+7OuQoJsYnbjYTbqEE8CnIB0/E0zTrc3zLc6th8MBsKpZJjd31ovqr/Xez6zCnvMKotSdScFtTgQqHxmVDBMfMgi2mm8cKQpdKwRufP/YhaDQlvFkmj2FQN0KAMNxwFBh/MWCVhz5uCJ50CGhChcn4h"
}
I am also not able to connect the AzureContainerManifestUri blob container. It fails with an error Authentication Error. Signature did not match.
Can anyone please guide me how can I proceed ahead?
The method parameters have been changed. Here is the latest updated documentation: https://learn.microsoft.com/en-us/sharepoint/dev/apis/export-amr-api
Sample Code: https://gist.github.com/techmadness/484e7de0a7c51e5faf952a79f1eacb85
using System;
using System.Linq;
using System.Security;
using System.Threading;
using Microsoft.SharePoint.Client;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
var userName = "admin#tenant.onmicrosoft.com";
var password = GetSecurePassword("password");
var siteUrl = "https://tenant.sharepoint.com/sites/testsite";
var listUrl = $"{siteUrl}/testlist";
var azStorageConnectionStrong = "DefaultEndpointsProtocol=https;AccountName=abcd;AccountKey=xyz";
using (var clientContext = new ClientContext(siteUrl))
{
clientContext.Credentials = new SharePointOnlineCredentials(userName, password);
var azManifestContainer = CreateContainerIfNotExists(azStorageConnectionStrong, "spread-manifest-container");
var azReportQueue = CreateQueueIfNotExists(azStorageConnectionStrong, "spread-report-queue");
var azManifestContainerUrl = GetSASUrl(azManifestContainer);
var azReportQueueUrl = GetSASUrl(azReportQueue);
var output = clientContext.Site.CreateSPAsyncReadJob(
listUrl,
new AsyncReadOptions
{
IncludeDirectDescendantsOnly = true,
IncludeSecurity = true,
},
null,
azManifestContainerUrl,
azReportQueueUrl);
clientContext.ExecuteQuery();
CloudQueueMessage message;
do
{
Thread.Sleep(TimeSpan.FromSeconds(10));
message = azReportQueue.GetMessage();
if (message != null)
{
Console.WriteLine(message.AsString);
azReportQueue.DeleteMessage(message);
}
} while (message != null);
Console.ReadLine();
}
}
private static SecureString GetSecurePassword(string pwd)
{
SecureString securePassword = new SecureString();
foreach (var ch in pwd.ToArray())
{
securePassword.AppendChar(ch);
}
return securePassword;
}
private static CloudBlobContainer CreateContainerIfNotExists(string storageConnectionString, string containerName)
{
var storageAccount = CloudStorageAccount.Parse(storageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
container.CreateIfNotExistsAsync().GetAwaiter().GetResult();
return container;
}
private static CloudQueue CreateQueueIfNotExists(string storageConnectionString, string queueName)
{
var cloudStorageAccount = CloudStorageAccount.Parse(storageConnectionString);
var queueClient = cloudStorageAccount.CreateCloudQueueClient();
var queue = queueClient.GetQueueReference(queueName);
queue.CreateIfNotExistsAsync().GetAwaiter().GetResult();
return queue;
}
public static string GetSASUrl(CloudBlobContainer container)
{
var sharedAccessSignature = container.GetSharedAccessSignature(new SharedAccessBlobPolicy
{
Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write,
SharedAccessStartTime = DateTime.UtcNow.AddDays(-1),
SharedAccessExpiryTime = DateTime.UtcNow.AddDays(7),
});
return container.StorageUri.PrimaryUri + sharedAccessSignature;
}
public static string GetSASUrl(CloudQueue queue)
{
var sharedAccessSignature = queue.GetSharedAccessSignature(new SharedAccessQueuePolicy
{
Permissions = SharedAccessQueuePermissions.Add | SharedAccessQueuePermissions.Read,
SharedAccessStartTime = DateTime.UtcNow.AddDays(-1),
SharedAccessExpiryTime = DateTime.UtcNow.AddDays(7)
});
return queue.StorageUri.PrimaryUri + sharedAccessSignature;
}
}
}

How to download image to Xamarin.forms listview

I have a image url in list view and after click to listview image should download on same row.
How can I achieve on Xamarin.forms?
I got image from Azure blob
I'd recommend checking this blog post using Xamarin Forms Listview with SQL and Blob storage. They basically used SQL to store the images from Blob storage using the bellow code:
public class AzureMediaStorageService : IMediaStorageService
{
private CloudBlobContainer MediaContainer { get; set; }
public void InitializeStorage(string settings = null)
{
if (MediaContainer != null)
return;
if (string.IsNullOrEmpty(settings))
throw new ArgumentNullException(nameof(settings), "Azure Storage Media Service needs a connection string name as \"settings\" parameter");
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings[settings].ConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
MediaContainer = blobClient.GetContainerReference("medias");
MediaContainer.CreateIfNotExists();
MediaContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
}
public async Task<Uri> UploadBytesAsync(byte[] bytes, string uniqueId, MediaType mediaType)
{
var blockBlob = MediaContainer.GetBlockBlobReference(uniqueId);
blockBlob.Properties.ContentType = mediaType == MediaType.Picture ? "image/jpeg" : "video/mp4";
await blockBlob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
return blockBlob.Uri;
}
public async Task<byte[]> DownloadBytesAsync(Uri uri)
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
}
public string GetSecureClientUploadUrl(string uniqueId, DateTimeOffset expirationDateTime)
{
if (expirationDateTime <= DateTimeOffset.Now)
throw new ArgumentOutOfRangeException(nameof(expirationDateTime), expirationDateTime, null);
var blob = MediaContainer.GetBlockBlobReference(uniqueId);
var sasPolicy = new SharedAccessBlobPolicy
{
SharedAccessExpiryTime = expirationDateTime,
Permissions = SharedAccessBlobPermissions.Create
};
return MediaContainer.Uri + blob.GetSharedAccessSignature(sasPolicy);
}
}
The blog post can be found here: https://forums.xamarin.com/discussion/98784/how-to-load-images-in-xamarin-forms-listview-with-asp-net-web-api-from-sql-azure-database

Send email using Office 365 using unified API

We are trying to use the O365 Unified API to send emails from our line-of-business apps. I use the following code to send the email. This throws a DataServiceQueryException exception "Unauthorized".
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
await graphClient.Me.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
private static Message InitializeMessage(EmailMessage message)
{
ItemBody body = new ItemBody {Content = message.Body, ContentType = BodyType.HTML};
Message m = new Message
{
Body = body,
Subject = message.Subject,
Importance = Importance.Normal,
};
//Add all the to email ids
if (message.ToRecipients != null)
foreach (Models.Messaging.EmailAddress emailAddress in message.ToRecipients)
{
m.ToRecipients.Add(new Recipient { EmailAddress = new Microsoft.Graph.EmailAddress { Address = emailAddress.Address, Name = emailAddress.Name } });
}
return m;
}
The code for _authenticationHelper.GetGraphClientAsync() is
public async Task<GraphService> GetGraphClientAsync()
{
Uri serviceRoot = new Uri(appConfig.GraphResourceUriBeta + appConfig.Tenant);
_graphClient = new GraphService(serviceRoot,
async () => await AcquireTokenAsyncForUser(appConfig.GraphResourceUri, appConfig.Tenant));
return _graphClient;
}
private async Task<string> AcquireTokenAsyncForUser(string resource, string tenantId)
{
AuthenticationResult authenticationResult = await GetAccessToken(resource, tenantId);
_accessCode = authenticationResult.AccessToken;
return _accessCode;
}
private async Task<AuthenticationResult> GetAccessToken(string resource, string tenantId)
{
string authority = appConfig.Authority;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(appConfig.ClientId, appConfig.ClientSecret);
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
string userAccessToken = authHeader.Substring(authHeader.LastIndexOf(' ')).Trim();
UserAssertion userAssertion = new UserAssertion(userAccessToken);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, credential, userAssertion);
return authenticationResult;
}
However if I change the SendEmailAsUserAsync method as shown below, the email is sent but an InvalidOperationException is thrown with message "The complex type 'System.Object' has no settable properties."
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
//await graphClient.Me.SendMailAsync(m, true); //This did not work
var user = await graphClient.Me.ExecuteAsync();
await user.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
Can any one point out if there is something wrong here.
Check the example project below, this has a working example (after you fill in the ClientID etc. in app.config).
Office 365 API demo applications
For sending email it uses the function below, which works if you set it up correctly. It also has a number of functions for Authenticating using Authorization Code Grant Flow.
public async Task SendMail(string to, string subject, string body)
{
var client = await this.AuthenticationHelper
.EnsureOutlookServicesClientCreatedAsync(
Office365Capabilities.Mail.ToString());
Message mail = new Message();
mail.ToRecipients.Add(new Recipient()
{
EmailAddress = new EmailAddress
{
Address = to,
}
});
mail.Subject = subject;
mail.Body = new ItemBody() { Content = body, ContentType = BodyType.HTML };
await client.Me.SendMailAsync(mail, true);
}
Actually, there is no assembly wrapper for the graph API.
Microsoft.Graph.dll is deprecrated.
So, you should to :
Deal with the REST requests : See here : http://graph.microsoft.io/docs/api-reference/v1.0/api/message_send
Generate a wrapper with Microsoft.Vipr project : see here : https://github.com/microsoft/vipr
For the authentication, ADAL works fine :)

Resources