We are using Microsoft Graph SDK. Implemented a POC in console application where the code works fine but when added this code in MVC its not working. code stucks at await call
Calling from controller as
[HttpPost]
public ActionResult InviteUser([Bind(Include = "EmailId")] UserLogin userLogin)
{
if (ModelState.IsValid)
{
string result = AzureADUtil.InviteUser(userLogin.EmailId);
}
return View();
}
Method implementation is as below
public static string InviteUser(string emailAddress)
{
string result = string.Empty;
result = InviteNewUser(emailAddress).Result;
return result;
}
private static async Task<string> InviteNewUser(string emailAddress)
{
string result = string.Empty;
try
{
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
// Send Invitation to new user
var invitation = new Invitation
{
InvitedUserEmailAddress = emailAddress,
InviteRedirectUrl = "https://myapp.com",
SendInvitationMessage = true,
InvitedUserType = "Member"
};
// It stucks at this line
await graphClient.Invitations
.Request()
.AddAsync(invitation);
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
Mixing async-await and blocking code like .Result or .Wait() tends to lead to deadlocks, especially on asp.net-mvc.
If going async then go all the way.
[HttpPost]
public async Task<ActionResult> InviteUser([Bind(Include = "EmailId")] UserLogin userLogin) {
if (ModelState.IsValid) {
string result = await AzureADUtil.InviteUser(userLogin.EmailId);
}
return View();
}
With implementation refactored to be async as well
public static async Task<string> InviteUser(string emailAddress)
{
string result = string.Empty;
result = await InviteNewUser(emailAddress);
return result;
}
InviteUser is now redundant since it basically wraps the private InviteNewUser call.
Reference Async/Await - Best Practices in Asynchronous Programming
The best would be to update your code to run async the whole way down the request chain. You could do that as follows:
[HttpPost]
public async Task<ActionResult> InviteUser([Bind(Include = "EmailId")] UserLogin userLogin)
{
if (ModelState.IsValid)
{
string result = await AzureADUtil.InviteNewUser(userLogin.EmailId).ConfigureAwait(false);
}
return View();
}
Related
I want to make an Post api call. the get method works without any problems, but the post call doesnt find the route. idk why...
Here's the Controller
[Authorize(Roles = AppRoles.Supervisor)]
[Route("api/[controller]/[action]")]
[ApiController]
public class SupportUserManagementController : ControllerBase
{
private readonly SupportUserService _supportUsersService;
public SupportUserManagementController(SupportUserService supportUserService)
{
this._supportUsersService = supportUserService;
}
// GET: api/GetSupportUsersListAsync
[HttpGet]
[ActionName("Test2")]
[Authorize(Roles = AppRoles.User)]
[ProducesResponseType(typeof(IEnumerable<ScopeSupportUsers>), StatusCodes.Status200OK)]
[SwaggerOperation("GetSupportUsers")]
public async Task<IEnumerable<ScopeSupportUsers>> GetSupportUsers(int id)
{
var result = await _supportUsersService.GetSupportUsersListAsync(id);
return result;
}
[HttpPost]
[ActionName("Test3")]
[Authorize(Roles = AppRoles.User)]
[ProducesResponseType(typeof(Guid?), StatusCodes.Status200OK)]
[SwaggerOperation("CreateSupportUsers")]
public async Task<Guid?> CreateSupportUsers(int id, ScopeSupportUsers user)
{
var existingUser = await GetSupportUsers(id);
var matches = existingUser.Where(u => u.Name == user.Name);
if (matches.Count() != 0)
{
return Guid.Empty;
}
var resultGuid = await _supportUsersService.CreateSupportUserAsync(id, user);
if (resultGuid != null)
{
var resultCertificate = await SetCertificate_1Async(id, user, (Guid)resultGuid);
if (resultCertificate == true)
return resultGuid;
}
return null;
}
Why does this work:
using var client = await httpClientHelper.GetHttpClientWithAccessToken();
string uri = $"{_serviceClientOptions.Value.BetriebstoolServiceAddress}/api/SupportUserManagement/Test2?id={selectedScope}";
supportUserResult = await client.GetFromJsonAsync<IEnumerable<ScopeSupportUsers>>(uri);
And this doesnt work?
using var client = await httpClientHelper.GetHttpClientWithAccessToken();
string uri = $"{_serviceClientOptions.Value.BetriebstoolServiceAddress}/api/SupportUserManagement/Test3?id={selectedScopeToCopyTo}";
var response = await client.PostAsJsonAsync<ScopeSupportUsers>(uri , user);
If i am debugging the GET works fine but at the POST Method it doesnt find the "way" to the controller"
This Breakpoint never reached :(
I've implemented a HttpResponseExceptionFilter in Asp.Net Core Web Api to handle errors and format them in a fixed format json string.Here is my code:
public class HttpResponseExceptionFilter : IAsyncExceptionFilter
{
private readonly ILogger<HttpResponseExceptionFilter> _logger;
public HttpResponseExceptionFilter(ILogger<HttpResponseExceptionFilter> logger)
{
_logger = logger;
}
public Task OnExceptionAsync(ExceptionContext context)
{
if (context.Exception == null)
return Task.CompletedTask;
switch (context.Exception)
{
case BadHttpRequestException ex:
context.Result = new JsonResult(new BaseResponse<object?>()
{
StatusCode = ex.StatusCode,
Message = ex.Message,
Data = null
})
{
StatusCode = ex.StatusCode
};
break;
default:
_logger.LogError(context.Exception, "error");
context.Result = new JsonResult(new BaseResponse<object?>()
{
StatusCode = 500,
Message = context.Exception.Message,
Data = null
})
{
StatusCode = 500
};
break;
}
context.ExceptionHandled = true;
return Task.CompletedTask;
}
}
and add it in program.cs:
builder.Services.AddControllers(options =>
{
options.Filters.Add<HttpResponseExceptionFilter>();
});
then I throw an exception in controller action(for example):
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register(UserRegisterRequest registerRequest)
{
throw new BadHttpRequestException("error details", 500);
User user = await _userService.Register(registerRequest);
return JsonResult(user);
}
[HttpPost]
[Route("Login")]
public IActionResult Login(string account, string password)
{
throw new BadHttpRequestException("error details", 500);
string token = _userService.Login(account, password);
return JsonResult(new LoginResponse() { Token = token });
}
then, it will work with Register method but doesn't work with Login method and if I convert Login method into public async Task<IActionResult> Login(string account, string password) it works.
So are there reasons to explain this?And how can I make it work with synchronized action?
By the way I've tried using UseExceptionHandler and it performs as the same way.
I have seen some of the existing questions regarding async waiting for completion , However for me none of the solution work.
I am using a C# wrapper for connecting to sales force https://github.com/developerforce/Force.com-Toolkit-for-NET/
In the below method i want to wait for the method UsernamePasswordAsync to complete execution so that i can get the values from the auth object.
public async Task<Token> GetTokenForSalesForce()
{
Token token = null;
try
{
var auth = new AuthenticationClient();
await auth.UsernamePasswordAsync(configuration.Value.ClientId, configuration.Value.ClientSecert,
configuration.Value.SFUsername, configuration.Value.SFPassword,
configuration.Value.SFBaseUrl);
if (!string.IsNullOrEmpty(auth.AccessToken) && !string.IsNullOrEmpty(auth.InstanceUrl))
{
token = new Token
{
BearerToken = auth.AccessToken,
InstanceURL = auth.InstanceUrl,
ApiVersion = auth.ApiVersion
};
}
}
catch (Exception ex)
{
throw ex;
}
return token;
}
public async Task<List<SFDashboardResponse>> GetOrderCountFromSalesForce(Token token)
{
List<SFDashboardResponse> sFDashboardResponses = new List<SFDashboardResponse>();
try
{
var client = new ForceClient(token.InstanceURL, token.BearerToken, token.ApiVersion);
var response = await client.QueryAsync<SFDashboardResponse>("SELECT something ");
var records = response.Records;
}
catch(Exception e)
{
}
return sFDashboardResponses;
}
The signature in the library is
public async Task WebServerAsync(string clientId, string clientSecret, string redirectUri, string code, string tokenRequestEndpointUrl)
{
}
The problem is while the method wait for await to be first execute another thread executes the other part of the orignal caller.
I call it from here
public IActionResult post()
{
var authtoken = _salesForceService.GetTokenForSalesForce();
var response = _salesForceService.GetOrderCountFromSalesForce(authtoken.Result);
DashboardModel dashboardModel = null;
if (authtoken.Status == TaskStatus.RanToCompletion)
{
fill the object
}
return Ok(dashboardModel);
}
You can wrap the IActionResult with a Task and await on the tasks below.
public async Task<IActionResult> post()
{
var authtoken = await _salesForceService.GetTokenForSalesForce();
var response = await _salesForceService.GetOrderCountFromSalesForce(authtoken);
DashboardModel dashboardModel = //fill the object
return Ok(dashboardModel);
}
At least this is what you are asking for as far as I understand, if its another problem let me know.
EDIT 1:
This is just my suggestion/opinion.
Personally I dont really like having the code wrapped in try-catch everywhere, this way the code can be hard to read and maintain. You really should consider centralizing exception handling in one place, you could have a base controller or just a middleware like this one:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context, ILogger logger)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, logger);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
logger.Log(exception);
//do something
return context.Response.WriteAsync(... something ...); //Maybe some JSON message or something
}
}
The you just register it as a middleware in the Configure method like below:
app.UseMiddleware<ErrorHandlingMiddleware>();
I am confused on where my deserialization logic should go.
I have a controller that returns data to the client specifically for a GET operation:
public accountscontroller:apicontroller
{
[HttpGet]
[Route("", Name = "GetAccount")]
public async Task<IHttpActionResult> GetAccount()
{
var query = Request.RequestUri.PathAndQuery.Split('/')[2];
var response = await _accountService.GetAccount(query);
if (response == null)
{
return NotFound();
}
return Ok(response);
}
//morestuff
}
and the AccountService.GetAccount code is the following:
public class AccountService
{
public async Task<Account> GetAccount(string query)
{
var task = await Client.HTTPCLIENT.GetAsync(Client.HTTPCLIENT.BaseAddress + query);
var jsonString = await task.Content.ReadAsStringAsync();
var value = JsonConvert.DeserializeObject<RootObject>(jsonString);
return value.value.FirstOrDefault();
}
//morestuff
}
as you can see, the deserialization is handled in the AccountService, not the AccountsController
however, if we look at the POST operation:
public class AccountController
{
[HttpPost]
[Route("", Name = "CreateAccount")]
public async Task<IHttpActionResult> CreateAccount([FromBody] JObject account)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var response = await _accountService.Create(account);
var newAccount = await response.Content.ReadAsAsync<object>();
return Ok(newAccount);
}
and the underlying Create method within the AccountService:
public async Task<HttpResponseMessage> Create(JObject account)
{
var request = new HttpRequestMessage(HttpMethod.Post, Client.HTTPCLIENT.BaseAddress + "accounts")
{
Content = new StringContent(account.ToString(), Encoding.UTF8, "application/json")
};
var response = await Client.HTTPCLIENT.SendAsync(request);
var uri = new Uri(response.Headers.GetValues("OData-EntityId").FirstOrDefault());
return await Client.HTTPCLIENT.GetAsync(uri);
}
you will see that in fact the deserialization happens on the controller level.
How can I encapsulate the deserialization logic for CRUD operations, such as GET/PUT/POST for consistency?
I have been stuck on a case for a while now, and i hope someone can help me out.
I am trying to receive data from an EventHub from Azure and to display these data (in real-time) on an ASP.NET page, using MVC.
I found a way to create an async method which would await to receive an event, and i refreshed this method and partial view with jQuery each second, but i found that this gave me too much instability over time and it would end in an endless loop of errors.
Here is my code:
public async Task<ActionResult> Index()
{
time = DateTime.Now;
list = new List<TestEntity>();
TestEntity t = new TestEntity();
list.Add(t);
await Initialize();
return View(list);
}
public async Task<ActionResult> RefreshLatest()
{
try
{
var message = await consumer.ReceiveAsync();
if (message != null)
{
TestEntity t = JsonConvert.DeserializeObject<TestEntity>(Encoding.UTF8.GetString(message.GetBytes()));
list.Add(t);
}
}
catch (Exception exception)
{
Console.WriteLine("exception on receive {0}", exception.Message);
}
return PartialView("Latest", list);
}
private static async Task Initialize()
{
EventHubClient eventHubClient = getEventHubClient(SharedAccessKeyName, SharedAccessKey, NamespaceURI, EventHubName, ConnectionString);
EventHubConsumerGroup consumerGroup = eventHubClient.GetDefaultConsumerGroup();
consumer = await consumerGroup.CreateReceiverAsync(partitionId, DateTime.Now, receiverEpoch); // All messages
}
public static EventHubClient getEventHubClient(string SharedAccessKeyName, string SharedAccessKey, string NamespaceURI, string EventHubName, string ConnectionString)
{
//Create EventHub
TokenProvider td = TokenProvider.CreateSharedAccessSignatureTokenProvider(SharedAccessKeyName, SharedAccessKey);
NamespaceManager manager = new NamespaceManager(NamespaceURI, td);
var description = manager.CreateEventHubIfNotExists(EventHubName);
//Create EventHubClient
EventHubClient client = EventHubClient.CreateFromConnectionString(ConnectionString + ";EntityPath=" + EventHubName);
return client;
}
And here is my Index View:
http://puu.sh/gDQUj/cb816b8870.png
I hope you can see what i'm trying to accomplish and how this fails.