How can I add Option object as service? - dependency-injection

In my ASP.NET Core application configuration usually doesn't change. So I want to create a typed Options object in ConfigureServices(IServiceCollection services) and add that object as a singleton to the services using services.AddSingleton(typeof(IOptions), configuration.Get<Options>()).
But ASP.NET Core doesn't allow to add additional parameters to the ConfigureServices(IServiceCollection services) method.
So the following isn't possible:
public interface IOptions { ... }
public class Options : IOptions { ... }
...
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services
.AddSingleton(typeof(IOptions), configuration.Get<Options>())
.AddControllers();
}
Is there a way to achieve this (i.e. adding an Option object, configured by ASP.NET configuration services, to the services collection)?

Solution was close at hand and I overlooked it:
IConfiguration is available as a member field in Startup, so it's already available and no need to provide it again as additional argument:
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration) => _configuration = configuration;
// This method gets called by the runtime. Use this method to add services to the container for dependency injection.
public void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton<Options>(_configuration.GetSection("AppSettings").Get<Options>())
.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Options options)
{
Context.ConnectionString = options.DbConStr;
}
}

Related

Injecting IServiceCollection in my custom middleware

I am trying to make a custom middleware that registers that ApplicationDbContextoutside the ConfigureServices method. Basically, I want to move this:
_services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(......);
outside of the ConfigureServices method and to the application pipeline via a custom middleware.
This is what my custom middleware looks like:
public class MyMW
{
IConfiguration _configuration;
RequestDelegate _next;
IServiceCollection _services;
public MyMW(RequestDelegate next, IServiceCollection services, IConfiguration configuration)
{
_configuration = configuration;
_next = next;
_services = services;
}
public async Task Invoke(HttpContext context)
{
//Add DbContext
_services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(....);
//Add Identity
_services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
......
await _next.Invoke(context);
......
}
}
I have injected IServiceCollection in the custom middleware to use it to register the database provider in my middleware. There is also an extension method to use the middleware in the Configure method.
public static class MyMWExtension
{
public static IApplicationBuilder UseMyMW(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMW>();
}
}
I'm getting
InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IServiceCollection' while attempting to activate 'TestProgram.MyMW'.
Which I think is trying to tell me that I can't inject IServiceCollection in to my custom middleware. My end goal is make custom middlewares that will allow me to use different database providers. For eg, a middle that allows SQLServer will be called SqlServerMiddleware, SqliteMiddleware and so on.
I think you need to use Factory-based middleware to achieve this

One EF Core DbContext Multiple Application

How can I use one DbContext with multiple application?
I have a WCF application (Net TCP binding) interface and implementation works fine with the DbContext. There is a need for API from the same application and I don't want to enable Http Binding on the WCF because of configuration and I have so many contracts. so I decided to import the service into asp.net core 2 via DI it works fine but works connect to Db via DbContext always returning null.
DB Context:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options){}
public AppDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"Server=.\;Database=Database;Trusted_Connection=True;MultipleActiveResultSets=true");
}
}
}
Service implementation
public partial class GeneralService : IGeneralService, IDisposable
{
protected readonly AppDbContext Db = new AppDbContext();
public void Dispose()
{
Db.Dispose();
}
}
Asp.net core Start Up
public void ConfigureServices(IServiceCollection services)
{
const string connection = #"Server=.\;Database=Database;Trusted_Connection=True;MultipleActiveResultSets=true";
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connection));
services.AddSingleton<IGeneralService,GeneralService>();
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver());
}
what am I doing wrong, what can I do I really don't want to use Proxy
connect to Db via DbContext always returning null.
I think that might be down to the fact that you're creating the DB context directly in the service class. You can/should inject your DbContext into your service instead. Something like:
public partial class GeneralService : IGeneralService, IDisposable
{
protected readonly AppDbContext Db;
public GeneralService(AppDbContext db)
{
Db = db;
}
// ... etc...
}
Further, since you're providing a connection string to the db in your Startup.cs you don't need the OnConfiguring method in your db context.
Finally, services shouldn't be singletons if they're using EF. See this answer which recommends the Request scope.

Autofac Named Registration AggregateServices

I am attempting to refactor some code in an MVC application using Autofac Aggregate services. One of the services I am attempting to inject is an log4net.ILog that is using the type of a specific controller. edit
Per suggestion, I am using the publicly available LoggingModule from the Autofac example on modules. Leaving that out of the question as it's the same as posted:
var builder = new ContainerBuilder();
builder.RegisterAggregateService<IHomeControllerDependencies>();
//other service registrations
builder.RegisterModule<LoggingModule>();
builder.RegisterType<HomeController>().InstancePerRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
Next, in my aggregate service, I am using the following (leaving out the other services for brevity):
public interface IHomeControllerDependencies
{
ILog Logger { get; }
//other services
}
public class HomeControllerDependencies : IHomeControllerDependencies
{
public HomeControllerDependencies(IComponentContext context)
{
Logger = context.Resolve<ILog>();
//other services
}
public ILog Logger { get; }
//other services
}
I then inject the dependencies object in my HomeController as follows:
public class HomeController : Controller
{
private readonly IHomeControllerDependencies _dependencies;
public HomeController(IHomeControllerDependencies dependencies)
{
_dependencies = dependencies;
}
...
}
I still get the error that 'log4net.ILog' has not been registered.
edit
The interesting thing here is that when I use the Logger explicitly in the Controller's constructor, and take it out of the aggregate service without modifying registrations, it works and gets the logging instance correctly, even correctly typed as the HomeController Logger:
public class HomeController : Controller
{
public ILog Logger { get; }
public HomeController(IHomeControllerDependencies dependencies, ILog logger)
{
_dependencies = dependencies;
Logger = logger;
}
...
}
Aggregate:
public interface IHomeControllerDependencies
{
//other services
}
public class HomeControllerDependencies : IHomeControllerDependencies
{
public HomeControllerDependencies(IComponentContext context)
{
//other services
}
//other services
}
I guess I could keep it this way, but the main reason why I'm using aggregate services is to have a one parameter constructor.
Thanks

Ninject + WebApi + Custom Membership Provider + Property Injection

I have a repository that i use to access user info IAccountCore and I have a custom membership provider that I've implemented as well. I have property injection setup in the membership provider but it seems that they're never injected. I've followed all the examples on the net to no avail.
I have the ninject bootstrapper + web activator firing the kernel.Inject(Membership.Provider) in the post startup method but the properties are always null. Here are some code snippets:
Bootstrapper in WebApi
[assembly: WebActivator.PostApplicationStartMethod(typeof(API.App_Start.NinjectWebCommon), "RegisterMembership")]
Method being called from above:
public static void RegisterMembership()
{
//bootstrapper.Kernel.Inject(Membership.Provider);
bootstrapper.Kernel.Load(new MembershipNinjectModule());
}
Ninject Module (in a different assembly/project):
public class MembershipNinjectModule : NinjectModule
{
/// <summary>
/// Loads the module into the kernel.
/// </summary>
public override void Load()
{
var kernel = Kernel;
kernel.Inject(Membership.Provider);
}
}
Property in custom membership provider:
[Inject]
public IAccountCore AccountCore {get;set;}
Bindings in another ninject module:
public class NinjectConfigurator : NinjectModule
{
public override void Load()
{
var kernel = Kernel
kernel.Bind<IAccountCore>().To<AccountCore>().InTransientScope();
}
}
Code that loads above module in the Ninject boot strapper:
private static void RegisterServices(IKernel kernel)
{
kernel.Load(new NinjectConfigurator());
}
Try to add on the constructor of your custom membership provider this line :
public MyCustomMembershipProvider()
{
NinjectWebCommon.Kernel.Inject(this);
}
On my project, I used an custom membership provider with Ninject, and I just have this line more than you.
Hope it helps

IOC Container use in MVC and a sample project on codeproject

I was wondering if there is a more ellegant pattern for using IOC containers than the way suggested in the codeproject example below.
Surely it is is possible to use an IOC container so that controller constructors are parameterless when you have a solid IOC process in place.
It also means when your application has more than MVC eg WEB-API and whatever else , you are building the same type of solution for that technology.
That doesnt look DRY to me.
Is there a nice pattern someone uses to "register" the Container eg as IMyMagicControllerHat and get singleton using some nice .net System library?
Clearly if you have a UI Depends on Core depends on Model type of application, you are concerned about build dependencies and static calls.
The static calls back to MVCApplication concern me. I was looking for a better approach.
The CODEProject link....
http://www.codeproject.com/Articles/99361/How-To-Use-Unity-Container-In-ASP-NET-MVC-Framewor
In short the relevant code....
public interface IContainerAccessor
{
IUnityContainer Container { get; }
}
public class MvcApplication : HttpApplication, IContainerAccessor
{
private static IUnityContainer _container;
public static IUnityContainer Container
{
get { return _container; }
}
public static IUnityContainer Container
{
get { return _container; }
}
Also there are examples like This from MS that shows a custom static as a solution. http://msdn.microsoft.com/en-us/library/vstudio/hh323691%28v=vs.100%29.aspx I was expecting there might be a well established adapter pattern or similar. Im going to have 100s of accesses to the unity container, so getting the pattern right looks important.
Even blogs that make perfect sense like this, dont show how deep in the application you should get a reference to the original container instance.
http://blog.ploeh.dk/2010/09/29/TheRegisterResolveReleasePattern.aspx
I Appreciate any tips on good ways to solve.
That codeproject article was published in 2010 based on MVC1. MVC3 now contains an IDependencyResolver implementation for better integration with IoC. And for MVC3 with Unity you might look at Unity.MVC3
And if you want to avoid constructor injection you could always register Unity as the default IServiceLocator:
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(Container));
In case anyone is interested, during a google search:
I ended using a custom Global Class. 1 Static Call to start things off.
The rest as Interface/adapter pattern. The example register Unity and Enterprise Library.
And records important details from authenticated request for logging.
... this is a partial extract only... For illustration purposes
Call from MVC Application Global.asax
public class BosMasterWebApplication : System.Web.HttpApplication
{
private bool IsBootStraped;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
IsBootStraped = false;
}
public override void Init()
{
base.Init();
// handlers managed by ASP.Net during Forms authentication
AuthenticateRequest += new EventHandler(AuthHandler);
PostAuthenticateRequest += new EventHandler(PostAuthHandler);
}
public void AuthHandler(object sender, EventArgs e)
{
// if you need to setup anything
}
public void PostAuthHandler(object sender, EventArgs e)
{
// called several times during the authentication process
if (!IsBootStraped)
BosGlobal.HttpBootStrap(this);
if (Request.IsAuthenticated)
{
// did FormsAuthentication.SetAuthCookie was executed if we get here, so set username
BosGlobal.BGA.IBosCurrentState.CurrentUser.SetFromEnvironment();
}
}
}
/// <summary>
/// Central adapter management. Dont abuse this class
/// </summary>
public static class BosGlobal
{
public static IBosGlobalAdpater BGA { get; private set; }
// set up the adapter
static BosGlobal()
{
BGA = new BosGlobalAdapter( );
}
public static void HttpBootStrap(HttpApplication httpApplication)
{
BGA.BootStrap(httpApplication);
}
public static void LocalBootStrap(string machineName)
{ // the LOCAL WPF bootstrap alternative... no http request exists.
BGA.BootStrap(machineName);
}
}
public class BosGlobalAdapter : IBosGlobalAdpater
{
public static bool IsBootStrapped { get; private set; }
/// Unity Container with key dependencies registered
public IUnityContainer IUnityContainer { get; private set; } //IOC Container
//Adapters
public IBosLogAdapter ILogAdapter { get; private set; }
public IBosExceptionManagerAdapter ExceptionManagerAdapter { get; private set; }
public BosGlobalAdapter()
{
IsBootStrapped = false;
IUnityContainer = new UnityContainer();// one container per work process. managing and resolving dependencies
}
public void BootStrap(HttpApplication httpApplication )
{
IBosCurrentState = new BosCurrentState(httpApplication);
BootStrapEnterpriseLibrary();
BootStrapAdapters();
IsBootStrapped = true;
}
private void BootStrapAdapters()
{
ILogAdapter = new BosLogAdapter(IUnityContainer.Resolve<LogWriter>());
ExceptionManagerAdapter = new BosExceptionManagerAdapter(IUnityContainer.Resolve<ExceptionManager>());
}
private void BootStrapEnterpriseLibrary()
{ // now we tell unity about the container manager inside EntLib.
// we dont want 2 containers, so we tell UNity look after EntLib as well please
IUnityContainer.AddNewExtension<EnterpriseLibraryCoreExtension>();
}

Resources