How to inject the .NET Core [IServiceProvider] itself into services? - asp.net-core

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.

Related

Access registered service from Program.cs in ASP.NET Core 6

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>();

Ninject Inject Common DbContext Into Numerous Repositories

There’s something which I am doing that is working, but I think it can probably be done a lot better (and therefore, with more maintainability).
I am using Ninject to inject various things into a controller. The problem which I needed to solve is that the DbContext for each repository needed to be the same. That is, the same object in memory.
Whilst, the following code does achieve that, my Ninject common config file has started to get quite messy as I have to write similar code for each controller:
kernel.Bind<OrderController>().ToMethod(ctx =>
{
var sharedContext = ctx.Kernel.Get<TTSWebinarsContext>();
var userAccountService = kernel.Get<UserAccountService>();
ILogger logger = new Log4NetLogger(typeof(Nml.OrderController));
ILogger loggerForOrderManagementService = new Log4NetLogger(typeof(OrderManagementService));
var orderManagementService = new OrderManagementService(
new AffiliateRepository(sharedContext),
new RegTypeRepository(sharedContext),
new OrderRepository(sharedContext),
new RefDataRepository(),
new WebUserRepository(sharedContext),
new WebinarRepository(sharedContext),
loggerForOrderManagementService,
ttsConfig
);
var membershipService = new MembershipService(
new InstitutionRepository(sharedContext),
new RefDataRepository(),
new SamAuthenticationService(userAccountService),
userAccountService,
new WebUserRepository(sharedContext)
);
return new OrderController(membershipService, orderManagementService, kernel.Get<IStateService>(), logger);
}).InRequestScope();
Is there a neater way of doing this?
Edit
Tried the following code. As soon as I make a second request, an exception is chucked that the DbContext has already been disposed.
kernel.Bind<TTSWebinarsContext>().ToSelf().InRequestScope();
string baseUrl = HttpRuntime.AppDomainAppPath;
kernel.Bind<IStateService>().To<StateService>().InRequestScope();
kernel.Bind<IRefDataRepository>().To<RefDataRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
var config = MembershipRebootConfig.Create(baseUrl, kernel.Get<IStateService>(), kernel.Get<IRefDataRepository>());
var ttsConfig = TtsConfig.Create(baseUrl);
kernel.Bind<MembershipRebootConfiguration>().ToConstant(config);
kernel.Bind<TtsConfiguration>().ToConstant(ttsConfig);
kernel.Bind<IAffiliateRepository>().To<AffiliateRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<IWebinarRepository>().To<WebinarRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<IWebUserRepository>().To<WebUserRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<IOrderRepository>().To<OrderRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<IInstitutionRepository>().To<InstitutionRepository>().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<IUserAccountRepository>().To<DefaultUserAccountRepository>().InRequestScope();
kernel.Bind<IRegTypeRepository>().To<RegTypeRepository>().InRequestScope().WithConstructorArgument("context", kernel.Get<TTSWebinarsContext>());
kernel.Bind<UserAccountService>().ToMethod(ctx =>
{
var userAccountService = new UserAccountService(config, ctx.Kernel.Get<IUserAccountRepository>());
return userAccountService;
});
kernel.Bind<IOrderManagementService>().To<OrderManagementService>().InRequestScope();
//RegisterControllers(kernel, ttsConfig);
kernel.Bind<AuthenticationService>().To<SamAuthenticationService>().InRequestScope();
kernel.Bind<IMembershipService>().To<MembershipService>().InRequestScope();
There's something about InRequestScope I'm misunderstanding.
Edit:
.InRequestScope() will ensure everything which gets injected that binding will receive exactly the same instance when during injection (creation) the HttpContext.Current is the same. That means when a client makes a request and the kernel is asked to provide instances with .InRequestScope(), it will return the same instance for the exact same request. Now when a client makes another request, another unique instance will be created.
When the request ends, ninject will dispose the instance in case it implements IDisposable.
However consider the following scenario:
public class A
{
private readonly DbContext dbContext;
public A(DbContext dbContext)
{
this.dbContext = dbContext;
}
}
and binding:
IBindingRoot.Bind<DbContext>().ToSelf().InRequestScope();
IBindingRoot.Bind<A>().ToSelf().InSingletonScope();
You got yourself a major problem. There's two scenarios how this can pan out:
You are trying to create an A outside of a request. It will fail. Instantiating the DbContext, ninject will look for HttpContext.Current - which is null at the time - and throw an Exception.
You are trying to create an A during a request. Instantiating will succeed. However, When you try to use some functionality of A (which is accessing DbContext in turn) after the request or during a new request, it will throw an ObjectDisposedException
To sum it up, an ObjectDisposedException when you access the DbContext can only be caused by two scenarios:
-you ar disposing the DbContext (or some component which in turn disposes the DbContext) before the request is over.
-you are keeping a reference to the DbContext (again, or to some component which in turn references the DbContext) across request boundaries.
That's it. Nothing complicated about this, but your object graph.
So what would help is drawing an object graph. Start from the root / request root. Then when you're done, start from the DbContext and check who's calling Dispose() on it. If there is no usage inside your code, it must be Ninject who's cleaning up when the request ends. That means, you need to check all references to the DbContext. Someone is keeping a reference across requests.
Original Answer:
You should look into scopes: https://github.com/ninject/ninject/wiki/Object-Scopes
Specifically, .InRequestScope() - or in case that is not appliccable to your problem - .InCallScope() should be interesting to you.
As you are already using .InRequestScope() for the original binding, i suggest that binding the shared context type also .InRequestScope() should be sufficient. It means every dependency of the OrderController will receive the same webinar context instance. Furthermore, if someone else in the same request wants to get a webinar context injected, he will also get the same instance.
You should look into scopes: https://github.com/ninject/ninject/wiki/Object-Scopes
Specifically, .InRequestScope() - or in case that is not appliccable to your problem - .InCallScope() should be interesting to you.
As you are already using .InRequestScope() for the original binding, i suggest that binding the shared context type also .InRequestScope() should be sufficient. It means every dependency of the OrderController will receive the same webinar context instance. Furthermore, if someone else in the same request wants to get a webinar context injected, he will also get the same instance.

gSOAP instance of .NET service

I have a linux c++ client (via gSOAP) to WCF c# server. The WCF c# service contains list of objects, on which some action is executed. Each time i call some function on service, the new object is created, action on that object is executed and that object lands into list in service. at the end i am calling another function on service, which loops over all objects in a list and executes another call on them. this works as intended on c#, with both client and service pure WCF.
it works different via gSOAP. each time i call a first function on service via gSOAP, that action is executed and list is updated. but it is each time new service. so basically i am dealing each time with new service. i do not wont serialize/deserialize object itself, to have it on inux side.
any ideas how to solve this?
on c# side i have something like (unnecessery details skipped)
class Service : IService
{
List list = new List();
void func1(int i)
{
Class1 c = new Class1(i);
c.create();
list.Add(c);
}
void func2()
{
foreach(Class1 c in list)
{
c.close();
}
}
}
on gSOAP side i have something like
Proxy service (endpoint);
service.func1(1);
service.func1(2);
//...
service.func2();
as i said problem is: when func2() is executed it operates on empty list, meaning gSOAP object of Proxy service does not contain c# object of service.
Help, help!
ps.
the solution is found: container made "static" does the trick.

WCF app . Structure map Objectfactory losing instance of an Object

I am trying to get WCF work with Structuremap. I took most of the code from Jimmy Bogard's blog
Integrating StructureMap with WCF
I am reusing all my objects (Domain, NHIBERNATE , Service layer ) that was created for ASP.nET MVC application.I have a separate IoC Library where I bootstrap all regisrties
I have added global.asax and in Application Start Event this is what I have :
var webServiceRegistry = new WebServiceRegistry();
var bootStrapper = new IoC.BootStrapper();
bootStrapper.Start();
WebServiceRegistry is a normal class and in its constructor I have the following:
var appUser = new AppUser
{
Role = userDto.Role,
Email = userDto.Email,
Id = userDto.Id,
CompanyName = dealerDto.Name,
Name = userDto.Name,
IsImpersonated = false,
Impersonater = Guid.Empty
};
System.Threading.Thread.CurrentPrincipal = System.Web.HttpContext.Current.User = appUser;
ObjectFactory.Configure(x => x.For<IAppUser>().Use(appUser));
Everything goes fine - no errors
Things start to fail when my my wcf service class is being instantiated which has a Domainservice object (IDomainServiceObject domainServiceObject) in constructor with 202 error.
The failure is in DomainService Registry where it fails to get default instance of IAppUser
Edit
Initial problem was in that I had error in constructor of StructureMapServiceHost.
Now I dont get any error but I am not getting the instance of IAppUser that I inject. In place of that it creates a new instance of AppUser.
Do anyone have a clue as to why this happens?
I have tried adding HttpContextScope , HybridHttpOrThreadLocalScoped and still same result
I have conformed that at the time all registries are called in bootstrapper IAppUser instance is the once that was provided with values in it (and not created by StructureMap)
In Application Start it does create the whole graph .
As soon as it enters webmethod ILMSUser is a new instance.
Thank you
Since you want the same object back every time you can use singleton
this.For<IAppUser>().Singleton().Use(appUser);

Adding services after container has been built

Is it possible to register a service at run-time, meaning after the ContainerBuilder has been built and the Container has been created (and ContainerBuilder disposed of)?
Yes you can, using the Update method on ContainerBuilder:
var newBuilder = new ContainerBuilder();
newBuilder.Register...;
newBuilder.Update(existingContainer);
Since ContainerBuilder.Update has been deprecated, the new recommendation is to use child lifetime scope.
Adding Registrations to a Lifetime Scope
Autofac allows you to add registrations “on the fly” as you create lifetime scopes. This can help you when you need to do a sort of “spot weld” limited registration override or if you generally just need some additional stuff in a scope that you don’t want to register globally. You do this by passing a lambda to BeginLifetimeScope() that takes a ContainerBuilder and adds registrations.
using(var scope = container.BeginLifetimeScope(
builder =>
{
builder.RegisterType<Override>().As<IService>();
builder.RegisterModule<MyModule>();
}))
{
// The additional registrations will be available
// only in this lifetime scope.
}
Working with Lifetime Scopes