There's a few posts on SO relating to the work around for allowing autofac injections with ValidationAttribute 's (Asp.Net MVC3: Set custom IServiceProvider in ValidationContext so validators can resolve services) but I'm using OWIN and WebApi so I'm not sure if this is possible?
All other dependency injection is working fine.
DependencyResolver isn't populated in OWIN and I remember reading a difference in how OWIN handles injections for the validation requests. Does anyone know if Autofac, OWIN, WebApi and ValidationAttribute 's are possible? And, with specific examples?
You need to register the Autofac middleware and then you need to extend it to the WebApi. Now you can use the Autofac resolution inside the OWIN middleware.
// Register the Autofac middleware FIRST.
app.UseAutofacMiddleware(container);
// extend the autofac scope to the web api
app.UseAutofacWebApi(HttpConfiguration);
After this, WebApi and OWIN middleware will share the same resolution context, and you can do whatever you want.
For the ValidationAttribute thing you can, for example, do something like this:
public class AppStartup
{
public void Configuration(IAppBuilder app)
{
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
//Set builder
var builder = new ContainerBuilder();
//IoC container build
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(HttpConfiguration);
WebApiConfig.Register(HttpConfiguration);
app.UseWebApi(HttpConfiguration);
}
}
and then
public override bool IsValid(object value)
{
var dependencyResolver = (AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver;
using (var lifetimeScope= dependencyResolver.BeginScope())
{
var foo = lifetimeScope.Resolve<Foo>();
// use foo
}
}
Related
In .Net 5, we use to be able to call the migration by passing DataContext to Configure method and call the migration in startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
// migrate any database changes on startup (includes initial db creation)
dataContext.Database.Migrate();
...
}
How can we do it in .Net 6?
Short Version
It sounds like the real question is where to put code that used to live in Startup.Configure.
In Program.cs use
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Rather long explanation
The Applying Migrations at Runtime section in the EF Core Migrations docs shows that nothing's changed as far as EF Core is concerned.
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
//Same as the question
db.Database.Migrate();
}
host.Run();
}
It sounds like the real question is where to put code that used to live in Startup.Configure. That code can be placed in the Main method or, if Minimal APIs are used, inside Program.cs. Configuration, Services, Environment etc are available as properties in the WebApplicationBuilder class or the WebApplication created by it. WebApplicationBuilder contains the builder interfaces for DI, configuration, Logging and the host, eg WebApplicationBuilder.Services exposes IServiceCollection.
WebApplication properties expose the middleware configured by WebApplicationBuilder, eg WebApplication.Services exposes IServiceProvider
Startup replacement in Minimal APIs
The methods that were in Startup.cswere merged in Program.cs in .NET 6. Startup.cs contained two kinds of methods:
Methods to configure the host and application, like setting up configuration and DI, by calling the various builder interfaces like IServiceCollection, IConfigurationBuilder. This includes the code that used to be in Startup.ConfigureServices.
Methods that used the host to configure endpoints, use services and middleware. This includes code that was in Startup.Configure.
In .NET 6, the interfaces move to the WebApplicationBuilder and WebApplication classes. Instead of .NET Core calling a "magic" Startup class and injecting the interfaces, the code in Program.cs can access the interfaces it needs directly.
The host building/configuration services are now available through the WebApplicationBuilder class.
Interfaces provided by the complete application host are now available through the WebApplication class which is built by the WebApplicationBuilder.
If you don't need to configure services, you can create a minimal API application with just 3 lines :
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
In your case you need to configure the DbContext at least, so you need to use WebApplicationBuilder and WebApplication separately. This is shown in the next section
Migrations in Minimal APIs
In the basic minimal API Program.cs :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
DbContexts can be created once a WebApplication instance is created through its Services property:
var builder = WebApplication.CreateBuilder(args);
//Register the DbContexts etc.
...
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
app.MapGet("/", () => "Hello World!");
app.Run();
Of course it's a lot better to use separate methods or classes for such code, keeping Program.cs clean :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
ApplyMigrations(app);
app.MapGet("/", () => "Hello World!");
app.Run();
static void ApplyMigrations(WebApplication app)
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Or even :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
app.ApplyMigrations()
.UseCustomLogging()
.DoSomeOtherConfiguration()
...;
app.MapGet("/", () => "Hello World!");
app.Run();
With ApplyMigrations an extension method in a separate class :
public static DataExtensions
{
public static WebApplication ApplyMigrations(this WebApplication app)
{
using var scope = app.Services.CreateScope()
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
return app;
}
}
In ASP.NET Core 6, it should be:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
db.Database.Migrate();
}
While Microsoft provides an example of adding a custom configuration source in ConfigureAppConfiguration, that is too early for what I need to do, as I need DI to add services before I am ready or even know if I have custom providers to register. Is there anyway I can add to the configuration sources/providers during Startup.Configure? I'm fine this source is only available in subsequent requests after application startup.
In an ASP.NET Core 3.1 project, I've tried injecting IConfigurationRoot but I cannot find a way to add to the Providers enumerable. Any help you can offer would be great.
Here is some pseudo-pseudo code demonstrating what I would like to do in an ideal/fool's world:
public class Startup
{
private IConfigurationRoot ConfigurtionRoot;
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonSettings(env.ContentRootPath, env.EnvironmentName)
.AddEnvironmentVariables();
ConfigurationRoot = builder.Build();
}
public void ConfigureServices(IServicesCollection services)
{
services.AddServicesNeededForCustomConfigProvider();
}
public void Configure(IApplicationBuilder app)
{
var provider = app.ApplicationServices.GetRequiredService<ICustomConfigProvider>();
// This is where we need some magic to add providers/sources after the initial configuration is built.
ConfigurationRoot.AddProvider(provider);
}
}
I am using the Options pattern in my AspNet Core 1.1 application. Everything is set according to the documentation:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<AppOptions>(Configuration);
...
}
I would like to inject the AppOptions class into a service that I am creating:
public class MyService
{
private readonly AppOptions options;
public MyService(IOptions<AppOptions> optionsAssesor)
{
options = optionsAssesor.Value;
}
...
}
My questions is: when I try to create an instance of the MyService class like:
MyService svc = new MyService();
I am getting an error saying that There is no argument given that corresponds to the required formal parameter optionsAssesor How can I take advantage of the DI and inject the AppOptions into my service?
I am kind of new to .NET Core so I am guessing I am missing something simple, but I cannot figure out what.
Thank you.
I am assuming that Service (MyService) is within scope of ASP.net core project.
You have to do following Thing.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<MyService>();
... other things
}
Now Suppose that you are using your service ( or you want to use your service some controller then you have to do
public class HomeController : Controller
{
private MyService _myService = null;
public HomeController(MyService service)
{
_myService = service;
}
// now you can use _myService in your controller method.
}
Every thing like you have to go through Dependency.
Update 1
Now suppose you want to use it in another class then you can do like this.
For example another class is MyClass1.
public class MyClass1
{
private MyService _myService = null;
public MyClass1(MyService service)
{
_myService = service;
}
}
Now if I am thinking correct then if you are using ASP.net core then MyClass1 one way or other it will use by Controller. You have to register dependency for this.
services.AddScoped<MyClass1>(); // This should be in your ConfigureServices.
It depends on where you are trying to get an instance of MyService from. Basically you need to get a reference to the services collection (and register MyService in DI as well in startup), build a service provider, and then get the service. This will allow DI to construct an instance of MyService and pass it in an instance of AppOptions.
For example:
var provider = serviceCollection.BuildServiceProvider();
var myService = provider.GetService<MyService>();
That being said, in most places in ASP.NET Core you don't need to do this and are better off getting an instance of MyService through the Controller / Middleware constructor without having to build a Service Provider on your own.
You should look at the Microsoft Docs on Dependency Injection in ASP.NET core to further understand the different Service Lifetimes.
I need to get the HttpContext in AspNet Core outside a controller. In the older AspNet 4 I could get it using HttpContext.Current, but it seems to be removed in the new AspNet. The only workaround I have found is resolving an IHttpContextAccessor by dependency injection and asking it the HttpContext, but to inject the IHttpContextAccessor I need to add IHttpContextAccessor as a singleton in the application Startup:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
I researched it and this is the only way I found. I google it and IHttpContextAccessor was removed as a default in the dependency resolver because it is very heavy dependency. Is there any other way to achieve this?
Edit:
I wonder if instead of adding it as a Singleton to the dependency resolver, I could get in that same place the instance of the HttpContextAccessor to save it in my own singleton class?
If you are porting a legacy application to ASP.Net Core that is reasonably complex, it would require totally reengineering to work properly with the .Net Core DI system. If you don't want to do this, you can 'cheat' by making this functionality global again in a Service Locator. To do this (which is not recommended if you can avoid it):
public class RequestContextManager
{
public static RequestContextManager Instance { get; set; }
static RequestContextManager()
{
Instance = new RequestContextManager(null);
}
private readonly IHttpContextAccessor contextAccessor;
public RequestContextManager(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public HttpContext CurrentContext
{
get
{
if (contextAccessor == null)
return null;
return contextAccessor.HttpContext;
}
}
}
// In Startup.cs
public void Configure(IApplicationBuilder app,...)
{
...
RequestContextManager.Instance = new RequestContextManager(app.ApplicationServices.GetService<IHttpContextAccessor>());
...
}
// In your code
var httpContext = RequestContextManager.Instance.CurrentContext;
For HttpContext to be valid, the program flow calling your class must originate in a controller or some middleware component. You could just pass a reference to HttpContext to your class.
To directly answer the question of why, this GitHub Announcement states that it's non-trivial to keep the HttpContext state tracked in IHttpContextAccessor. So it was moved to as-needed only. The HttpContext is still available in a Controller, however, without this being injected.
I don't believe you're pulling in a new dependency on the project level, just into classes via dependency injection, or grabbing it from IServiceProvider when you need it.
I am using Autofac to inject all my project dependencies which is working great. Now I have added a Custom Authorization attribute (I don't need very complex functionality like OWIN and Identity stuff). The custom authorization attribute has dependency to data layer and therefore I am trying to inject it as a property injection. However the property is always Null. The code is below:
public class CustomAuthorizationFilterAttribute : AuthorizeAttribute, IAutofacAuthorizationFilter
{
public IAuthorisationHelper AuthorisationHelper { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
**... removed for brevity**
**// TODO: this should be injected by autofac and is always null??**
if (AuthorisationHelper.IsValidUser(username, password, out roleOfUser))
{
var principal =
new GenericPrincipal((new GenericIdentity(username)),
(new[] { roleOfUser }));
Thread.CurrentPrincipal = principal;
return;
}
... removed for brevity
}
}
Code that injects the AuthorizationHelper:
public static IContainer Container()
{
var builder = new ContainerBuilder();
var assemblies = new List<Assembly>();
assemblies.Add(Assembly.Load("Kids.Math.Interfaces"));
assemblies.Add(Assembly.Load("Kids.Math.Data"));
assemblies.Add(Assembly.Load("Kids.Math.Business"));
assemblies.Add(Assembly.Load("Kids.Math.ImportExport"));
assemblies.Add(Assembly.Load("Kids.Math.Common"));
assemblies.Add(Assembly.Load("Kids.Math.Api"));
builder.RegisterAssemblyTypes(assemblies.ToArray()).
AsImplementedInterfaces();
builder.RegisterType(typeof(MathContext)).As(typeof (DbContext)).InstancePerRequest();
// Register web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// TODO: this is not working, also this should be generic to register it for all controllers
// inject the authorisation filter
builder.RegisterType<AuthorisationHelper>().As<IAuthorisationHelper>();
builder.Register(c => new CustomAuthorizationFilterAttribute()).PropertiesAutowired()
.AsWebApiAuthorizationFilterFor<QuestionsImportController>()
.InstancePerRequest();
// Set the dependency resolver to be Autofac.
var container = builder.Build();
return container;
}
Attribute is registered in FilterConfig as
filters.Add(new CustomAuthorizationFilterAttribute());
All the wiring up works but AuthorisationHelper is always null.
Any comments will be appreciated.
Aren't you missing some key registration steps here? Refer to the Autofac doco
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
EDIT: After being told that the configuration has been setup correctly, have you tried registering your filter like this?
builder.RegisterType<CustomAuthorizationFilterAttribute>().PropertiesAutowired()
.AsWebApiAuthorizationFilterFor<QuestionsImportController>()
.InstancePerRequest();
seems like this is a known bug in autofac:
https://code.google.com/p/autofac/issues/detail?id=289