Access registered service from Program.cs in ASP.NET Core 6 - asp.net-core

In my Program.cs file I'm registering two repositories on builder.Services that will be used via dependency injection. However, I now have a case where I need to call one to configure the other. So essentially I need to do this:
builder.Services.AddFirstService();
var tempProvider = builder.Services.BuildServiceProvider();
var injected = tempProvider.GetRequiredService<IFirstService>();
var password = await injected.GetPasswordAsync();
builder.Services.AddSecondService(password);
That of course gives a warning about creating duplicate services. Is there a way to properly inject the first service so that I can then use it in another call?
The second service isn't something that can be modified to know about the first service.

The password is runtime data. This means you should design that component as such that it doesn't require runtime data during creation but instead allow to fetch the password lazily after it was created.
In case the component can't be changed—for instance because it comes from a third-party library—it's best to hide its use behind an abstraction. This also makes sure you adhere to the Dependency Inversion Principle, which states that:
high level modules should own the abstractions they depend on.
For instance, inside your application layer, you can create an abstraction such as the following:
public interface IApplicationTailoredAbstraction
{
Task DoSomething();
}
Application code that originally depended on the third-party component can now start to take a dependency on IApplicationTailoredAbstraction instead.
Inside your application's Composition Root, you can now create an Apapter that adapts from IApplicationTailoredAbstraction to the third-party component:
public class AppTailoredToThirdPartyComponentAdapter
: IApplicationTailoredAbstraction
{
public AppTailoredToThirdPartyComponentAdapter(IFirstService firstService) ...
// Optionally cache the password (depending on your needs)
private string password;
public async Task DoSomething()
{
if (password is null)
{
password = await firstService.GetPasswordAsync();
}
// Create the third-party library component
var service = new SecondService(password);
// Invoke its method(s)
await service.Run();
}
}
The registration of your components can now be done as follows:
builder.Services.AddFirstService();
builder.AddSingleton<IApplicationTailoredAbstraction,
AppTailoredToThirdPartyComponentAdapter>();

Related

IdentityServer4 Authorization context as transient service

We have a running IS4 instance for corporate customers (built on top of QuickUI), now the mgmt wants to extend it with another client and they want to have a special layout and page adjustments when the authorization process comes from this client (in essence we need to collect additional info in login form and it has to be branded slightly differently).
Now, adjusting the form and visual appearance is not a problem, but deciding when to show it is proving a challenge. We have our templating and branding in _Layout which is used by all the pages, and I somehow need to know inside _layout, if the page load is part of the authentication context and if so, which one.
The way QuickUI did it in AccountController, is using IS interaction, which generates AuthenticationRequest which contains the client:
var context = await interaction.GetAuthorizationContextAsync(returnUrl);
var clientName = context.Client?.ClientName;
This fits nicely with AccountController logic and works just fine for authentication purposes, but does little for visual. I do not have the returnUrl inside _Layout (and layout is used by other pages, for registration, etc, so I cannot really cram specific viewmodels into it) and apparently no clean way to determine the context.
a) I could start digging through HttpRequest and parsing request URLs, fishing for returnUrls, but I would first like to see if there is a more streamlined, or even existing solution.
b) Another option is to have multiple layout files, and expose Client to the ViewModel and switch layouts based on it.
Ideally however, I would like something "clean" and maintainable, a service I could #inject into _Layout.cshtml and just do #if (contextService.Client == ...)
Has someone seen or done something like it?
Until (and if) someone posts a "cleaner" solution (especially the one that does not revolve around returnUrl magic string), this is the workaround I implemented. It's dirty, I admit, it pokes on HttpContext from a service, which is not really asp.net core way, but at this point, I see no other way to obtain the information I need to construct authorization context:
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Http;
namespace MyServices
{
public interface IContextService
{
Task<AuthorizationRequest> GetContextAsync();
}
public class ContextService : IContextService
{
private readonly IIdentityServerInteractionService interaction;
private readonly IHttpContextAccessor contextAccessor;
private AuthorizationRequest context;
public ContextService(IIdentityServerInteractionService interaction, IHttpContextAccessor contextAccessor)
{
this.interaction = interaction;
this.contextAccessor = contextAccessor;
}
public async Task<AuthorizationRequest> GetContextAsync() => context ??= await InternalObtainContextAsync();
private async Task<AuthorizationRequest> InternalObtainContextAsync()
{
var query = contextAccessor.HttpContext?.Request.Query;
if (query == null || query.Count == 0 || !query.ContainsKey("returnUrl")) return new AuthorizationRequest(); // empty context
return await interaction.GetAuthorizationContextAsync(query["returnUrl"]);
}
}
}
Register in startup with services.AddTransient<IContextService, ContextService>(); and then you can inject it into any page and/or layout:
#inject IContextService contextService;
#{
// render only when invoked from authorization context
var context = await contextService.GetContextAsync();
if (context.Client != null)
{
<div>Hi, I am inside authorization context for client #context.Client.ClientId</div>
}
}

Dependency Injection Access While Configuring Service Registrations in asp.net Core (3+)

I have cases, where I want to configure services based on objects which are registered in the dependency injection container.
For example I have the following registration for WS Federation:
authenticationBuilder.AddWsFederation((options) =>{
options.MetadataAddress = "...";
options.Wtrealm = "...";
options.[...]=...
});
My goal in the above case is to use a configuration object, which is available via the DI container to configure the WsFederation-middleware.
It looks to me that IPostConfigureOptions<> is the way to go, but until now, I have not found a way to accomplish this.
How can this be done, or is it not possible?
See https://andrewlock.net/simplifying-dependency-injection-for-iconfigureoptions-with-the-configureoptions-helper/ for the I(Post)ConfigureOptions<T> way, but I find that way too cumbersome.
I generally use this pattern:
// Get my custom config section
var fooSettingsSection = configuration.GetSection("Foo");
// Parse it to my custom section's settings class
var fooSettings = fooSettingsSection.Get<FooSettings>()
?? throw new ArgumentException("Foo not configured");
// Register it for services who ask for an IOptions<FooSettings>
services.Configure<FooSettings>(fooSettings);
// Use the settings instance
services.AddSomeOtherService(options => {
ServiceFoo = fooSettings.ServiceFoo;
})
A little more explicit, but you have all your configuration and DI code in one place.
Of course this bypasses the I(Post)ConfigureOptions<T> entirely, so if there's other code that uses those interfaces to modify the FooSettings afterwards, my code won't notice it as it's reading directly from the configuration file. Given I control FooSettings and its users, that's no problem for me.
This should be the approach if you do want to use that interface:
First, register your custom config section that you want to pull the settings from:
var fooSettingsSection = configuration.GetSection("Foo");
services.Configure<FooSettings>(fooSettingsSection);
Then, create an options configurer:
public class ConfigureWSFedFromFooSettingsOptions
: IPostConfigureOptions<Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions>
{
private readonly FooSettings _fooSettings;
public ConfigureWSFedFromFooSettingsOptions(IOptions<FooSettings> fooSettings)
{
_fooSettings = fooSettings.Value;
}
public void Configure(WsFederationOptions options)
{
options.MetadataAddress = _fooSettings.WsFedMetadataAddress;
options.Wtrealm = _fooSettings.WsFedWtRealm;
}
}
And finally link the stuff together:
services.AddTransient<IPostConfigureOptions<WsFederationOptions>, ConfigureWSFedFromFooSettingsOptions>();
The configurer will get your IOptions<FooSettings> injected, instantiated from the appsettings, and then be used to further configure the WsFederationOptions.

How to inject the .NET Core [IServiceProvider] itself into services?

How to inject the [IServiceProvider] interface into custom services? I mean after the [Startup] class finishes construction of [IServiceProvider] from [IServiceCollection] set of bindings. How do I then subscribe to the newly created [IServiceProvider] built after method [ConfigureServices] invoked?
If you want to call your service inside startup you should create a scope
// initial database
using (var scope = app.ApplicationServices.CreateScope())
{
var initDatabase = new YourClass(scope.ServiceProvider.GetService<YourServiceInterface>());
}
But Accessing to IServiceProvider in other services don't make sense. If you describe more You could get the right answer.

Blazor concurrency problem using Entity Framework Core

My goal
I want to create a new IdentityUser and show all the users already created through the same Blazor page. This page has:
a form through you will create an IdentityUser
a third-party's grid component (DevExpress Blazor DxDataGrid) that shows all users using UserManager.Users property. This component accepts an IQueryable as a data source.
Problem
When I create a new user through the form (1) I will get the following concurrency error:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread-safe.
I think the problem is related to the fact that CreateAsync(IdentityUser user) and UserManager.Users are referring the same DbContext
The problem isn't related to the third-party's component because I reproduce the same problem replacing it with a simple list.
Step to reproduce the problem
create a new Blazor server-side project with authentication
change Index.razor with the following code:
#page "/"
<h1>Hello, world!</h1>
number of users: #Users.Count()
<button #onclick="#(async () => await Add())">click me</button>
<ul>
#foreach(var user in Users)
{
<li>#user.UserName</li>
}
</ul>
#code {
[Inject] UserManager<IdentityUser> UserManager { get; set; }
IQueryable<IdentityUser> Users;
protected override void OnInitialized()
{
Users = UserManager.Users;
}
public async Task Add()
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
}
What I noticed
If I change Entity Framework provider from SqlServer to Sqlite then the error will never show.
System info
ASP.NET Core 3.1.0 Blazor Server-side
Entity Framework Core 3.1.0 based on SqlServer provider
What I have already seen
Blazor A second operation started on this context before a previous operation completed: the solution proposed doesn't work for me because even if I change my DbContext scope from Scoped to Transient I still using the same instance of UserManager and its contains the same instance of DbContext
other guys on StackOverflow suggests creating a new instance of DbContext per request. I don't like this solution because it is against Dependency Injection principles. Anyway, I can't apply this solution because DbContext is wrapped inside UserManager
Create a generator of DbContext: this solution is pretty like the previous one.
Using Entity Framework Core with Blazor
Why I want to use IQueryable
I want to pass an IQueryable as a data source for my third-party's component because its can apply pagination and filtering directly to the Query. Furthermore IQueryable is sensitive to CUD
operations.
UPDATE (08/19/2020)
Here you can find the documentation about how to use Blazor and EFCore together
UPDATE (07/22/2020)
EFCore team introduces DbContextFactory inside Entity Framework Core .NET 5 Preview 7
[...] This decoupling is very useful for Blazor applications, where using IDbContextFactory is recommended, but may also be useful in other scenarios.
If you are interested you can read more at Announcing Entity Framework Core EF Core 5.0 Preview 7
UPDATE (07/06/2020)
Microsoft released a new interesting video about Blazor (both models) and Entity Framework Core. Please take a look at 19:20, they are talking about how to manage concurrency problem with EFCore
General solution
I asked Daniel Roth BlazorDeskShow - 2:24:20 about this problem and it seems to be a Blazor Server-Side problem by design.
DbContext default lifetime is set to Scoped. So if you have at least two components in the same page which are trying to execute an async query then we will encounter the exception:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread-safe.
There are two workaround about this problem:
(A) set DbContext's lifetime to Transient
services.AddDbContext<ApplicationDbContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
(B) as Carl Franklin suggested (after my question): create a singleton service with a static method which returns a new instance of DbContext.
anyway, each solution works because they create a new instance of DbContext.
About my problem
My problem wasn't strictly related to DbContext but with UserManager<TUser> which has a Scoped lifetime. Set DbContext's lifetime to Transient didn't solve my problem because ASP.NET Core creates a new instance of UserManager<TUser> when I open the session for the first time and it lives until I don't close it. This UserManager<TUser> is inside two components on the same page. Then we have the same problem described before:
two components that own the same UserManager<TUser> instance which contains a transient DbContext.
Currently, I solved this problem with another workaround:
I don't use UserManager<TUser> directly instead, I create a new instance of it through IServiceProvider and then it works. I am still looking for a method to change the UserManager's lifetime instead of using IServiceProvider.
tips: pay attention to services' lifetime
This is what I learned. I don't know if it is all correct or not.
I downloaded your sample and was able to reproduce your problem. The problem is caused because Blazor will re-render the component as soon as you await in code called from EventCallback (i.e. your Add method).
public async Task Add()
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
If you add a System.Diagnostics.WriteLine to the start of Add and to the end of Add, and then also add one at the top of your Razor page and one at the bottom, you will see the following output when you click your button.
//First render
Start: BuildRenderTree
End: BuildRenderTree
//Button clicked
Start: Add
(This is where the `await` occurs`)
Start: BuildRenderTree
Exception thrown
You can prevent this mid-method rerender like so....
protected override bool ShouldRender() => MayRender;
public async Task Add()
{
MayRender = false;
try
{
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" });
}
finally
{
MayRender = true;
}
}
This will prevent re-rendering whilst your method is running. Note that if you define Users as IdentityUser[] Users you will not see this problem because the array is not set until after the await has completed and is not lazy evaluated, so you don't get this reentrancy problem.
I believe you want to use IQueryable<T> because you need to pass it to 3rd party components. The problem is, different components can be rendered on different threads, so if you pass IQueryable<T> to other components then
They might render on different threads and cause the same problem.
They most likely will have an await in the code that consumes the IQueryable<T> and you'll have the same problem again.
Ideally, what you need is for the 3rd party component to have an event that asks you for data, giving you some kind of query definition (page number etc). I know Telerik Grid does this, as do others.
That way you can do the following
Acquire a lock
Run the query with the filter applied
Release the lock
Pass the results to the component
You cannot use lock() in async code, so you'd need to use something like SpinLock to lock a resource.
private SpinLock Lock = new SpinLock();
private async Task<WhatTelerikNeeds> ReadData(SomeFilterFromTelerik filter)
{
bool gotLock = false;
while (!gotLock) Lock.Enter(ref gotLock);
try
{
IUserIdentity result = await ApplyFilter(MyDbContext.Users, filter).ToArrayAsync().ConfigureAwait(false);
return new WhatTelerikNeeds(result);
}
finally
{
Lock.Exit();
}
}
Perhaps not the best approach but rewriting async method as non-async fixes the problem:
public void Add()
{
Task.Run(async () =>
await UserManager.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" }))
.Wait();
}
It ensures that UI is updated only after the new user is created.
The whole code for Index.razor
#page "/"
#inherits OwningComponentBase<UserManager<IdentityUser>>
<h1>Hello, world!</h1>
number of users: #Users.Count()
<button #onclick="#Add">click me. I work if you use Sqlite</button>
<ul>
#foreach(var user in Users.ToList())
{
<li>#user.UserName</li>
}
</ul>
#code {
IQueryable<IdentityUser> Users;
protected override void OnInitialized()
{
Users = Service.Users;
}
public void Add()
{
Task.Run(async () => await Service.CreateAsync(new IdentityUser { UserName = $"test_{Guid.NewGuid().ToString()}" })).Wait();
}
}
I found your question looking for answers about the same error message you had.
My concurrency issue appears to have been due to a change that triggered a re-rendering of the visual tree to occur at the same time as (or due to the fact that) I was trying to call DbContext.SaveChangesAsync().
I solved this by overriding my component's ShouldRender() method like this:
protected override bool ShouldRender()
{
if (_updatingDb)
{
return false;
}
else
{
return base.ShouldRender();
}
}
I then wrapped my SaveChangesAsync() call in code that set a private bool field _updatingDb appropriately:
try
{
_updatingDb = true;
await DbContext.SaveChangesAsync();
}
finally
{
_updatingDb = false;
StateHasChanged();
}
The call to StateHasChanged() may or may not be necessary, but I've included it just in case.
This fixed my issue, which was related to selectively rendering a bound input tag or just text depending on if the data field was being edited. Other readers may find that their concurrency issue is also related to something triggering a re-render. If so, this technique may be helpful.
Well, I have a quite similar scenario with this, and I 'solve' mine is to move everything from OnInitializedAsync() to
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
//Your code in OnInitializedAsync()
StateHasChanged();
}
{
It seems solved, but I had no idea to find out the proves. I guess just skip from the initialization to let the component success build, then we can go further.
/******************************Update********************************/
I'm still facing the problem, seems I'm giving a wrong solution to go. When I checked with this Blazor A second operation started on this context before a previous operation completed I got my problem clear. Cause I'm actually dealing with a lot of components initialization with dbContext operations. According to #dani_herrera mention that if you have more than 1 component execute Init at a time, probably the problem appears.
As I took his advise to change my dbContext Service to Transient, and I get away from the problem.
#Leonardo Lurci Had covered conceptually. If you guys are not yet wanting to move to .NET 5.0 preview, i would recommend looking at Nuget package 'EFCore.DbContextFactory', documentation is pretty neat. Essential it emulates AddDbContextFactory. Ofcourse, it creates a context per component.
So far, this is working fine for me so far without any problems...
I ensure single-threaded access by only interacting with my DbContext via a new DbContext.InvokeAsync method, which uses a SemaphoreSlim to ensure only a single operation is performed at a time.
I chose SemaphoreSlim because you can await it.
Instead of this
return Db.Users.FirstOrDefaultAsync(x => x.EmailAddress == emailAddress);
do this
return Db.InvokeAsync(() => ...the query above...);
// Add the following methods to your DbContext
private SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1);
public TResult Invoke<TResult>(Func<TResult> action)
{
Semaphore.Wait();
try
{
return action();
}
finally
{
Semaphore.Release();
}
}
public async Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action)
{
await Semaphore.WaitAsync();
try
{
return await action();
}
finally
{
Semaphore.Release();
}
}
public Task InvokeAsync(Func<Task> action) =>
InvokeAsync<object>(async () =>
{
await action();
return null;
});
public void InvokeAsync(Action action) =>
InvokeAsync(() =>
{
action();
return Task.CompletedTask;
});
#Leonardo Lurci has a great answer with multiple solutions to the problem. I will give my opinion about every solution and which I think it is the best one.
Making DBContext transient - it is a solution but it is not optimized for this cases..
Carl Franklin suggestion - the singleton service will not be able to control the lifetime of the context and will depend on the service requester to dispose the context after use.
Microsoft documentation they talk about injecting DBContext Factory into a component with the IDisposable interface to Dispose the context when the component is destroied. This is not a very good solution, because a lot of problems happen with it, like: performing a context operation and leaving the component before it finishes that operation, will dispose the context and throw exception..
Finally. The best solution so far is to inject the DBContext Factory in the component yes, but whenever you need it, you create a new instance with using statement like bellow:
public async Task GetSomething()
{
using var context = DBFactory.CreateDBContext();
return await context.Something.ToListAsync();
}
Since DbFactory is optimazed when creating new context instances, there is no significante overhead, making it a better choice and better performing than Transient context, it also disposes the context at the end of the method because of "using" statement.
Hope it was useful.

TDD Best Practice In Using Restful Api in Yii application

I'm constantly looking for the best way to use TDD in Yii app development. Nowday most web app are composed by a fronted, an API layer (usually JSON) to provide async calls to the server and a backend. By my side, most used test in this of app are unit tests and functional ones. The latter the most widely showed in guides and books leverage PHPUnit + Selenium, but Behat + Mink seems very cool too (but I'm not really confident with it yet)
If you ever used functional tests that use a browser (like Selenium) you know that the less you have to run them the better you feel. This cause they're slower, harder to maintain and sometimes (like the popup FB Login using JS SDK) are painful.
When working with a single web page application I care about testing JSON output of my apis. I'd like to test these functionalities with a unit test like approach in order to have faster tests that are easier to maintain. Considering that most of my Controller's action are availaible to Logged only user using accessControl filter I wondered on the best ways to have my tests up and running.
At this moment I think to have two ways to accomplish this
use cUrl toward the desired enpoint to get the JSON directly invoke
the controller's function
In the first scenario I can use fixtures but I got no way to mock CWebUser class (to emulate a logged user), using Apache when the cUrl comes it gets executed by an instance of my CWebApplication that is not the one executed by PHPUnit. I can get rid of this problem by making all my API calls stateless and, as a consequence, removing accessControl filter.
In the second one the only way I found to mock CWebUser class is to override it in the test class that I'm executing. This approach pays until I dont need to test use cases requiring different type of user, and I got no way to change at runtime (or at setup time) my webuser mock. The only way I found to mock my webuser is the one you can find below, this cause $this->getMock('WebUser') doesnt affect anyway CWebApplication's WebUser (read-only) singleton defined in the configuration file.
Here comes a concrete example:
class UserControllerTest extends CDbTestCase
{
public $fixtures=array(
/* NEEDED FIXTURES*/
);
public function testUserCanGetFavouriteStore() {
$controller = new UserController(1);
$result = json_decode($controller->actionAjaxGetFavStore());
$this->assertInternalType('array', $result->data);
$model = $result->data[0];
$this->assertEquals($model->name, "Nome dello Store");
}
}
class WebUser extends CWebUser {
public function getId() {
return 1;
}
public function getIsGuest() {
return false;
}
};
I was wondering if being able to authenticate with the api interface, either by an API key or a user/password combo could be useful.
This is ok if I move toward a almost stateless API integration, but most of the time I just have controller's actions (permitted to logged user only) that returns Json data to populate the frontend.
Anyone can suggest me a better method? Maybe it's just useless to test this kind of JSON output?
Best Regards
Maybe I'm oversimplifying your problem. It sounds like you want to emulate user logins before running the test? If that's the case, why not just create a User object in your fixture and actually log them in before running a test, and log them out after?
Something like:
/**
* Sets up before each test method runs.
* This mainly sets the base URL for the test application.
*/
protected function setUp()
{
parent::setUp();
// login as registered user
$loginForm = new UserLoginForm();
$loginForm->email = USER_EMAIL; // use your fixture
$loginForm->password = USER_PASSWORD; // use your fixture
if(!$loginForm->login()) {
throw new Exception("Could not login in setup");
}
}
protected function tearDown()
{
parent::tearDown();
Yii::app()->user->logout(true);
}
Ok actually the only solution that me and my team found is creating a stub WebUser class.
Rewriting WebUser class in this way you can authenticate a user without having Yii relying on the session.
class WebUserMock extends WebUser {
public function login($identity,$duration=0)
{
$id=$identity->getId();
$states=$identity->getPersistentStates();
if($this->beforeLogin($id,$states,false))
{
$this->changeIdentity($id,$identity->getName(),$states);
$duration = 0;
if($duration>0)
{
if($this->allowAutoLogin)
$this->saveToCookie($duration);
else
throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.',
array('{class}'=>get_class($this))));
}
$this->afterLogin(false);
}
return !$this->getIsGuest();
}
public function changeIdentity($id,$name,$states)
{
$this->setId($id);
$this->setName($name);
$this->loadIdentityStates($states);
}
// Load user model.
protected function loadUser() {
$id = Yii::app()->user->id;
if ($id!==null)
$this->_model=User::model()->findByPk($id);
return $this->_model;
}
};
In the setUp method of your test class you can login any user (in this case leveraging my fixtures)
//a piece of your setUp() method....
$identity = new UserIdentity($this->users('User_2')->email, md5('demo'));
$identity->authenticate();
if($identity->errorCode===UserIdentity::ERROR_NONE)
Yii::app()->user->login($identity);
As a final thing to do just override the user component in the test configuration file and tell him to use this one:
protected/config/test.php
'user'=>array(
'class' => 'application.tests.mock.WebUserMock',
'allowAutoLogin'=>false,
),
Still not sure that this is the best way to handle it but seems to work fine