Accessing a DbContext dynamically - c#

I have an application used to display location data to a user. This data can come from multiple sources (e.g. a WebApi or a LocalDatabase). Therefore I have a repository which manages all the data from these sources.
Each repository implements the following interface
interface IRepository<TKey, TValue> : IDiscoverable, ILocalizable
{
IDictionary<TKey, TValue> FindAll();
TValue Find(TKey identifier);
// Additional methods left out.
}
A concrete implementation (in this case for a WebApi request) then creates a request which gets executed by a ServiceClient. This service client returns a response to the Repository which passes said response to a converter that converts the data into a format used by the application.
Now my problem is the Request part. Not for the WebApi, but for database requests.
I currently have one DbContext for the Locations database, but more will follow when the application gets extended. And herein lies the crux: Writing a ServiceClient for Api requests is easy, since I only have to query a webpage and fetch the JSON. The URL can therefore be built by joining multiple strings. So a Request can look like this:
public interface IRequest
{
/// <summary>Gets the resource path.</summary>
string Resource { get; }
/// <summary>Gets the request parameters.</summary>
/// <returns>A collection of parameters.</returns>
IEnumerable<KeyValuePair<string, string>> GetParameters();
/// <summary>Gets additional path segments for the targeted resource.</summary>
/// <returns>A collection of path segments.</returns>
IEnumerable<string> GetPathSegments();
}
How can I achieve a similar approach with multiple DbContext?
I understand that I have to pass the instance of the DbContext I want to query to the service manager. But how can I query the DbSet properties without writing a Service manager for each DbContext?
Is this eve possible, or would I have to scrap my design altogether?
Here are some additional information, This is my current repository implementation. I commented out the code used to query a web api. But I'd like my database querying code to look almost the same.
To clarify: explicit interface implementation is the method the rest of the program calls to get the data. The user is able to switch out the type of repository, thus giving the ability to choose from different data sources.
Inside the method I'll generate a request. If the data source is a web api the request holds information to build an URL to the api endpoint (the Url is built and queried by the ServiceClient). The ServiceClient then returns a response implementing the IResponse interface (see below). The response then is passed to a converter which converts the response to POCOs used by the rest of the application.
What I want to do: Build a request class that allows me to dynamically select the DbContext used to access the database. For that I need a way to pass the DbContext inside the Request, so the ServiceClient can use it. I further need to pass which properties to use so I can get the data, since not every endpoint has the same Properties.
Here is the code I have so fat:
public interface IResponse<T> : ILocalizable
{
/// <summary>Gets or sets the response content.</summary>
T Content { get; set; }
/// <summary>Gets or sets the <see cref="DateTimeOffset"/> at which the message originated..</summary>
DateTimeOffset Date { get; set; }
/// <summary>Gets or sets a collection of custom response headers.</summary>
IDictionary<string, string> ExtensionData { get; set; }
}
public interface ILocationRepository : IRepository<Guid, Location>
{
}
public class LocationRepository : ILocationRepository
{
private readonly LocationContext context;
private readonly IConverter<IResponse<ICollection<LocationDataContract>>, IDictionary<Guid, Location>> responseConverter;
public LocationRepository()
{
this.context = new LocationContext();
var locationConverter = new LocationConverter();
this.responseConverter = new DictionaryResponseConverter<LocationDataContract, Guid, Location>(locationConverter, location => location.Id);
}
IDictionary<Guid, Location> IRepository<Guid, Location>.FindAll()
{
// This is how the request against a web api looks like.
// This works without a doubt and totally flawless.
// IItemRepository self = this;
// var request = new ItemDetailsRequest
// {
// Identifier = identifier.ToString(NumberFormatInfo.InvariantInfo),
// Culture = self.Culture
// };
// var response = this.serviceClient.Send<ItemDataContract>(request);
var locations = this.context.Locations.ToList();
var cityDetails = this.context.CityDetails.ToList();
var planetDetails = this.context.PlanetDetails.ToList();
var systemDetails = this.context.SystemDetails.ToList();
IEnumerable<LocationsDetails> details = cityDetails.Cast<LocationsDetails>().Concat(planetDetails).Concat(systemDetails).ToList();
foreach (var location in locations)
{
location.Details = details.SingleOrDefault(detail => detail.Id == location.Id);
}
var response = new Response<ICollection<LocationDataContract>>
{
Content = locations
};
var service = new DatabaseServiceClient<LocationContext>();
return this.responseConverter.Convert(response);
}
Location IRepository<Guid, Location>.Find(Guid identifier)
{
throw new NotImplementedException();
}
}

Related

How can I create a variable that is globally available but transient per REST request

I'm thinking that perhaps my entire idea of how to approach this is wrong, so let me explain what I'm trying to do.
I have a UserId that is a property contained within my JWT token.
On many of my REST endpoints, I need to read that UserId to use it within my DB queries.
I implemented a filter which intercepts all of my calls and decodes my JWT and assigns the UserId value into a static Globals class that I had created.
I just realised now though, that that class is GLOBAL. As in, the values are actually shared across the entire server for anybodies REST requests.
I intended for the value to essentially just be transiently available for the duration of each individual request.
How can I change my implementation so that I can globally access the UserId contained in the JWT token for the current request.
My suggestion is to make some kind of abstraction e.g ICurrentUser and make an implementation, which will take UserId from HttpContext.
// Define in Domain/Application project
public interface ICurrentUser
{
public string? Id { get; set; }
}
// Implement in ASP.NET project
public class CurrentUser : ICurrentUser
{
public CurrentUser(IHttpContextAccessor contextAccessor)
{
var user = contextAccessor.HttpContext?.User;
if (user == null)
{
return;
}
Id = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue(JwtClaimTypes.Subject);
}
public string? Id { get; set; }
}
Also, don't forget to add .AddHttpContextAccessor() call for you services
If you want something to be available for the duration of an individual request I would recommend using a service registered as scoped see Scoped Services
But lets start from the beginning. First implement a service itself like:
public UserService : IUserService
{
private readonly IHttpContextAccessor _accessor;
/// inject the `IHttpContextAccessor` to access the actual
/// request / token / headers etc.
public UserService(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public async Task<string> GetUserIdAsync()
{
var userId = await GetUserIdFromTokenAsync();
return userId;
}
private Task<string> GetUserIdFromTokenAsync()
{
/// Add your logic here to get or parse the
/// user id from the token or do some other stuff to get the user id.
/// ... or get the user id from the current User object claim
/// depends on your auth settings `_accessor.HttpContext?.User`
var token = _accessor... // from headers?
return userId;
}
}
/// Always use an interface to make it well testable and mockable for unit tests
public interface IUserService
{
Task<string> GetUserIdAsync();
}
Then in your dependency injection part (Startup.cs or Program.cs depends which tempate you have selected).
/// register the `IHttpContextAccessor` to be able to inject it.
services.AddHttpContextAccessor();
/// register your `UserService` as scoped!
services.AddScoped<IUserService, UserService>();
Now you can use this in all your services and controllers (which are at least also registered as scoped). This will resolve the service per request.
/// In a data service
class YourDataService
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public YourDataService(IUserService service)
{
_userService = service
}
public async Task DoYourQueryStuffAsync()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
}
}
/// The same applies for a controller
[ApiController]
[Route("values")]
public class ValuesController : ControllerBase
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public ValuesController(IUserService service)
{
_userService = service
}
[Authorized]
[HttpGet]
public async Task<IActionResult> Query()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
var queryresult = await ...
return Ok(queryresult);
}
}
Notes at the end:
Do not fall into the trap to consume scoped services from a singleton service this is not working because singletons are persistent without the request context.
Documentation links:
ASP.net Core Dependency Injection
UserId in Bearer

Setting a per-request proxy (or rotating proxies) with .NET Flurl/HttpClient

I know that with the Flurl HTTP .NET library I can set a global proxy by using a custom HttpClientFactory, but is there a way to choose a custom proxy for each request?
With many other programming languages, setting a proxy is as easy as setting an option. For example, with Node.js I can do:
const request = require('request');
let opts = { url: 'http://random.org', proxy: 'http://myproxy' };
request(opts, callback);
The ideal way to do that with Flurl would be something like this, which is currently not possible:
await "http://random.org".WithProxy("http://myproxy").GetAsync();
I also know that creating a FlurlClient/HttpClient for every request is not an option, because of the socket exhaustion issue, which I've experienced myself in the past as well.
The scenario for this is when you need to have a pool of proxies that are rotated in some way, so that each HTTP request potentially uses a different proxy URL.
So after some discussion with the Flurl creator (#228 and #374), the solution we'come up with is to use a custom FlurlClient manager class, which would be in charge of creating the required FlurlClients and the linked HttpClient instances. This is needed because each FlurlClient can only use one proxy at a time, for limitations of how the .NET HttpClient is designed.
If you're looking for the actual solution (and code), you can skip to the end of this answer. The following section still helps if you want to understand better.
[UPDATE: I've also built an HTTP client library that takes care of all the stuff below, allowing to set a per-request proxy out of the box. It's called PlainHttp.]
So, the first explored idea was to create a custom FlurlClientFactory that implements the IFlurlClientFactory interface.
The factory keeps a pool of FlurlClients, and when a new request needs to be sent, the factory is invoked with the Url as the input parameter. Some logic is then performed to decide whether the request should go through a proxy or not. The URL could potentially be used as the discriminator for choosing the proxy to use for the particular request. In my case, a random proxy would be chosen for each request, and then a cached FlurlClient would be returned.
In the end, the factory would create:
at most one FlurlClient per proxy URL (which will be then used for all the requests that have to go through that proxy);
a set of clients for "normal" requests.
Some code for this solution can be found here. After registering the custom factory, there would be not much else to do. Standard requests like await "http://random.org".GetAsync(); would be automagically proxied, if the factory decided to do so.
Unfortunately, this solution has a drawback. It turns out that the custom factory is invoked multiple times during the process of building a request with Flurl. According to my experience, it is called at least 3 times. This could lead to issues, because the factory might not return the same FlurlClient for the same input URL.
The solution
The solution is to build a custom FlurlClientManager class, to completely bypass the FlurlClient factory mechanism and keep a custom pool of clients that are provided on demand.
While this solution is specifically built to work with the awesome Flurl library, a very similar thing can be done using the HttpClient class directly.
/// <summary>
/// Static class that manages cached IFlurlClient instances
/// </summary>
public static class FlurlClientManager
{
/// <summary>
/// Cache for the clients
/// </summary>
private static readonly ConcurrentDictionary<string, IFlurlClient> Clients =
new ConcurrentDictionary<string, IFlurlClient>();
/// <summary>
/// Gets a cached client for the host associated to the input URL
/// </summary>
/// <param name="url"><see cref="Url"/> or <see cref="string"/></param>
/// <returns>A cached <see cref="FlurlClient"/> instance for the host</returns>
public static IFlurlClient GetClient(Url url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url));
}
return PerHostClientFromCache(url);
}
/// <summary>
/// Gets a cached client with a proxy attached to it
/// </summary>
/// <returns>A cached <see cref="FlurlClient"/> instance with a proxy</returns>
public static IFlurlClient GetProxiedClient()
{
string proxyUrl = ChooseProxy();
return ProxiedClientFromCache(proxyUrl);
}
private static string ChooseProxy()
{
// Do something and return a proxy URL
return "http://myproxy";
}
private static IFlurlClient PerHostClientFromCache(Url url)
{
return Clients.AddOrUpdate(
key: url.ToUri().Host,
addValueFactory: u => {
return CreateClient();
},
updateValueFactory: (u, client) => {
return client.IsDisposed ? CreateClient() : client;
}
);
}
private static IFlurlClient ProxiedClientFromCache(string proxyUrl)
{
return Clients.AddOrUpdate(
key: proxyUrl,
addValueFactory: u => {
return CreateProxiedClient(proxyUrl);
},
updateValueFactory: (u, client) => {
return client.IsDisposed ? CreateProxiedClient(proxyUrl) : client;
}
);
}
private static IFlurlClient CreateProxiedClient(string proxyUrl)
{
HttpMessageHandler handler = new SocketsHttpHandler()
{
Proxy = new WebProxy(proxyUrl),
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(10)
};
HttpClient client = new HttpClient(handler);
return new FlurlClient(client);
}
private static IFlurlClient CreateClient()
{
HttpMessageHandler handler = new SocketsHttpHandler()
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10)
};
HttpClient client = new HttpClient(handler);
return new FlurlClient(client);
}
}
This static class keeps a global pool of FlurlClients. As with the previous solution, the pool consists of:
one client per proxy;
one client per host for all the requests that mustn't go through the proxy (this is actually the default factory strategy of Flurl).
In this implementation of the class, the proxy is chosen by the class itself (using whatever policy you want, e.g. round robin or random), but it can be adapted to take a proxy URL as the input. In that case, remember that with this implementation clients are never disposed after they're created, so you might want to think about that.
This implementation also used the new SocketsHttpHandler.PooledConnectionLifetime option, available since .NET Core 2.1, to solve the DNS issues that arise when your HttpClient instances have a long lifetime. On .NET Framework, the ServicePoint.ConnectionLeaseTimeout property should be used instead.
Using the manager class is easy. For normal requests, use:
await FlurlClientManager.GetClient(url).Request(url).GetAsync();
For proxied requests, use:
await FlurlClientManager.GetProxiedClient().Request(url).GetAsync();

Autofac Wcf - Inject service depending on data within SOAP Request

I have a WCF Service with the following operation contract:
[OperationContract]
Response SearchEntities(Query query);
This operation takes a request that contains a specified Entity like so:
[DataContract]
public class Query
{
[DataMember]
public string SearchTerm { get; set; }
[DataMember]
public string Entity { get; set; }
[DataMember]
public bool ExactMatch { get; set; }
}
Based on the value contained within the Entity property, one the following properties is populated within this response:
[DataContract]
public class Response
{
[DataMember]
public List<Asset> Assets { get; set; }
[DataMember]
public List<Stage> Stages { get; set; }
[DataMember]
public List<Sector> Sectors { get; set; }
}
Terrible design, I know! However. I am using Autofac.Wcf as my service factory to inject dependencies. Normally I would use a common Interface and Generics to determine a service to use based on the Entity value like so:
public interface IEntitySearch<T>
{
Response Search(Query query);
}
The above interface would have several implementations for each of the Lists within the response. Using a design pattern such as a service location I could determine which service to use (all of which inherit from IEntitySearch<T>, something like:
public IEntitySearch ResolveSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
While this works, a more elegant solution (I believe) would be to customize the Autofac container per request for this particular operation, depending on the data contained within the request.
IE: Before the WCF pipe line sends the request to the service implementation, is it possible to examine the request data and customize how the container resolves dependencies. That way I can avoid exposing dependency resolution within my service layer.
Is this possible?
If another DI library other than Autofac has a solution for this, I will happily change our DI framework.
Thanks.
I haven't personally tried this but I think a direction you can go down is to combine:
Using OperationContext.Current to get the current request message data.
Specifying a custom IServiceImplementationDataProvider for Autofac that tells Autofac which WCF interface to host for that request.
Using a lambda registration for your service implementation to switch the backing service based on OperationContext.Current.
You can see two examples of the IServiceImplementationDataProvider by looking at the DefaultServiceImplementationProvider - the one that works in Autofac WCF hosting by default; andMultitenantServiceImplementationDataProvider, which is more about generating a proxy to enable multitenant WCF hosting.
While neither of these use OperationContext.Current to determine the actual backing service, you can build on the ideas:
Look at the Autofac.Multitenant.Wcf implementation. You may be able to use it as-is. The point of the instance data provider there is that WCF grabs on to the concrete type of the service being hosted and if you try to swap types out from under it, you get errors. The multitenant support fools WCF by creating a proxy type and your implementation type can be swapped out under the proxy. Note the MultitenantServiceImplementationDataProvider doesn't actually tie anything to a tenant or tenant ID; it's only about that proxy.
In your .svc file specify a service interface rather than any individual concrete implementation since you'll be swapping out the implementation.
Use a lambda registration to figure out your implementation.
Make sure your service is InstanceContextMode.PerCall to ensure things get swapped out on a per request basis.
The registration might look something like this:
builder.Register(ctx => {
var context = OperationContext.Current;
var type = DetermineTypeFromContext(context);
return ctx.Resolve(type);
}).As<IMyServiceInterface>();
The Autofac WCF and Autofac Multitenant section on WCF may also help.
In my opinion you're trying to move your problem just to another place. Why would making decision based on request at low-level WCF is better than switch in SearchEntities method? It's much worse ;-)
I would consider to use IEntitySearch factory/provider e.q.IEntitySearchProvider (it's not so much better but always).
public interface IEntitySearch
{
bool IsMatchQuery(Query query);
Response Search(Query query);
}
// without service locator
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IEnumerable<IEntitySearch> _searchers;
public EntitySearchProvider(IEnumerable<IEntitySearch> searchers)
{
_searchers = searchers;
}
public IEntitySearch GetSearcher(Query query)
{
// last registered
return _searchers.LastOrDefault(i=>i.IsMatchQuery(query))
?? throw new NotSupportedException();
}
}
or
public interface IEntitySearchProvider
{
IEntitySearch GetSearcher(Query query);
}
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IComponentContext _container;
public EntitySearchProvider(IComponentContext container)
{
_container = container;
}
public IEntitySearch GetSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
}
with
public class WcfService
{
private readonly IEntitySearchProvider _provider;
public WcfService(IEntitySearchProvider provider)
{
_provider = provider;
}
public Response SearchEntities(Query query)
{
var searcher = _provider.GetSearcher(query);
return searcher.Search(query);
}
}

Is it thread safe to create separate instance of dependent component in case of web api2 using structuremap

I have a web api 2 controller: TestController.cs and an action filter: TestAuthorizeAttribute.cs
I am using StructureMap.WebApi2 nuget package for Web API 2 project for setting the dependency injection.
I am trying to create instance of TestService object in both TestController.cs and TestAuthorizeAttribute.cs.
Is this the correct approach to create instance of TestService.
Is it possible that the multiple threads seem to refer to Web API handling multiple simultaneous requests that are somehow handled by the same DataContext
Please help me to know are there any issues with the below mentioned code.
[RoutePrefix("api/test")]
public class TestController : ApiController
{
public TestController(ITestService testService)
{
_testService = testService;
}
/// <summary>
/// Get details of individual test
/// </summary>
/// <param name="Id"> Id</param>
/// <param name="selectedSection">Selected Section</param>
/// <returns>Details of the Test</returns>
[Route("{Id:int}/{selectedSection?}", Name = "TestDetails")]
[HttpGet]
[TestAuthorize]
public HttpResponseMessage Get(int Id, string selectedSection = "")
{
var testDetails = _testService.GetTestResults(Id);
if (scan != null)
{
var progress = _testService.GetProgress(scan, user);
return Request.CreateResponse(HttpStatusCode.OK, scanDetails);
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound, new { error = GlobalConstants.ERROR_REVIEW_NOTFOUND });
}
}
}
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class TestAuthorizeAttribute : ActionFilterAttribute
{
ITestService testService;
public ScanAuthorizeAttribute()
{
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
_testService = actionContext.Request.GetDependencyScope().GetService(typeof(ITestService)) as ITestService;
var Id = Convert.ToInt32(actionContext.ActionArguments["Id"]);
var testDetails = _testService.GetTestResults(Id);
}
What you're doing looks pretty much spot on. This is pretty much exactly what you want to do.
A few things to note:
Assuming the ITestService is TransientScoped (the default) your Filter and your Controller will actually use the same instance of the ITestService.
If your DataContext is TransientScoped it will be unique per request (since the NestedContainer is stored on the request via the DependencyScope) so you should not see a race condition you were worried about.
There are a few caveats to this that I know of. One of which is ModelBinders and ModelBinderProviders are instantiated with this method from System.Web.Http.ModelBinding.ModelBinderAttribute:
private static object GetOrInstantiate(HttpConfiguration configuration, Type type)
{
return configuration.DependencyResolver.GetService(type) ?? Activator.CreateInstance(type);
}
This method does NOT use a DependencyScope so any DBContext used here will be completely unique (TransientScoped object from non-Nested container.)
I have seen really weird race conditions when dealing with IEnumerable dependencies in webapi. The default IEnumerables used by StructureMap do not appear to be thread safe.
Be careful with data access in Filters, Delegating Handlers, Model Binders, etc. Introducing a N+1 or otherwise costly query in these layers can really hurt since they get called for every request they handle.
Any other questions about this stuff please ask, I've done a ton of stuff in this area lately so I have a pretty decent understanding of all of it.

Application Insights - How to add custom metric to your request information?

This question is very similar to what I want to know. I've got a web api service on an azure cloud service with Application Insights configured. On the request information portal, that is generated automatically, I want to add a custom http header that's a part of the request into the information that is being logged with each request. The question is how do I do this?
I've tried using a telemetry initializer like below, but this fails (as in I don't see the information on the portal). I also added this in the global.asax
TelemetryConfiguration.Active.TelemetryInitializers.Add(propertyTelemetryInitializer);
public class PropertyTelemetryInitializer : ITelemetryInitializer
{
private readonly HttpContext httpContext;
public PropertyTelemetryInitializer(HttpContext httpContext)
{
this.httpContext = httpContext;
}
public void Initialize(ITelemetry telemetry)
{
this.AddTelemetryContextPropertFromContextHeader(telemetry, "xyz");
this.AddTelemetryContextPropertFromContextHeader(telemetry, "abc");
this.AddTelemetryContextPropertFromContextHeader(telemetry, "123");
}
private void AddTelemetryContextPropertFromContextHeader(ITelemetry telemetry, string headerKey)
{
var requestTelemetry = telemetry as RequestTelemetry;
telemetry.Context.Properties[headerKey] = this.httpContext.Request.Headers[headerKey] ?? string.Empty;
telemetry.Context.Properties[headerKey] = this.httpContext.Request.Headers[headerKey] ?? string.Empty;
}
}
Also is there a way to do this from the controller method itself? Something similar to the below (note: the below does not work)?
[Route("api/Something")]
[HttpGet]
[ResponseType(typeof(Something))]
public async Task<Something> GetSomething()
{
var requestTelemetry = new RequestTelemetry();
this.AddCustomHeadersToRequestTelemetry(requestTelemetry);
var result = await this.Service.GetSomethingAsync();
requestTelemetry.Properties["result"] = result.ToString();
return TypeMapper.Map<Model.Something, Something>(result);
}
/// <summary>
/// Adds the custom headers to request telemetry.
/// </summary>
/// <param name="controller">The controller.</param>
/// <param name="requestTelemetry">The request telemetry.</param>
public static void AddCustomHeadersToRequestTelemetry(this ApiController controller, RequestTelemetry requestTelemetry)
{
if (controller == null)
{
throw new ArgumentNullException("controller");
}
if (requestTelemetry == null)
{
throw new ArgumentNullException("requestTelemetry");
}
requestTelemetry.Context.Properties["abc"] = controller.Request.GetABCFromHeader();
requestTelemetry.Context.Properties["xyz"] = controller.Request.GetXYZFromHeader();
requestTelemetry.Context.Properties["123"] = controller.Request.Get123FromHeader();
}
Using TelemetryInitializers is the right solution. Some comments:
var requestTelemetry = telemetry as RequestTelemetry;: you do not use requestTelemetry after that. I guess you wanted to check for null.
Adding telemetry initializer in the Active configuration should be fine. You can also consider moving it to the applicationinsights.config
Custom properties do not show up in the portal immediately. Have you tried to reopen IE after some time and check your request again?
Can you debug? Do you see that you get in your tememetry initializer? Do you see any AI specific traces in search?
Regarding your second question. Right now telemetry initializers are the only (official) way to get to the autogenerated RequestTelemetry (which is actually in the HttpContext). There are plans to make most of the classes in web public and eventually open source it. But there is no ETA yet. If you create and track request yourself you can add custom properties as you mentioned.
UPDATE: Starting from 2.0.0-beta3 autogenerated request telemetry is accessible though HttpContext extension method: System.Web.HttpContextExtension.GetRequestTelemetry

Categories

Resources