Application Insights: Console Application HttpClient correlation not working - asp.net-core

I have a Console Application which I plan to use Application Insights to start telemetry. This Console App calls a Web API within it.
Operation correlation works, but the Parent hierarchy does not. Essentially, the Parent of the Web API call is not the initial call from Console Application.
Below is my code:
Console App
static async Task SendHttpOnly()
{
//Create TelemetryClient
TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault();
configuration.InstrumentationKey = "<id>";
var telemetryClient = new TelemetryClient(configuration);
RequestTelemetry requestTelemetry = new RequestTelemetry { Name = "ConsoleTest" };
var operation = telemetryClient.StartOperation(requestTelemetry);
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:37970/");
var responseTask = await client.PostAsJsonAsync<MessageDto>("MessageReceiver", new MessageDto() { Body = "Test" });
}
}
catch (Exception e)
{
operation.Telemetry.Success = false;
telemetryClient.TrackException(e);
throw;
}
finally
{
telemetryClient.StopOperation(operation);
telemetryClient.Flush();
Task.Delay(5000).Wait();
}
}
Web API
[HttpPost]
public string Post([FromBody] MessageDto dto)
{
_telemetryClient.TrackTrace($"Service Bus Message Processed: Message: {dto.Body}");
return $"Processed { dto.Body }";
}
Weird thing is, if I do a Web API to Web API call, it logs it properly. Even with the same code; the 2nd Web API call parent is the 1st Web API call.

Thankyou Water. Glad that you resolved your issue and posting the same as an answer so that it will be helpful for other community members.
Application HttpClient correlation not working because of using wrong Nuget package instead of using below package
Microsoft.ApplicationInsights.AspNetCore
We need to use the below package
Microsoft.ApplicationInsights.WorkerService
Below is the sample code for using SDK in application insights.
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.13.1" />
</ItemGroup>
For more information check the Application Insight worker service.

Related

Blazor Web Assembly App with Azure B2C is always trying to authenticate as soon as page is loaded

I am adding support for Azure AD B2C to a Blazor WebAssembly App, I followed the instructions here
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-azure-active-directory-b2c?view=aspnetcore-3.1#client-app-configuration
however, the application is always trying to authenticate as soon as I load the page,
which does not allow for a public anonymous section of the site.
Is there any solution to this problem?
The default httpClient requires authorization so even making a call to see if a person is authorized causes the code to prompt the user to log in kicks in.
So to get around this, in the Program.cs file (in the Client project), I created a httpClient that allows anonymous requests
// This allows anonymous requests
// See: https://learn.microsoft.com/en-us/aspnet/core/security/blazor/webassembly/additional-scenarios?view=aspnetcore-3.1#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client
builder.Services.AddHttpClient("ServerAPI.NoAuthenticationClient", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
This example should help:
https://github.com/ADefWebserver/SyncfusionHelpDeskClient/blob/main/Client/Pages/Index.razor
It calls the NoAuthenticationClient httpClient
protected override void OnInitialized()
{
// Create a httpClient to use for non-authenticated calls
NoAuthenticationClient =
ClientFactory.CreateClient(
"ServerAPI.NoAuthenticationClient");
}
public async Task HandleValidSubmit(EditContext context)
{
try
{
// Save the new Help Desk Ticket
// Create a new GUID for this Help Desk Ticket
objHelpDeskTicket.TicketGuid =
System.Guid.NewGuid().ToString();
await NoAuthenticationClient.PostAsJsonAsync(
"SyncfusionHelpDesk", objHelpDeskTicket);
// Send Email
HelpDeskEmail objHelpDeskEmail = new HelpDeskEmail();
objHelpDeskEmail.EmailType = "Help Desk Ticket Created";
objHelpDeskEmail.EmailAddress = "";
objHelpDeskEmail.TicketGuid = objHelpDeskTicket.TicketGuid;
await NoAuthenticationClient.PostAsJsonAsync(
"Email", objHelpDeskEmail);
// Clear the form
objHelpDeskTicket = new HelpDeskTicket();
// Show the Toast
ToastContent = "Saved!";
StateHasChanged();
await this.ToastObj.Show();
}
catch (Exception ex)
{
ToastContent = ex.Message;
StateHasChanged();
await this.ToastObj.Show();
}
}

RestRequest Body not received in .net core web api

I am trying to build a service client to simplify calling my microservices in .net core.
Here is a service client sample:
public ProductServiceClient(SystemEnvironment.MachineEnvironment? environment = null)
{
this.url = ServiceEnvironment.Urls.GetUrl(ServiceEnvironment.Service.Product, environment);
}
private RestClient GetClient(string method)
{
return new RestClient(url + "/api/" + method);
}
private RestRequest GetRestRequest(Method method)
{
var restRequest = new RestRequest(method);
restRequest.RequestFormat = DataFormat.Json;
restRequest.AddHeader("Content-Type", "application/json");
return restRequest;
}
public FindProductsResponse FindProducts(FindProductsRequest request)
{
var restRequest = GetRestRequest(Method.GET);
restRequest.AddJsonBody(request);
var client = this.GetClient("Products");
var restResponse = client.Get(restRequest);
return new JsonDeserializer().Deserialize<FindProductsResponse>(restResponse);
}
public void Dispose()
{
}
And here is how I am trying to read it in my .net core api:
[HttpGet]
public ActionResult<FindProductsResponse> Get()
{
var request = "";
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
request = reader.ReadToEnd();
}
var buildRequest = JsonConvert.DeserializeObject<FindProductsRequest>(request);
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
However the request variable is always empty after Request.Body has been processed by the StreamReader.
If I make the same request from Postman (also using GET), I get the body just fine.
What am I doing wrong here?
EDIT: This is the unit test calling the api:
[Test]
public void Test1()
{
using (var productServiceClient = new ProductServiceClient())
{
var products = productServiceClient.FindProducts(new FindProductsRequest()
{
Id = 50
}).Products;
}
}
It can be your Request.Body has been already consumed.
Try to call Request.EnableRewind() before to open the StreamReader.
I'm not sure why you are manually doing it. It looks like you are reinventing the wheel. ASP.NET Core already does that for you.
This is what your service should look like:
[HttpGet] // oops, GET requests will not allow Bodies, this won't work
public ActionResult<FindProductsResponse> Get([FromBody]FindProductsRequest buildRequest)
{
// skip all the serialization stuff, the framework does that for you
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
And if you don't want to redo all the busy work that is retyping all the code on the client side, I suggest you read up on swagger (probably in the form of Swashbuckle). Client code can be generated. Even from within Visual Studio, if you right-click on the project and in the context menu pick "Add REST API Client...". Please don't erroneously hand-code what can be generated flawlessly by a machine instead. I don't really know what went wrong in your specific case, but searching bugs that could be avoided altogether is just busywork, that time should be spent on other parts of the program.
I just realized this is a GET request. ASP.NET will not recognize bodies for GET-Requests. You will need to make it a PUT or POST request or put your parameters in the query string.
If you happen to make that mistake as often as I did, you might want to write some unit tests that cover this. Because .NET is not helping you there. Been there, done that..

Retrieving ITokenService from CRM Workflow context

Currently i'm working on a CRM project. In this project i have to send data to web service. This operation must be work on custom workflow. In order to make web service call i need Authentication token, Currently i am trying to get ITokenService from CodeActivityContext, which is retrieving null, Any suggestions how to get ITokenService in workflow context?
>
protected override void Execute(CodeActivityContext executionContext)
{
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
ITokenService TokenService = (ITokenService)executionContext.GetExtension<ITokenService>();
if(TokenService == null) throw new InvalidOperationException("Token service is null");
serviceFactory.CreateOrganizationService(context.UserId)
try
{
//do something
}
catch
{
//do something
}
}

Xamarin Portable Class Library Gets Proxy Access Denied on iPhone Simulator

I've run into a bit of an issue with the iPhone simulator when trying to access a WCF REST service.
I've asked the question on the Xamarin forums, but no joy.
Some context:
I have a PCL for a Xamarin cross platform project, in VS 2012.
I use the Portable Microsoft HttpClient package and the Json.NET package.
I have a pretty simple WCF REST service sitting in the background.
When testing
I can access the service fine from a browser on the dev machine.
I can access it fine using a console application going via the PCL.
I can access it fine via the app, from a real android device on the WiFi network of
the same corporate network.
I can access it fine from Safari on the build Mac.
I can access it fine from Safari on the iPhone simulator on the build Mac.
The issue is, as soon as I try to access the service via the app on the iPhone simulator, I get a 407, Proxy Access Denied error.
Here is the code I'm using to set up the connection:
private static HttpRequestMessage PrepareRequestMessage(HttpMethod method, string baseUri,
string queryParameters, out HttpClient httpClient, string bodyContent)
{
var finalUri = new Uri(baseUri + queryParameters);
var handler = new HttpClientHandler();
httpClient = new HttpClient(handler);
var requestMessage = new HttpRequestMessage(method, finalUri);
if (handler.SupportsTransferEncodingChunked())
{
requestMessage.Headers.TransferEncodingChunked = true;
}
if (method == HttpMethod.Post || method == HttpMethod.Put)
{
requestMessage.Content =
new StringContent(bodyContent, Encoding.UTF8, "application/json");
}
return requestMessage;
}
That code gives me the 407 error.
I have tried setting the proxy by using various combinations of SupportsProxy and SupportsUseProxy. (Both returning false from the simulator.)
I've tried forcing the proxy settings regardless. I've tried setting the credentials on the handler itself. I've tried playing with the UseDefaultCredentials and UseProxy flags. I've also tried setting the IfModifiedSince value in the message header. I've tried using the PortableRest package as well.
All of that only seemed to make things worse. Where I was initially getting the 407 error, the call to httpClient.GetAsync would just immediately return null.
I am at a bit of a loss here, so any help would be greatly appreciated.
PS. For completeness, the rest of the surrounding code that makes the call: (please forgive crappy exception handling, I'm still playing around with the errors)
public static async Task<T> SendRESTMessage<T>(HttpMethod method, string baseUri,
string queryParameters, T contentObject)
{
HttpClient httpClient;
var payload = string.Empty;
if (contentObject != null)
{
payload = JsonConvert.SerializeObject(contentObject);
}
var requestMessage =
PrepareRequestMessage(method, baseUri, queryParameters, out httpClient, payload);
HttpResponseMessage responseMessage = null;
try
{
if (method == HttpMethod.Get)
{
responseMessage = await httpClient.GetAsync(requestMessage.RequestUri);
}
else
{
responseMessage = await httpClient.SendAsync(requestMessage);
}
}
catch (HttpRequestException exc)
{
var innerException = exc.InnerException as WebException;
if (innerException != null)
{
throw new Exception("Unable to connect to remote server.");
}
}
return await HandleResponse<T>(responseMessage);
}
private static async Task<T> HandleResponse<T>(HttpResponseMessage responseMessage)
{
if (responseMessage != null)
{
if (!responseMessage.IsSuccessStatusCode)
{
throw new Exception("Request was unsuccessful");
}
var jsonString = await responseMessage.Content.ReadAsStringAsync();
var responseObject = JsonConvert.DeserializeObject<T>(jsonString);
return responseObject;
}
return default(T);
}
This was my attempt at implementing IWebProxy quick and dirty, which I think could have made things worse:
public class MyProxy : IWebProxy
{
private System.Net.ICredentials creds;
public ICredentials Credentials
{
get
{
return creds;
}
set
{
creds = value;
}
}
public Uri GetProxy(Uri destination)
{
return new Uri("proxy addy here");
}
public bool IsBypassed(Uri host)
{
return true;
}
}
Thanks again for taking the time to read my question.
So I finally got it working.
Turns out it was something really stupid, but being new to iOS mobile dev and the fact that the service worked via Safari on the simulator threw me for a loop.
I read that the simulator uses the proxy settings as defined on the Mac. So I went to the network settings and added the service address to the proxy bypass list.
Works like a charm now.
If anybody feels there is a better way to do this, please add your opinions.

How to pass a service header when calling a WCF Service on a WinRT app

I got two projects on my solution - a Console Application project and a WinRT project.
I need to consume a WCF Service, what I can perfectly do through my Console Application simply using the following lines:
static void Main(string[] args)
{
var client = new ServiceReference1.ComeAndGoClient("BasicHttpBinding_IComeAndGo");
var header = new ServiceReference1.ServiceHeader{ AccountID = "a1", DeviceID = "d1" };
var res = client.GetEmployees(ref header, "");
}
The problem is - I'm not finding out how can I send the same service header (AccountID and DeviceID) when calling the same method through my WinRT app.
var client = new ServiceReference1.ComeAndGoClient(ServiceReference1.ComeAndGoClient.EndpointConfiguration.BasicHttpBinding_IComeAndGo);
public async Task LoadData()
{
var sessions = await client.GetEmployeesAsync("");
}
Note: On my WinRT project I don't have any GetEmployeesAsync method which gets ServiceHeader as a parameter like I have GetEmployees on my Console Application project.
I had added the same service reference to both projects, though.