Use NETCore ScopedService outside Controller - c#

First, let me explain breafly what I have, what I want to achieve, how I did it so far, and why I'd like to improve my current implementation.
WHAT I HAVE
Basically, I have a .NET Core project that runs an API Service with some APIs.
Also, I have a class called MyFundamentalClass, which is used throughout the whole application: in fact, MyFundamentalClass implements the Singleton Pattern, having something like this:
public class MyFundamentalClass {
private static _myFundamentalClass = null;
public static MyFundamentalClass GetInstance() {
if (_myFundamentalClass == null)
_myFundamentalClass = new MyFundametalClass();
return _myFundamentalClass;
}
}
Reason why I want this Singleton Pattern is that this class is used in many occasion in the whole project; the alternative would be to instantiate the class in the ControllerAction, and then pass it basically EVERYWHERE: it's not sustainable.
THE PROBLEM
As you can imagine, here was the first problem: each request MUST HAVE its own instance of MyFundamentalClass. As you can imagine, static keyword does not work very well with that.
Why I'm telling this: if I want an instance of MyFundamentalClass in each ControllerAction, I should write something like this:
public async Task<ActionResult> GetUserData() {
MyFundamentalClass = new MyFundamentalClass();
return await MyFundametalClass.GetUserData();
}
So far so good, but as I said, I need the Singleton Pattern, so I should change the code into:
public async Task<ActionResult> GetUserData() {
MyFundamentalClass = MyFundamentalClass.GetInstance();
return await MyFundametalClass.GetUserData();
}
What's the problem? Two different API calls will overwrite the private field MyFundamentalClass._myFundamentalClass, mixing the context of the two API. HUGE PROBLEM!
MY CURRENT SOLUTION
What I found , the only way, was the use of AsyncLocal<MyFundamentalClass>. I've something like this:
public class RequestContext {
publicv static AsyncLocal<MyFundamentalClass> Instance = new AsyncLocal<MyFundamentalClass>(null);
}
// Then, in each ControllerAction
public async Task<ActionResult> GetUserData() {
var method = async () => {
RequestContext.Instance.Value = new MyFundamentalClass();
// Whatever I need to do
}
}
// Then, in the MyFundamentalClass
public MyFundamentalClass {
public MyFundamentalClass GetInstance() {
return RequestContext.Instance.Value;
}
}
With this solution, since the AsyncLocal context lives only thourghout the async context, it perfectly fits my need.
Though, why am I searching for something else? Because I feel like I am missusing the Dependency Injection and the whole ServiceProvider stuffs.
WHAT I AM LOOKING FOR
So.. I also come upon the services.AddScoped<MyFundamentalClass>() code (Link to Microsfot DOC).
By what it tells, it should perfectly fit my need: what is created by that AddScoped lives only for the API -> one API one instance.
But, problem is: how could I exploit the instance created by AddScoped with the Singleton Pattern?
I know that, with DependencyInject, in my Controller I can add the object in the constructor:
public class MyController : ControllerBase {
private MyFundamentalClass _myFundamentalClass;
public MyController(MyFundamentalClass myFundamentalClass) {
_myFundamentalClass = myFundamentalClass;
}
public async Task<ActionResult> GetUserData() {
return await _myFundamentalClass.GetUserData();
}
}
That feels much more correct, from a code point of view, but.. I don't have the SingletonPattern anymore, unless I still use the AsyncContext.
What I thought it was possible was to use:
public static IServiceProvider ServiceProvider;
public static WorkbenchViewModel GetInstance() {
ServiceProvider.GetService(typeof(WorkbenchViewModel));
}
But I have the same problem: each request has its own IServiceProvider, thus different API would override the value.

Related

In a C# method, how do I determine (externally) which methods use a field/property

I want to be able to determine the impact of making a change to a Service. In other words, given the following C# example class, I need to be able to determine if I change MyServiceWithConfig that I:
will impact the route /feature/my-route-with-config
will not impact the route /feature/my-route
[RoutePrefix("feature")]
public sealed class MyWebApiController : ApiController
{
public MyWebApiController()
: this(
new MyService(),
new MyServiceWithConfig(StaticClass.Configuration))
{
// noop
}
private MyWebApiController(
MyService myService,
MyServiceWithConfig myServiceWithConfig)
{
this.MyServiceInstance = myService;
this.MyServiceWithConfigInstance = myServiceWithConfig;
}
private MyService MyServiceInstance { get; }
private MyServiceWithConfig MyServiceWithConfigInstance { get; }
[HttpGet,
Route("my-route")]
public async Task<object> Get()
{
return await this.MyServiceInstance.DoWork();
}
[HttpGet,
Route("my-route-with-config")]
public async Task<object> GetWithConfig()
{
return await this.MyServiceWithConfigInstance.DoWork();
}
}
What I have done so far (using reflection in PowerShell 5)
Find all Types that have a base class of ApiController
Of those types I know
If they have a private field or property of type MyServiceWithConfig
Which methods are routes and what those actual routes are
What I am not able to determine how to do:
- Determine that only one of these routes actually references this.MyServiceWithConfigInstance
From what I've read in other posts - it does not look like I can determine usage of a field using reflection. Is this something that I can use another tool to do? It has to be something I can automate because I need it to scan thousands of classes and it isn't practical to process one class at a time.
Is this something that I can leverage Roslyn for?
Thanks!
Note: I can post the PowerShell, but that doesn't seem to be helpful as it is over 100 lines so far.

How to correctly Register abstract, generic classes in startup.cs for Dependency Injection?

I am trying to build an MVC service which calls 2 different APIs, an Amazon one and an Apple one. The code looks like this:
public abstract class ApiHttpCaller<T>
{
protected static HttpClient _client;
protected ApiHttpCaller()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public abstract Task<T> RetrieveApiResultAsync(string searchValue);
}
This ApiHttpCaller is implemented by my 2 specifics AmazonApiCaller and AppleApiCaller, let's take only one of them into account:
public class AmazonApiCaller : ApiHttpCaller<AmazonResponseModel>
{
protected static IOptions<ApiUrls> _apiUrls;
public AmazonApiCaller(IOptions<ApiUrls> apiUrls)
{
_apiUrls = apiUrls;
}
public override async Task<AmazonResponseModel> RetrieveApiResultAsync(string searchValue)
{
..logic to call the api..
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<AmazonResponseModel>(responseBody);
}
}
as you can see, correct me if the architecture is wrong, there is an AmazonResponseModel used as generics here. As you can imagine AmazonApi and AppleApi return 2 different models. That's why my abstract parent class ApiHttpCaller uses a generics T that into the specifc AmazonApiCaller becomes an AmazonResponseModel. Such APIs are called from my controller.
[Route("api/[controller]")]
[ApiController]
public class ItemsController<T> : ControllerBase
{
private readonly IEnumerable<ApiHttpCaller<T>> _apiCallers;
[HttpPost]
public async Task<ActionResult> Post([FromBody] string value)
{
var amazonCaller = _apiCallers.First(x => x.GetType() == typeof(AmazonApiCaller));
var itemResult = await amazonCaller.RetrieveApiResultAsync(value);
..more logic to map the itemResult to a viewModel..
}
}
So, first question is: do you think it's correct to use the genercis T in the controller that then becomes a specifc type inside each api caller?
Second and more important: I don't know how to register in Startup.cs the ApiHttpCallers in such a way that they get injected properly in my controller. First guess is:
services.AddSingleton<ApiCaller<T>, AmazonApiCaller<AmazonResponseModel>>();
services.AddSingleton<ApiCaller<T>, AppleApiCaller<AppleResponseModel>>();
point is Startup.cs doesn't know anything of T .
services to be registred:
services.AddSingleton<ApiCaller<AmazonResponseModel>, AmazonApiCaller>();
services.AddSingleton<ApiCaller<AppleResponseModel>, AppleApiCaller>();
services.AddTransient(typeof(ItemsController<>));
Change the controller as follows:
public class ItemsController<T> : ControllerBase
{
private readonly ApiHttpCaller<T> _apiCaller;
public ItemsController(ApiHttpCaller<T> apicaller){
_apiCaller = apicaller;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] string value)
{
// do something with the requested API Caller
}
}
This should now inject the correct ApiCaller into your service.
Of course you need to specify the type when injecting an ItemsController:
// Constructor
public AnyClass(ItemsController<AmazonResponseModel> controller){
// _apiCaller of controller will be AmazonApiCaller
}
Or maybe use another IoC Container like ninject.
You could benefit from Features like Contextual and named Bindings, which is documented on their page.
You DI registration is incorrect here. It should be like this:
services.AddSingleton<ApiCaller<AmazonResponseModel>, AmazonApiCaller>();
services.AddSingleton<ApiCaller<AppleResponseModel>, AppleApiCaller>();
you need to specify which generic would correspond to which implementation.

Is it viable/thread-safe to use a static HttpClient in a using block?

Say I have a controller with an Index() method, and this controller utilizes multiple "Manager classes" that manage certain assets that need to be retrieved with an HttpClient from an API.
I've read that sharing an HttpClient with multiple calls is better than to reinstantiate it with every call to save ports.
I do however want to dispose of the HttpClient before the controller returns the view, because the view contains an entire Knockout/Typescript based front end project that handles the rest of the data (so it's basically only settings and meta data stuff).
Do I need to pass the HttpClient variable to each and every "Manager class", or does it suffice to do something like the following, and use a static HttpClient inside the classes?
public ActionResult Index()
{
using (Globals.Client = new System.Net.Http.HttpClient())
{
// do stuff like SettingManager.GetSetting("settingKey") which uses
// the Globals.Client variable
}
return View();
}
Or should I not even want to dispose the HttpClient in the first place?
One solution is to make a separate dependency responsible for managing your HttpClient. This has the side benefit of keeping your controllers from depending directly on HttpClient. Any class that depends on HttpClient becomes harder to test. It's also a maintenance issue because if you want to change the behavior you have to change it everywhere. Imagine if you decide one day that whatever you're getting from that HttpClient can be cached? You'd have to change it in lots of classes.
You can define an abstraction and implementation like this:
public interface IDoesSomething
{
string GetSetting(string key);
}
public class HttpClientDoesSomething : IDoesSomething, IDisposable
{
private readonly HttpClient _client;
private readonly string _apiUrl;
public HttpClientDoesSomething(string apiUrl)
{
_client = new HttpClient();
_apiUrl = apiUrl;
}
public string GetSetting(string key)
{
// use the client to retrieve the setting
}
public void Dispose()
{
_client?.Dispose();
}
}
Now the problem is moved out of your controller because you inject the interface:
public class MyController : Controller
{
private readonly IDoesSomething _doesSomething;
public MyController(IDoesSomething doesSomething)
{
_doesSomething = doesSomething;
}
public ActionResult Index()
{
var setting = _doesSomething.GetSetting("whatever");
// whatever else this does.
return View();
}
}
Now in your startup configuration you can register HttpClientDoesSomething as a singleton:
services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
Your implementation is disposable, so if you do need to create and dispose it you will also dispose the HttpClient. But it won't be an issue because your application will keep reusing the same one.

Inject object as lazy loading

I have MVC code like below. I use nInject for IoC.
I wonder when I push request to do for example SendMail there is create controller object, nInject create subobjects for both readonly objects: _mailSrv and _dbSrv, but I need in this request only one variable.
Is it possible to inject variable as lazy loading. When code will need object, then it will be created?
public class HomeController:Controller
{
private readonly IMailService _mailSrv;
private readonly IDatabaseService _dbSrv;
public HomeController:Controller(IMailService mailSrv, IDatabaseService dbSrv)
{
_mailSrv = mailSrv;
_dbSrv = dbSrv;
}
public ActionResult SendMail(string mailAddress)
{
_mailSrv.Do(mailAddress);
}
public ActionResult SaveToDatabase(int id, string data)
{
_dbSrv.Do(id, data);
}
}
Just tried it out.
Add Ninject.Extensions.Factory to your project and change the member variables to Lazy.
public class HomeController : Controller
{
private readonly Lazy<IMailService> _mailSrv;
private readonly Lazy<IDatabaseService> _dbSrv;
public HomeController(Lazy<IMailService> mailSrv, Lazy<IDatabaseService> dbSrv)
{
_mailSrv = mailSrv;
_dbSrv = dbSrv;
}
public ActionResult SendMail(string mailAddress)
{
_mailSrv.Value.Do(mailAddress);
}
public ActionResult SaveToDatabase(int id, string data)
{
_dbSrv.Value.Do(id, data);
}
}
Instances will now be created lazily.
Hmm, Not sure about ninject in particular, but normally no, you would get instances of the objects when the controller is instantiated.
Alternatives would be:
Make two controllers (I suggest this one)
Inject factories rather than objects
Personally this is not for me, I think the IoC container should be your factory.
public ActionResult SendMail(string mailAddress)
{
_mailSrvFactory.Create().Do(mailAddress);
}
Directly bind the object in the method rather than injecting
This is usually considered 'bad' because you have to pass the IoC container around
public ActionResult SendMail(string mailAddress)
{
kernel.Get<IMailServer>().Do(mailAddress);
}
I guess looking at it at a deeper level, It might be possible to create a custom scope which in effect wrapped the class in a factory in the same way that IoC containers can provide classes as singletons. I'd have to think about that one though

How to mock owin's authorizationManager to test my api controller

I'm using ThinkTecture's resource based authorization in my WebApi.
I'm trying to test one of my controller that I needed to check the access inside the function. But now, I can't test the function anymore since, I can't mock an extension method and since it's a nuget method, I can't modify the class to inject another value.
My controller look like this:
public class AlbumController : ApiController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await Request.CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And the ResourceAuthorizationManager is setted into the startup like this:
app.UseResourceAuthorization(new ChinookAuthorization());
Source code of the ThinkTecture project is here.
Thank you for your help
The ResourceAuthorizationAttribute uses Reqest.CheckAccess so I don't think it is a good solution to abstract away the implementation and then injecting it into the controller since in theory, the ResourceAuthorizationAttribute and the created service could use different implementations of the CheckAccess method.
I took a simpler approach by creating a BaseController
public class BaseController : ApiController
{
public virtual Task<bool> CheckAccessAsync(string action, params string[] resources)
{
return Request.CheckAccessAsync(action, resources);
}
}
and making CheckAccessAsync virtual so I can mock it (by for example Moq).
then from my controller
public class AlbumController : BaseController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
Unit testing the controller then is as easy as:
[TestClass]
public class TestClass
{
Mock<AlbumController> mockedTarget
AlbumController target
[TestInitialize]
public void Init()
{
mockedTarget = new Mock<AlbumController>();
target = mockedTarget.Object;
}
[Test]
public void Test()
{
mockedTarget.Setup(x => x.CheckAccessAsync(It.IsAny<string>(),
It.IsAny<string[]>()))
.Returns(Task.FromResult(true));
var result = target.Get(1);
// Assert
}
}
You could always wrap this static call into some abstraction of yours:
public interface IAuthorizationService
{
Task<bool> CheckAccessAsync(string view, string album, string id);
}
and then have some implementation that will delegate the call to the static extension method. But now since you will be working with the IAuthorizationService you can freely mock the CheckAccessAsync method in your unit tests.
As far as testing the implementation of this abstraction is concerned, you probably don't need it as it only acts as a bridge to the ThinkTecture's classes which should already be pretty well tested.
I finally solved my problem.
The real problem was that the CheckAccess method was an extension.
(for my answer, every class will refer to the sample that can be find here)
To stop using the extension method, I added these methods into my chinookAuthorization
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, string action, params string[] resources)
{
var ctx = new ResourceAuthorizationContext(user ?? Principal.Anonymous, action, resources);
return CheckAccessAsync(ctx);
}
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
user ?? Principal.Anonymous,
actions,
resources);
return CheckAccessAsync(authorizationContext);
}
Then I changed my controller to have an instance of the chinookAuthorization
public class AlbumController : ApiController
{
protected readonly chinookAuthorization chinookAuth;
public BaseApiController(chinookAuthorization chinookAuth)
{
if (chinookAuth == null)
throw new ArgumentNullException("chinookAuth");
this.chinookAuth = chinookAuth;
}
public async Task<IHttpActionResult> Get(int id)
{
if (!(await chinookAuth.CheckAccessAsync((ClaimsPrincipal)RequestContext.Principal, ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And I'm still declaring my ChinookAuthorization into my owin startup, to keep using the same pattern for my attribute check access call.
So now, I just have to mock the chinookAuthorization, mock the response of the call to return true, and that's it!

Categories