I'm trying to create a Microsoft Graph subscription inside of Startup.ConfigureServices(), but this requires an async operation.
After doing some research it turns out that .NET does not and probably will never support async operations inside of Startup. At this point I'm just willing to block the thread to create the subscription.
However, I have ran into another issue. The controllers are not set up inside of Startup.ConfigureServices even though I am creating the subscription after calling services.AddControllers(). This means that the Graph API is never receiving the 200 from my controller which it needs to register the subscription since the controller hasn't been set up yet I'm assuming.
Is what I am trying to accomplish even possible with the current structure of the .NET framework?
The only workaround I've found is to create the Subscription object without calling the Graph API, call services.AddSingleton<Subscription(provider => subscription), dependency inject the Subscription object into my controller, and then create the subscription by calling the Graph API via an API endpoint inside the controller. Then I must update all of the fields for the dependency injected subscription, since I can't overwrite the object itself with subscription = await _gsc.Subscriptions.Request().AddAsync(_sub); (_gsc being an instance of GraphServiceClient).
This is what I would like to do:
Startup.ConfigureServices()
Task<Subscription> subscriptionTask = gsc.Subscriptions.Request().AddAsync(subscription);
subscription = subscriptionTask.GetAwaiter().GetResult();
services.AddSingleton<GraphServiceClient>(provider => gsc);
services.AddSingleton<Subscription>(provider => subscription);
This is my current workaround:
Startup.ConfigureServices()
services.AddSingleton<GraphServiceClient>(provider => gsc);
services.AddSingleton<Subscription>(provider => subscription);
One of my controller endpoints
public async Task<IActionResult> CreateMSGraphSubscription()
{
try
{
Subscription newSubscription = await _gsc.Subscriptions.Request().AddAsync(_sub);
// Update our singleton instance.
_sub.Id = newSubscription.Id;
_sub.AdditionalData = newSubscription.AdditionalData;
_sub.ApplicationId = newSubscription.ApplicationId;
_sub.CreatorId = newSubscription.CreatorId;
_graphRenewalSettings.IsActive = true;
}
catch (Exception e)
{
return new BadRequestObjectResult(e);
}
return new OkObjectResult(_sub);
}
My problem is that I have to call one of my API endpoints after the application has started, rather than having the graph subscription ready when the app has started.
UPDATE
I figured out that I need to inject into Startup.Configure() IHostApplicationLifetime lifetime and then use
lifetime.ApplicationStarted.Register(async () =>
{
Subscription newSubscription = await gsc.Subscriptions.Request().AddAsync(sub);
// Update our singleton instance.
sub.Id = newSubscription.Id;
sub.AdditionalData = newSubscription.AdditionalData;
sub.ApplicationId = newSubscription.ApplicationId;
sub.CreatorId = newSubscription.CreatorId;
graphRenewalSettings.IsActive = true;
});
at the end of Startup.Configure() to register the graph subscription with the Graph API once the app has started up. (sub and gsc are my Subscription and GraphServiceClient objects, respectively, and I inject these into Startup.Configure() also)
This is not ideal since I would like to inject an already registered Subscription object instead of updating all of the fields like I do above, but I could not find a better way to do this.
Related
I am trying to find a way to inject a dependency in Startup.cs, for a aspnet5 application. I am aware of the following previous question, but hopefully I can expand on my particular requirements:
Aspnet core 5.0 inject dependency in startup.cs constructor
I initially needed a way to fire a custom method after OnSignIn with MS Identity Platform (IP) within my app. After posting the following questions, I was quickly pointed in the right direction:
How to fire a custom method after OnSignIn with MS Identity Platform
In short, during ConfigureServices in StartUp.cs, I needed to configure the IP middleware service, to add my own methods to some of its available events. Works a treat!
So I initially tried:
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = async ctex =>
{
// Blah
},
});
As I say, this works great. However, to do anything serious, I need access to my existing code and the DI magic that goes with it. This is where I a hitting this problem. I need my custom logic that requires DI, during ConfigureServices, before I have configured the services for DI.
On the same post, someone pointed out that you can wrap up the configuration, and essentially inject it back in, so you can get DI back. Great! But for some reason, I can only inject the handler, either as a Singleton, or Transient. Not Scoped. I get to some extent why it can't be scoped (if it is has a dependency of its own on a transient service), but then are why both extremes ok? Why is Singleton OK? Why is Transient? It just so happens, but my DI requirements are that I would need to inject scoped services, as that is what I need. I need some of the data to persist across instances, but I can't have a single instances shared across the whole application/user base.
So then I tried:
public class AzureAdOpendIdHandler : IConfigureNamedOptions<OpenIdConnectOptions>
{
public AzureAdOpendIdHandler(Authentication.AuthenticationManager authenticationManager)
{
AuthenticationManager = authenticationManager;
}
public Authentication.AuthenticationManager AuthenticationManager { get; }
public void Configure(string name, OpenIdConnectOptions options)
{
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
//Blah
return Task.CompletedTask;
},
};
}
}
Then called in StartUp.cs like:
services.AddScoped<Authentication.IAuthenticationManager, Authentication.AuthenticationManager>();
services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, AzureAdOpendIdHandler>();
Again, the handler works fine, and everything fires, as long as I don't try and pass "AuthenticationManager" within the constructor. I get an error saying that I can't inject a scoped service into a singleton service.
I keep getting within spitting distance of doing what I need, but there is always another block in the way.
In summary:
To override/extend MS Identity Platform, I need to make changes in ConfigureServices() in StartUp.cs
These changes require my classes to be available via DI.
The DI must allow me to pass them as scoped
So far, I can only meet the first two requirements, I can not find a way to satisfy all three.
My end requirement is to be able to add a call to my AuthenticationManager class from within the following OnTicketReceived Event:
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = async ctex =>
{
//use my AuthenticationManager class!!
},
});
I have tried the direct method, wrapping things up in a handler and injecting, but DI seems to be getting in the way now.
Any help, would be gratefully received.
I've taken over a code base from someone else. This is a web application built on Angular 8 (client) and .NET Core 3.0 (server).
Brief description of the application:
Frequent notifications are stored in a database, with an SqlTableDependency attached to it for detecting new notifications.
When new notifications occur, the server prompts all clients to request an updated list based on their custom filters. These client-to-server requests happen over HttpPost with the filter as a parameter.
The problem occurs when too many notifications arrive at once. Say, when 10 new notifications arrive, the server sends 10 update prompts to the client at the same time, causing the client to immediately send 10 HttpPost requests to the API.
The API takes the filter from the POST, uses it to query the database, and returns the filtered result to the calling client. However, when 10 of these arrive at the same time, it causes a DbContext error - more specific:
A second operation started on this context before a previous operation
completed. This is usually caused by different threads using the same
instance of DbContext.
public class AlarmController : Controller
{
private readonly IAlarmRepository alarmRepo;
private readonly ISiteRepository siteRepo;
public AlarmController(IAlarmRepository alarmRepo, ISiteRepository siteRepo)
{
this.alarmRepo = alarmRepo;
this.siteRepo = siteRepo;
}
[HttpPost("filter")]
public async Task<IActionResult> FilterAlarm([FromBody] AlarmRequest alarmRequest)
{
var snmpReciverList = await this.alarmRepo.GetFilteredSNMPReceiverHistory(alarmRequest.FromDate, alarmRequest.ToDate);
var siteList = await this.siteRepo.GetSiteListFiltered(int.Parse(alarmRequest.Filter), alarmRequest.SiteName);
return Ok(await SNMPHistoryMapping.DoMapping(siteList, snmpReciverList);
}
This HttpPost returns an Ok() with a list of the data requested, in which some mapping is done:
IEnumerable<Site> sites = siteList;
IEnumerable<SnmpreceiverHistory> histories = snmpReceiverList;
IEnumerable<SNMPHistoryResponse> data = (from s in sites
join rh in histories on s.Address equals rh.Ipaddress
where priority > 0 ? s.SitePriority == priority : true
&& !string.IsNullOrEmpty(trap) ? rh.AlarmDescription.Contains(trap) : true
select new SNMPHistoryResponse()
{
AlarmDescription = rh.AlarmDescription,
EventType = rh.EventType,
OnOffStatus = rh.OnOffStatus,
ParentSiteName = TraceFullParentDescription(s.Parent),
ReceiveTime = rh.ReceiveTime,
RepeatCount = rh.RepeatCount,
SiteName = s.Description,
SitePriority = s.SitePriority,
Status = AlarmStatus.GetStatusDescription(rh.EventType),
Value = rh.Value
});
When multiple of these [HttpPost("filter")] requests arrive at the same time, it appears as if a new thread is created for each one. They all connect on the same DbContext, and the next query starts before the previous is completed.
I can solve it by putting delays between each request from the client, but I want a more robust server-side solution to it, effectively processing these specific requests sequentially.
Note that this is EF Core and .NET Core 3.0, which does not have a SynchronizationContext.
I believe the comment posted by Panagiotis Kanavos is correct:
In this case a single DbContext is created by dependency injection, don't do that. All examples and tutorials show that the DbContexts are Scoped.
This catches me often, and actually just did. I wasn't using dependency injection, but sharing the DbContext around because I was being lazy. Best to properly set up dependency injection and do it the right way, e.g.:
IHostBuilder host = CreateHostBuilder(args);
host.ConfigureServices(services => {
services.AddSingleton(service);
// other stuff...
// Then the context:
services.AddScoped<DataContext>(x => {
DbContextOptionsBuilder<DataContext> dbBuilder =
new DbContextOptionsBuilder<DataContext>();
dbBuilder.UseNpgsql(connstr);
return new DataContext(dbBuilder.Options);
});
});
// Start the host
host.Build().Run();
The documentation for AddScoped is here, and in true microsoft form, it is impossible to read or digest. Stackoverflow does a better job at explaining it.
I’m creating an API that serves as the bridge between the app and 2 other APIs. I want to know if what is the best way to do this. I’m using HttpClient. The app has almost a thousand users.
I read this article https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/. Should I really not use the using statement? Also I am doing synchronous api calls. Does this have any effect? Is what I did efficient?
Here is my code:
[HttpGet]
[Route("api/apiname")]
public String GetNumberofP([FromUri]GetNumberofPRequest getNPRequest){
var request = JsonConvert.SerializeObject(getNPRequest);
string errorMessage = "";
try{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.gettoken());
var response = httpClient.GetAsync("api/MobileApp/GetNumberP?"
+ "strCardNumber=" + getNPRequest.strCardNumber
+ "&strDateOfBirth=" + getNPRequest.strDateOfBirth).Result;
return response;
}
catch (Exception e){
throw utils.ReturnException("GetNumberofP", e, errorMessage);
}
}
HttpClient does not need to be disposed and you should hold on to it to reuse it later.
One thing you can use (from the thread you linked):
You just provide your HttpClient factory and dispose methods and the
LimitedPool does the rest:
_httpClientPool = new LimitedPool<httpclient>(
CreateHttpClient, client => client.Dispose(), HttpClientLifetime);
using (var httpClientContainer = _httpClientPool.Get())
{ ... use httpClientContainer.Value ... }
When httpClientContainer is disposed, the HttpClient is actually returned back to the pool for other threads to use. When
lifetime is reached next dispose will eventually call the Dispose
method.
See code here
Alternative for .Net Core
Implement it as described in this document.
The IHttpClientFactory can be registered by calling the AddHttpClient extension method on the IServiceCollection, inside the Startup.ConfigureServices method.
services.AddHttpClient();
Once registered, code can accept an IHttpClientFactory anywhere services can be injected with dependency injection (DI). The IHttpClientFactory can be used to create a HttpClient instance:
public MyConstructor(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
....
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
...
}
No need to use using().
If you are using asp.net core the right way to use HttpClient is explained in this article from Microsoft:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#typed-clients
I usually use the typed client approach explained in the article.
This basically means that I delegate to the asp.net core container the injection of the http client in my class (a controller class, a service, a filter, whatever)
Doing so you can safely modify the http client in your class by adding all the request headers you need (you usually do it inside the constructor of your class).
You do not have to call dispose on the injected http client, you simply use it.
The asp.net core container will manage the http client lifetime for you and the pool of resources used by http client instances so that your app do not leak resources. All of this happens automatically.
Do not use sync calls. Make your action method async, and await on async methods of http client. Asp.net core fully support async code and make blocking requests does not make sense, doing so you will limit the scalability of your app.
I am working with a WPF based application and using Autofac to resolve the dependency of DbContext of Entityframework. I used the below code to register my data module.
public class DataModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DataContext>()
.As<IDbContext>()
.WithParameter("nameOrConnectionString", "DefaultConnectionString")
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
}
}
This works fine while using in normal scenario but while using TPL, due to simultaneous calls to repository, it creates error stating that "ExecuteReader requires an open and available Connection. The connection's current state is open."
In web application, this can be resolved using InstancePerRequest to resolve dependency per request but in WPF I need to resolve this dependency per Thread request. Is there any way out for this?
I have review InstancePerRequest summary or autofac and it states that this method is used for Web request only:
// Summary:
// Share one instance of the component within the context of a single web/HTTP/API
// request. Only available for integration that supports per-request dependencies
// (e.g., MVC, Web API, web forms, etc.).
Update:
This is a simple async method that I used to get the data:
private async void OnLoadClientDetail()
{
long clientId = SelectedClient != null ? SelectedClient.Id : 0;
var listOfCollection = await _collectionService.GetCollectedCollectionAsync(clientId);
CollectionList = new ObservableCollection<CollectedCollection>(listOfCollection);
}
Here OnLoadClientDetail is bound to selection change event of a combobox. When user change the selection frequently then this method will be called multiple times. The _collectionService is injected in the viewmodel and has InstancePerLifetimeScope define. So how can I get different scope for all this calls?
As far as I can see, you share the _collectionService instance across the different event handlers by injecting it by Constructor Injection.
It probably better to use Method Injection here, so you'll get the instance per call, as you need, resolving it before method:
builder.Register(c =>
{
var result = new MyObjectType();
var dep = c.Resolve<TheDependency>();
result.SetTheDependency(dep);
return result;
});
I'm using TPL to send emails to the end-users without delaying the api response, i'm not sure which method should be used since im dealing with the db context here. I did method 2 because i wasn't sure that the db context would be available by the time the task gets to run, so a created a new EF object, or maybe im doing it all wrong.
public class OrdersController : ApiController {
private AllegroDMContainer db = new AllegroDMContainer();
public HttpResponseMessage PostOrder(Order order) {
// Creating a new EF object and adding it to the database
Models.Order _order = new Models.Order{ Name = order.Name };
db.Orders.Add(_order);
/* Method 1 */
Task.Factory.StartNew(() => {
_order.SendEmail();
});
/* Method 2 */
Task.Factory.StartNew(() => {
Models.Order rOrder = db.Orders.Find(_order.ID);
rOrder.SendEmail();
});
return Request.CreateResponse(HttpStatusCode.Created);
}
}
Both methods are wrong, because you're starting a fire-and-forget operation on a pool thread inside the ASP.NET process.
The problem is, an ASP.NET host is not guaranteed to stay alive between handling HTTP responses. E.g., it can be automatically recycled, manually restarted or taken out of the farm. In which case, the send-mail operation would never get completed and you wouldn't get notified about it.
If you need to speed up the response delivery, consider outsourcing the send-mail operation to a separate WCF or Web API service. A related question: Fire and forget async method in asp.net mvc.