My ConfigureServices section of Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var builder = services.AddIdentityServer()
.AddInMemoryApiResources(Configurations.ApiResources.GetApiResources())
.AddInMemoryClients(Configurations.Clients.GetClients());
services.AddEntityFrameworkNpgsql();
services.AddDbContext<IdentityDbContext>();
services.BuildServiceProvider();
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
// Login Service and User Repo Injection
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ILoginService, LoginService>();
// Connection String Config
services.Configure<ConnectionStringConfig>(Configuration.GetSection("ConnectionStringConfig"));
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
}
I am injecting my loginService into ResourceOwnerPasswordValidator, and I am injecting userRepository into my loginService. ResourceOwnerPasswordValidator is handling the validation of my user's login.
I originally added my repository and loginService as singletons but I got the error
unable to consume scoped instance of DbContext from singleton
userRepository.
As you can see above I changed both my loginService and userRepository instances to transient. Is this a safe way to do it, or is there another way I should choose?
My loginService uses userRepository to talk to the database. However if I add them as singletons,
I get a cannot consume scoped db instance
, so I thought I'd make the whole thing transient.
Is there a better way to do this which would allow me to keep loginService and userRepository as singletons?
Typically you'd only want to use a singleton in a web application if any of the following is true and if the class in question is thread-safe:
Construction of an underlying resource (e.g. a connection to a distributed cache) is expensive
You need to maintain in-memory state for the duration of the application
You need to serialize access to a resource (e.g. an append-only file)
In your case none of these are true so scoped or transient are totally fine.
Related
I have a multitenant application which uses scoped dependencies to retrieve a tenantId from an incoming httprequests to the Azure functions and instantiate various resources based on the tenant.
For example:
services.RegisterScoped((sp)=>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var tenantId = httpContextAccessor.GetTenantId(); // extension method
// Instantiate some other tenant specific dependencies e.g. DBContext.
});
Now, I have a ServiceBusTrigger which processes messages for all tenants. The ServiceBusReceivedMessage parameter contains an object which has a tenantId property so that each message can be processes for a particular tenant.
Is there a way to have the ServiceBusReceivedMessage instantiate a resource, say a DbContext, from within the function? This is necessary because each message may have to persist some data via a DbContext, which needs to be instantiated with a specific connection string based on the tenantId in the message.
Some things I have considered:
Using Activator.CreateInstance(//pass in the tenant connection string after retrieving it manually)
Can ServiceBusRecievedMessage be registered within the serviceBusTrigger per message?? This seems hacky (there isn't a straightforward way to do that) but also the most preferred approach as the rest of the DI containers can be leveraged without having to instantiate objects manually as in the first option.
[FunctionName("MyCustomTrigger")]
public Task Run(
[ServiceBusTrigger("MultiTenantEndpoint")]
ServiceBusReceivedMessage message,
ServiceBusClient client,
ServiceBusMessageActions messageActions,
ILogger logger,
ExecutionContext executionContext)
{
var tenantId = message.GetTenantId();
// register tenantId as scoped for this request only somehow?
await injectedService.Process(message);
}
Alternate approaches or designs for this?
I am refactoring a project that creates multiple DbCOntexts per method call in the data repositories.
So I want to have one instance of the DbContext per HTTP request.
I have tried to do:
container.RegisterType<ApplicationDbContext>(new PerRequestLifetimeManager());
and for every repository:
...
container.RegisterType<IBusinessRepository, BusinessRepository>(new InjectionConstructor(new ApplicationDbContext()));
container.RegisterType<ICountryRepository, CountryRepository>();
...
But that will create a new instance of the DbContext() per repository, of course.
I tried
container.RegisterType<IBranchCategoryRepository, BranchCategoryRepository>(
new InjectionConstructor(container.Resolve<ApplicationDbContext>()));
But that gives me:
The PerRequestLifetimeManager can only be used in the context of an
HTTP request. Possible causes for this error are using the lifetime
manager on a non-ASP.NET application, or using it in a thread that is
not associated with the appropriate synchronization context.
So how can I inject the same instance to each repository?
I'm not familiar with Unity, but i am with various other IoC frameworks.
Why are you trying to set what to inject on the repositories?
container.RegisterType<IBusinessRepository, BusinessRepository>(new InjectionConstructor(new ApplicationDbContext()));
Just leave out the DbContext here, but change it to:
container.RegisterType<IBusinessRepository, BusinessRepository>();
Because you did already register this component, it will be automatically injected in the constructor in each class that have a dependency. Your class should then look like this:
public class BusinessRepository : IBusinessRepository
{
private ApplicationDbContext _context;
public BusinessRepository(ApplicationDbContext context)
{
this._context = context
}
}
This works in Castle Windsor.
I've been trying to modify injected services with values available from authenticated users.
Using the built-in DI container, I added the required service with Scoped lifetime.
services.AddScoped<ITodoRepository, TodoRepository>();
Then registered a custom middleware between authorization and MVC middlewares to modify the repository with user specific information.
// Configure authorization middleware
app.Use(async (context, next) =>
{
var todoRepository = context.ApplicationServices.GetRequiredService<ITodoRepository>();
todoRepository.UserStoreId = context.User.GetUserStoreId();
await next.Invoke();
});
// Configure MVC middleware
When the program execute a request, the injected repository within my controller does not presist the saved value.
Am i doing something wrong?
From my understanding, scoped objects are saved within request.
Also, is there another way to achieve this?
You can create your own service, i.e. IAuthenticatedUserService/AutheticatedUserService.
Into it, you inject IHttpContextAccessor.
public interface IAuthenticatedUserService
{
ClaimsPrincipal User { get; }
}
Then you inject the IAuthenticatedUserService into your repository, where you can access the logged-in user.
Of course you could also directly inject IHttpContextAccessor into your repository and access httpContextAccessor.HttpContext.User within it, but since repositories are usually defined in their own assembly, you'd also need to reference the Microsoft.AspNetCore.Http.Abstractions package from it which would cause a tight(er) coupling.
But if you don't mind this kind of coupling, just inject the IHttpContextAccessor into your repository, they are supposed to be scoped (=per request) or transient anyways.
P.S. don't forget the Dependency Injection/Inversion of Control mantra: "Don't call us, we call you". You have attempted to call "us" (the repository) to set a value.
Am i doing something wrong? From my understanding, scoped objects are saved within request.
I was able to fix the issue by replacing
context.ApplicationServices.GetRequiredService<ITodoRepository>();
with
context.RequestServices.GetRequiredService<ITodoRepository>();
I have some controllers that require a web service connection (an instance of MS Dynamics CRM CrmService) and I would like the controllers to receive this through their constructors. The CRM service has to be set up with a token that is based on who the currently logged in user is (when the user logs in the application authenticates against the CRM and can store the returned token in Session).
I'm not sure how best to supply this instance using Dependency Injection and Ninject. It seems a bit rubbish for the Ninject ToMethod() Func<> to access FormsAuth/Session for the current request (to obtain the token if authenticated) to create the appropriate instance. I'm also not sure what should happen if the user is not authenticated - I don't need these users be able to access the controller but the controller will be instantiated before any filters like [Authorize] will run so the dependency will always have to be met. From what I have read returning null is not ideal and I would have to change the Ninject configuration to do this anyway.
I was thinking that maybe the controller could get an instance of ICrmServiceFactory or something but that doesn't help me if the controllers end up having other dependencies which also rely on CrmService directly (and don't want to be passed a factory).
Any advice on how to solve this would be appreciated.
I usually set up a binding for IPrincipal:
kernel.Bind<IPrincipal>().ToMethod(c => HttpContext.Current.User);
Never really had a problem with this approach.
If I understand your question correctly then your controller has a dependency to CrmService and the CrmService requires some token stored in the session.
In that case you could inject a CrmTokenProvider to CrmService and add a property to that class which gets the value from the session whenever it is requested by the CrmService.
public class CrmService
{
public CrmService(CrmTokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}
public void DoSomeWork()
{
...
this.tokenProvider.Token;
...
}
}
I have ended up implementing this as follows:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<CrmService>()
.ToMethod(context =>
{
//return unauthenticated instance if user not logged in.
if (!HttpContext.Current.User.Identity.IsAuthenticated) return new CrmService();
return GetConnection(HttpContext.Current);
})
.InRequestScope();
}
private static CrmService GetConnection(HttpContext ctx)
{
//get stuff out of session and return service
}
I just want to know what would be best practice/ widely used, I currently do my logging in the domain service layer, however anything that happens inside my web application layer is not logged.
I would like one centralized and simple location to do all my create/delete/update loggging...
I have heard of Elmah, not sure how useful it is for domain service layer logging only...
I currently do not use any framework for logging at all, I just string builder my log message or get the exception and flush it into the database... Is this the best way to go about it?
If it matters... I need to use Ninject to inject in my ILoggingService
NOTE: I am not talking about Logging User Activity... that will definetly reside only inside my domain service layer...
Haroon,
Leverage Ninject to create and manage the lifetime of an ILoggingService. The implementation of that service should be built directly on top of a well tested logging library like NLog or log4net.
Once you have an instance of the service, you can easily inject it into either you MVC controller or your domain layer. All logging should happen against that instance, not a static logging class.
This will allow you to have the unified logging you are looking for, with a clean separation of concerns.
imho logging should not be injected. The reason is that most of your services (if not all) will use logging.
If you look at most logging frameworks (like nlog), they are using a singleton/facade and abstract factories to provide logging.
Something like:
public static class LogFactory
{
private static ILogFactory _instance;
public void Assign(ILoggingFactory factory)
{
_instance = factory;
}
public ILogger CreateFor<T>()
{
return _instance.CreateFor<T>();
}
}
The design makes your services only dependent of one class and one interface. Thus it's still extremely easy to switch logging implementations.
In your class use the code like:
public class ServiceImp : IService
{
private ILogger _logger = LogFactory.CreateFor<IService>();
public void SomeMethod()
{
_logger.Warning("Something went wrong, but we can handle it. Hence only a warning");
}
}