Dependency injection class that is resolved by async provider - c#

I have an api client that in its constructor accepts access token
public class ApiClient : IApiClient
{
public ApiClient(string accessToken)
}
To get that api token I need to create instance of another class and call method GetTokenAsync()
public class TokenClient
{
public Task<string> GetTokenAsync();
}
now I want to inject IApiClient to my TestService
public class TestService
{
public TestService(IApiClient client)
}
However it is not possible register instance in this way
services.AddScoped<IApiClient,ApiClient>(async x=>
{
tokenClient = x.GetService<TokenClient>();
var token = await tokenClient.GetTokenAsync();
return new ApiClient(token);
}
to overcome that I have created ApiClientProvider class
public class ApiClientProvider()
{
public async Task<IApiClient> GetClientAsync()
{
tokenClient = x.GetService<TokenClient>();
var token = await tokenClient.GetTokenAsync();
return new ApiClient(token);
}
}
and now I inject it to TestService
but in every method I have to use
IApiClient apiClient= await _apiClientProvider.GetClientAsync();
I don not like this code, I prefer when dependencies are injected and not resolved in every function, however I do not see any way around. Can you advise if this can be moved to registration or maybe it shouldn't go there.

You should change your design slightly.
Rather than inject a string into your ApiClient, inject an ITokenClient:
public interface ITokenClient
{
Task<string> GetTokenAsync();
}
public class TokenClient : ITokenClient
{
public Task<string> GetTokenAsync() { }
}
public class ApiClient : IApiClient
{
readonly Task<string> tokenTask;
public ApiClient(ITokenClient tokenClient)
{
// Initiate the token request on construction
tokenTask = tokenClient.GetTokenAsync();
}
public async Task SomeMethod()
{
string token = await tokenTask;
// Use your token..
}
}
Any methods that rely on the token must be async, but as your calling an API they're likely to be async anyway.
Using this method, you simply need to register your clients with the container and no factories / provider types are required.

Related

Dependency injection for Stripe.Net classes

I created a service that allowed me to utilize the Stripe.Net classes (I called it a handler) but it wasn't really testable because in the methods I would instantiate the class. For example:
public Customer CreateCustomer(string email)
{
var service = new CustomerService();
return service.Create(new CustomerCreateOptions
{
Email = email
});
}
which it's great when trying to create a test. So I figured I could just use the classes in Stripe.net instead of creating a handler. So I have tried doing this:
private static void AddTransients(IServiceCollection services)
{
services.AddTransient<Service<IStripeEntity>>();
services.AddTransient<ICreatable<IStripeEntity, BaseOptions>>();, BaseOptions));
}
Which I through would allow me to pass the classes around my controllers like any other injected class. But when I launch my application I get this error:
Cannot instantiate implementation type 'Stripe.IStripeEntity' for service type 'Stripe.IStripeEntity'.
So I tried registering the classes as generic like this:
private static void AddTransients(IServiceCollection services)
{
services.AddTransient(typeof(Service<>));
services.AddTransient(typeof(ICreatable<,>));
}
But when I run that, I get the same error. Does anyone know what I can do to get this to work?
I solved it by creating wrapper classes. Not the most ideal, but it works:
public class StripeCustomerService : IStripeCustomerService
{
private readonly CustomerService _customerService;
public StripeCustomerService() => _customerService = new CustomerService();
public Customer Create(string email)
{
return _customerService.Create(new CustomerCreateOptions
{
Email = email
});
}
public async Task<Customer> GetAsync(string id, CancellationToken cancellationToken) =>
await _customerService.GetAsync(id, cancellationToken: cancellationToken);
}

How to Call Api Controller from another Api Controller In Same Project

i have an object "ApplicantDetail" with list of objects in ApplicantController and i want to send Post Request to Personaldetails Api On To save into database and get response back
Api On ApplicantController
// POST: api/ApplicantDetail
[HttpPost]
[Route("~/api/ApplicantDetail")]
public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
Personaldetail personaldetail = new Personaldetail
{
Name = ApplicantDetail.Name,
Cnic = ApplicantDetail.Cnic,
Phone = ApplicantDetail.Phone
};
List<Address> addresses = new List<Address>();
List<Employee> employees = new List<Employee>();
List<Bank> banks = new List<Bank>();
addresses.AddRange(ApplicantDetail.Address);
employees.AddRange(ApplicantDetail.Employee);
banks.AddRange(ApplicantDetail.Bank);
var response = *//How to Call Personaldetail Post APi of PersonaldetailController Controller From Here and Get
response back//*
return null;
}
Api On Personaldetail Controller
// POST: api/Personaldetails
[HttpPost]
public async Task<IActionResult> PostPersonaldetail([FromBody] Personaldetail personaldetail)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Personaldetail.Add(personaldetail);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPersonaldetail", new { id = personaldetail.ApplicantId }, personaldetail);
}
You should create a service class to represent the personal details API. In this class, you would inject HttpClient, and then set up IHttpClientFactory with a strongly-typed client:
public class PersonalDetailsService
{
private readonly HttpClient _client;
public PersonalDetailsService(HttpClient client)
{
_client = client;
}
// methods here to interact with API via `_client`
}
Then, in ConfigureServices:
services.AddHttpClient<PersonalDetailsService>(c =>
{
c.BaseAddress = new Uri(Configuration["PersonalDetailsAPIUrl"]);
// configure anything else you need to on the `HttpClient`
});
Finally, in your controller, you inject PersonalDetailsService:
[ApiController]
[Route("api/ApplicantDetail")]
public class ApplicantController
{
private readonly PersonalDetailsService _personalDetailsService;
public ApplicantController(PersonalDetailsService personalDetailsService)
{
_personalDetailsService = personalDetailsService;
}
[HttpPost("")]
public async Task<IActionResult> Post([FromBody] Personaldetail ApplicantDetail)
{
...
var response = await _personalDetailsService.SomeMethodAsync(someParam);
return null;
}
}
If this is within the same process it's unwise to call a controller from another controller.
It's more common to create a class, usually called a service, in which you put your logic to apply the task at hand.
This will have some benifits:
isolation of the logic
not having to worry about it when changing one endpoint
not worry about changing url's or authentication
not having an unnecessary network action
E.g.:
The service:
public class YourService
{
public void YourMethod(parameters)
{
//do your stuff
}
}
The usage:
public class Controller1 : Controller
{
public void YourAction1()
{
//controller specific stuff like model validation
//shared logic
var service = new YourService();
service.YourMethod();
}
}
public class Controller2 : Controller
{
public void YourAction2()
{
//controller specific stuff like model validation
//shared logic
var service = new YourService();
service.YourMethod();
}
}
Alternativly you can use a DI framework to resolve your service.
I could see that your target method (PostPersonaldetail) on controller2 (Personaldetail Controller) is an asynchronous method. While we are calling it we need to use the await keyword with async applied to the method (POST: api/ApplicantDetail) as we can't use await keyword in a method with out making the method as async. Alternatively we can apply wait on the method call too.
Controllers are nothing but classes, we can create an instance and call methods defined in it, however it's not a good practice. Using dependency injection we can get references to other services/controllers with in the application and use it to call methods defined on them.
Approach 1:
public async IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
var response = await new controller2().PostPersonaldetail(persondetails);
}
Approach 2:
public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
var response = new controller2().PostPersonaldetail(persondetails).Wait();
}
The following links can be helpful.
Resource 1
Resource 2
I don't think you want to new up an instance because it could be missing dependencies. You can do something like:
var controller = DependencyResolver.Current.GetService<PostPersonaldetail>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
Then make the call.
You could also use dependency injection and just have a reference in the controller:
class ApplicantController : ControllerBase
{
public ApplicantController(ControllerB controllerb)
{
_contollerB = controllerB;
}
you may have to add a call to add the transient service:
services.AddTransient<ControllerB>();

Access or Inject HubCallerContext outside of Hub in SignalR Core

I have an asp-net-core project with signalR implementation. I need to extract user information from Context.User when a method is invoked in my hub. The problem is, when the hub being constructed Context.User does not contain and User information. But in the method scope, Context.User is just what I expected.
public class Basehub : Hub
{
public Basehub(IUserProfileProvide userProfileProvider)
{
this.CurrentUser = userProfileProvider.InitUserProfile(Context); // Context.User is empty when breakpoint hits this line
}
public IUserProfile CurrentUser {get;}
}
public class NotificationHub: BaseHub
{
private IUserProfileProvide userProfileProvider;
public NotificationHub(IUserProfileProvide userProfileProvider)
{
}
public async Task InvokeMe(string message)
{
var contextUser = Context.User;
var profile = CurrentUser;//this is empty because Context is empty in the construction phase
await Clients.All.SendAsync("invoked",message); // Context.User is OK when breakpoint hits this line
}
}
My main goal is to inject HubCallerCOntext to IUserProfileProvide and BaseHub will be as clean as possible.
*My question: How can I inject HubCallerContext outside of the hub?
The context is not available as yet when the constructor is invoked.
It will be populated by the time the intended function is invoked.
public class Basehub : Hub {
protected IUserProfileProvide userProfileProvider;
public Basehub(IUserProfileProvide userProfileProvider) {
this.userProfileProvider = userProfileProvider;
}
}
Deferring access to it later in the flow, like in a method when the framework has had time to properly populate the context.
public class NotificationHub: BaseHub {
public NotificationHub(IUserProfileProvide userProfileProvider)
: base(userProfileProvider) { }
public async Task InvokeMe(string message) {
IUserProfile profile = userProfileProvider.InitUserProfile(Context); //context populated
//...
await Clients.All.SendAsync("invoked",message);
}
}

How to configure services based on request in ASP.NET Core

In ASP.NET Core we can register all dependencies during start up, which executed when application starts. Then registered dependencies will be injected in controller constructor.
public class ReportController
{
private IReportFactory _reportFactory;
public ReportController(IReportFactory reportFactory)
{
_reportFactory = reportFactory;
}
public IActionResult Get()
{
vart report = _reportFactory.Create();
return Ok(report);
}
}
Now I want to inject different implementations of IReportFactory based on data in current request (User authorization level or some value in the querystring passed with an request).
Question: is there any built-in abstraction(middleware) in ASP.NET Core where we can register another implementation of interface?
What is the possible approach for this if there no built-in features?
Update
IReportFactory interface was used as a simple example. Actually I have bunch of low level interfaces injected in different places. And now I want that different implementation of those low level interfaces will be injected based on request data.
public class OrderController
{
private IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService;
}
public IActionResult Create()
{
var order = _orderService.Create();
return Ok(order);
}
}
public class OrderService
{
private OrderBuilder _orderBuilder;
private IShippingService _shippingService; // This now have many different implementations
public OrderService(
OrderBuilder _orderBuilder,
IShippingService _shippingService)
{
_orderService = orderService;
_shippingService = shippingService;
}
public Order Create()
{
var order = _orderBuilder.Build();
var order.ShippingInfo = _shippingService.Ship();
return order;
}
}
Because we know which implementation we need to use on entry point of our application (I think controller action can be considered as entry point of application), we want inject correct implementation already there - no changes required in already existed design.
No, you can't. The IServiceCollection is populated during application startup and built before Configure method is called. After that (container being built), the registrations can't be changed anymore.
You can however implement an abstract factory, be it as factory method or as an interface/class.
// Its required to register the IHttpContextAccessor first
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IReportService>(provider => {
var httpContext = provider.GetRequired<IHttpContextAccessor>().HttpContext;
if(httpContext.User.IsAuthorized)
{
return new AuthorizedUserReportService(...);
// or resolve it provider.GetService<AuthorizedUserReportService>()
}
return new AnonymousUserReportService(...);
// or resolve it provider.GetService<AnonymousUserReportService>()
});
Alternatively use an abstract factory class
I'm afraid you can not directly acheive the goal via simple dependency injection , as the the dependency injection configured at Startup stage , in other words , all services and implementions has been configured before a request comming .
However , you can inject a Create Service delegate so that can we create the required service implemention instance in runtime .
For instance , if we have a IReportFactory Interface and two implementions as blew :
public interface IReportFactory
{
object Create();
}
public class ReportFactory1 : IReportFactory
{
public object Create()
{
return new { F = 1, };
}
}
public class ReportFactory2 : IReportFactory {
public object Create()
{
return new { F = 2, };
}
}
As we want to get the required implemention in future , we need to register the Implementions first .
services.AddScoped<ReportFactory1>();
services.AddScoped<ReportFactory2>();
and here's where the magic happens :
We don't register a IReportFactory
We just add a Func<HttpContext,IReportFactory> instead , which is a CreateReportFactoryDelegate
public delegate IReportFactory CreateReportFactoryDelegate(Microsoft.AspNetCore.Http.HttpContext context);
We need add the CreateReportFactoryDelegate to servies too.
services.AddScoped<CreateReportFactoryDelegate>(sp => {
// return the required implemention service by the context;
return context => {
// now we have the http context ,
// we can decide which factory implemention should be returned;
// ...
if (context.Request.Path.ToString().Contains("factory1")) {
return sp.GetRequiredService<ReportFactory1>();
}
return sp.GetRequiredService<ReportFactory2>();
};
});
Now , we can inject a CreateReportFactoryDelegate into controller :
public class HomeController : Controller
{
private CreateReportFactoryDelegate _createReportFactoryDelegate;
public HomeController(CreateReportFactoryDelegate createDelegate) {
this._createReportFactoryDelegate = createDelegate;
// ...
}
public async Task<IActionResult> CacheGetOrCreateAsync() {
IReportFactory reportFactory = this._createReportFactoryDelegate(this.HttpContext);
var x=reportFactory.Create();
// ...
return View("Cache", cacheEntry);
}
}
It is possible by using the HttpContextAccessor in Startup.cs
services.AddHttpContextAccessor();
services.AddScoped<IYourService>(provider =>
{
var contextAccessor = provider.GetService<IHttpContextAccessor>();
var httpContext = contextAccessor.HttpContext;
var contextVariable = httpContext. ...
// Return implementation of IYourService that corresponds to your contextVariable
});
Expanding on #JohanP comment about using IEnumerable
//Program.cs
//get the builder
var builder = WebApplication.CreateBuilder(args);
//register each type
builder.Services.AddScoped<IReport,Report1>();
builder.Services.AddScoped<IReport,Report2>();
builder.Services.AddScoped<IReport,Report3>();
//register the factory class
builder.Services.AddScoped<IReportFactory,ReportFactory>();
//IReport Interface
public interface IReport
{
string ReportType{ get; set; }
}
//ReportFactory.cs
public class ReportFactory : IReportFactory
{
private IEnumerable<IReport> _handlers;
//ctor
public ReportFactory(IEnumerable<IReport> handlers)
=> _handlers = handlers;
internal IReport? Creat(string reportType) =>
_handlers.Where(h => h.ReportType== reportType).First();
}
//Controller
public class ReportController
{
private IReportFactory _reportFactory;
public ReportController(IReportFactory reportFactory)
{
_reportFactory = reportFactory;
}
//modify to your project needs
public IActionResult Get([FromBody] string reportType)
{
if (HttpContext.User.IsAuthorized)
{
var report = _reportFactory.Create(reportType);
return Ok(report);
}
}
}

Creating stub for `private static readonly` field

Due on Improper Instantiation problem it is recommended to create private static readonly instance of HttpClient.
Due on lack of time I have injected mocked client into test method with client as their parameter.
The problem is how can I in simple way inject mock into private static readonly HttpClient field of SingleHttpClientInstanceController?
how can I in simple way inject mock into private static readonly
HttpClient field of SingleHttpClientInstanceController?
Answer: There is no simple way.
Suggestion:
Abstract the resource behind an accessor
public interface IHttpClientAccessor {
HttpClient HttpClient { get; }
}
and inject that into the dependent controller.
public class SingleHttpClientInstanceController : ApiController {
private readonly HttpClient HttpClient;
public SingleHttpClientInstanceController(IHttpClientAccessor httpClientAccessor) {
HttpClient = httpClientAccessor.HttpClient;
}
// This method uses the shared instance of HttpClient for every call to GetProductAsync.
public async Task<Product> GetProductAsync(string id) {
var hostName = HttpContext.Current.Request.Url.Host;
var result = await HttpClient.GetStringAsync(string.Format("http://{0}:8080/api/...", hostName));
return new Product { Name = result };
}
}
The same should also be done for accessing HttpContext which is what was recently introduced in Asp.Net-Core's IHttpContextAccessor
An implementation of the IHttpClientAcessor can look something like this
public class HttpClientAccessor : IHttpClientAccessor {
static readonly Lazy<HttpClient> client = new Lazy<HttpClient>(() => new HttpClient());
public HttpClient HttpClient { get { return client.Value; } }
}
So now for tests you can inject mock of the dependency.
If using a DI container remember to register the accessor as a singleton as well.

Categories

Resources