Im new to DI libraries and trying to use Autofac in a WebApi 2 project with Owin. This is my Owin Startup class,
[assembly: OwinStartup(typeof(FMIS.SIGMA.WebApi.Startup))]
namespace FMIS.SIGMA.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
WebApiConfig.Register(config);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
When I call a Api method I get this error
An error occurred when trying to create a controller of type
'MyController'. Make sure that the controller has a
parameterless public constructor.
What am I missing here?
MyController code is something like this
public class MyController : ApiController
{
ISomeCommandHandler someCommanHandler;
public MyController(ISomeCommandHandler SomeCommandHandler)
{
this.someCommanHandler = SomeCommandHandler;
}
// POST: api/My
public void Post([FromBody]string value)
{
someCommanHandler.Execute(new MyCommand() {
Name = "some value"
});
}
// GET: api/My
public IEnumerable<string> Get()
{
}
// GET: api/My/5
public string Get(int id)
{
}
}
You have set DependencyResolver to AutofacWebApiDependencyResolver, so Autofac comes into play and instantiates dependencies for you. Now you have to explicitly tell Autofac which concrete implementations should be used when an instance of interface is required.
Your controller requires an instance of ISomeCommandHandler:
MyController(ISomeCommandHandler SomeCommandHandler)
So you need to configure types that expose that interface:
builder.RegisterType<CommandHandler>.As<ISomeCommandHandler>();
Have a look at this documentation section for more examples about Autofac registration concepts.
Related
I'm trying to use Autofac to have one instance of the ITransportHeartbeat interface for my ASP.NET MVC 5 app to track all connected users. I use the ITransportHeartbeat interface to determine if a user's connection is still active. To do this, I need to have one instance of the SignalR hub for the app by using Autofac.
The challenge: when I run the app, it never hits the overridden OnConnected(), OnReconnected(), or OnDisconnected() in the SignalR hub. However, the client's hub.start() is hit and I see the Hub Started message when the page loads:
$.connection.hub.start().done(function () {
console.log("Hub Started");
});
If I add a parameterless constructor to the hub, it does hit them, but then the ITransportHeartbeat interface is null and that defeats the point of injecting the interface into the hub.
I've referenced the following for help:
https://stackoverflow.com/a/49214891/177416
https://autofaccn.readthedocs.io/en/latest/integration/signalr.html
https://stackoverflow.com/a/36476106/177416
https://stackoverflow.com/a/21126852/177416
Here is Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
var signalRConfig = new HubConfiguration();
builder.RegisterType<MonitoringHubBase>().ExternallyOwned();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
.As<Microsoft.AspNet.SignalR.IDependencyResolver>()
.SingleInstance();
builder.Register(context =>
context.Resolve<Microsoft.AspNet.SignalR.IDependencyResolver>()
.Resolve<IConnectionManager>()
.GetHubContext<MonitoringHubBase, IMonitoringHubBase>())
.ExternallyOwned();
builder.RegisterType<Microsoft.AspNet.SignalR.Transports.TransportHeartbeat>()
.As<Microsoft.AspNet.SignalR.Transports.ITransportHeartbeat>()
.SingleInstance();
var container = builder.Build();
DependencyResolver.SetResolver(
new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
signalRConfig.Resolver = container
.Resolve<Microsoft.AspNet.SignalR.IDependencyResolver>();
app.UseAutofacMiddleware(container);
app.MapSignalR("/signalr", signalRConfig);
config.DependencyResolver =
new AutofacWebApiDependencyResolver((IContainer)container);
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Here is the hub from which the other hubs derive:
public interface IMonitoringHubBase
{
Task OnConnected();
Task OnReconnected();
Task OnDisconnected(bool stopCalled);
}
public abstract class MonitoringHubBase : Hub<IMonitoringHubBase>
{
private ITransportHeartbeat Heartbeat { get; }
public MonitoringHubBase(ITransportHeartbeat heartbeat)
{
Heartbeat = heartbeat;
}
public MonitoringHubBase()
{
}
public override async Task OnConnected()
{
// Add connection to db...
}
public override async Task OnReconnected()
{
// Ensure connection is on db...
}
public override async Task OnDisconnected(bool stopCalled)
{
// Remove connection from db...
}
public async Task SomeMethod(int id)
{
// Heartbeat object used here...
}
}
And here's one of the hubs that inherits from the base hub:
[HubName("MyHub")]
public class MyHub : MonitoringHubBase
{
public MyHub(ITransportHeartbeat heartbeat) : base(heartbeat)
{
}
public MyHub()
{
}
}
Found a solution! Removed the parameterless c'tor from the hub and modified Startup.cs to this; hope this helps the next person struggling with this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
builder.RegisterType<Microsoft.AspNet.SignalR.Transports.TransportHeartbeat>()
.As<Microsoft.AspNet.SignalR.Transports.ITransportHeartbeat>()
.SingleInstance();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
.As<Microsoft.AspNet.SignalR.IDependencyResolver>()
.SingleInstance();
var container = builder.Build();
var signalRConfig = new HubConfiguration();
signalRConfig.Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
app.Map("/signalr", map =>
{
map.UseAutofacMiddleware(container);
var hubConfiguration = new HubConfiguration
{
Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container),
};
map.RunSignalR(hubConfiguration);
});
config.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container);
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
With help from this article: https://kwilson.io/blog/get-your-web-api-playing-nicely-with-signalr-on-owin-with-autofac/
Im trying to use Hangfire with Microsoft.Extensions.DependencyInjection in an Asp.Net 4.7.2 MVC Application.
I have the DI setup and working, I just cant seem to configure Hangfire correctly. The current implementation below runs the dashboard, but executing a background task returns the error:
JobActivator returned NULL instance of the 'Dh.Web.Services.EmailService' type.
I know EmailService is correctly setup in DI because I can access it using DI in the same controller that is calling the BackgroundJob.Enqueue method.
My Implementation is:
HangfireActivator.cs
public class HangfireActivator : JobActivator
{
private readonly IServiceProvider _serviceProvider;
public HangfireActivator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object ActivateJob(Type type)
{
return _serviceProvider.GetService(type);
}
}
ConfigureHangfire Method inside startup.cs
private void ConfigureHangfire(IServiceProvider serviceProvider, IAppBuilder app)
{
var hangfireConnString = ConfigurationManager.ConnectionStrings["Dh"].ConnectionString;
GlobalConfiguration.Configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseActivator(new HangfireActivator(serviceProvider))
.UseSqlServerStorage(hangfireConnString, new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.FromSeconds(10),
UseRecommendedIsolationLevel = true,
UsePageLocksOnDequeue = true,
DisableGlobalLocks = true,
}
);
app.UseHangfireServer(new BackgroundJobServerOptions {WorkerCount = 3});
var options = new DashboardOptions()
{
Authorization = new[] {new SystemAuthorizationFilter()}
};
app.UseHangfireDashboard("/hangfire",options);
}
and then finally Configuration Method in Startup.cs
public void Configuration(IAppBuilder app)
{
var services = new ServiceCollection();
//App DI
ConfigureServices(services);
var serviceProvider = services.BuildServiceProvider();
var resolver = new DefaultDependencyResolver(serviceProvider);
DependencyResolver.SetResolver(resolver);
//Hangfire
ConfigureHangfire(serviceProvider,app);
}
I have a suspicion that its the line: var serviceProvider = services.BuildServiceProvider(); that is creating a seperate service provider to the one i setup all my App DI in, but i dont know how to get the ServiceProvider to the UseActivator option in Hangfire without that line...
I would really really appreciate any input. Thank you!
I had registered in my DI:
services.AddTransient<IEmailService,EmailService>();
However adding this to register the concrete class without the interface worked.
services.AddTransient<EmailService>();
When using IdentityServerBearerTokenAuthentication middle-ware, I would like to have access to a service that is registered in my WebApi config. Specifically, I want to call a service to give me the default user id and add it to the Claims in the the request. See my TODO in the example code below in the OnValidateIdentity lambda.
//startup.cs
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["Federation.IdentityServerPath"]
});
// azure functions will authenticate using Azure AD Tokens
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
Provider = new OAuthBearerAuthenticationProvider()
{
OnValidateIdentity = context =>
{
//add the account id claim if it's specified in the header
var accountIdString = context.Request.Headers["X-AccountId"];
if (!string.IsNullOrWhiteSpace(accountIdString) && Guid.TryParse(accountIdString, out var _))
{
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountIdString));
}
else
{
//TODO: Need to pull the system user or admin user and jam that account id into NameIdentifier.
var systemDefaultId = "";
// How do I get a dependency from HttpContext or context.OwinContext?????
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, systemDefaultId));
}
context.Ticket.Identity.AddClaim(new Claim("cl_aad_user", "true"));
return Task.FromResult<object>(null);
}
}
}
);
}
}
}
The application services are registered with the autofac builder pattern.
e.g.
builder.RegisterType<AccountInfoService>().As<IAccountInfoService>().InstancePerRequest(); in a static method that is called in a Global class (Global.asax).
// Global.asax.cs
...
private void Application_Start(object sender, EventArgs e)
{
// ... truncated code
// registers the services
GlobalConfiguration.Configure(WebApiConfig.Register);
}
I have tried grabbing a service from the HttpContext, but it always resolves to null. (i.e. var db = (IOutcomesContext)HttpContext.Current.Request.RequestContext.HttpContext.GetService(typeof(IAccountService));)
I have also checked this answer, but I am not using that middle ware.
This is how I register the dependencies.
// WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
RegisterIoc(config);
}
private static void RegisterIoc(HttpConfiguration config)
{
ContainerBuilder builder = GetIocContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
//all the external dependencies
builder.RegisterType<InstitutionRequestContext>().As<IInstitutionRequestContext>().InstancePerRequest();
Autofac.IContainer container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
internal static ContainerBuilder GetIocContainerBuilder()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// ... truncated service list
builder.RegisterType<AccountInfoService>().As<IAccountInfoService>().InstancePerRequest();
return builder;
}
}
Thanks for the help.
I ended up solving the above problem by switching the application to use the app.UseAutofacMiddleware(container); middle ware as described in this answer.
For my particular use case, it only involved a few changes, specifically...
removing the autofac from the Global config file (e.g. Global.asax)
updating my WebApiConfig to return the Autofac.IContainer
adding 3 or so method calls to the Startup.Configure class as described in their documentation
I've been using Identity 2.2.1 with Ninject on a Web API project and all seems to be working well other than the UserManager. The UserManager is created but when it's injected into a service it only contains it's default implementation and not the configuration it should be using.
I've tried to follow multiple solutions to answers on here but to no avail. Could someone point out where I'm going wrong please.
Startup.cs
public class Startup
{
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
var kernel = NinjectConfig.CreateKernel();
app.UseNinjectMiddleware(() => kernel);
WebApiConfig.Register(config);
ConfigureOAuth(app, kernel);
app.UseNinjectWebApi(config);
app.UseWebApi(config);
}
private void ConfigureOAuth(IAppBuilder app, IKernel kernel)
{
DataProtectionProvider = app.GetDataProtectionProvider();
app.CreatePerOwinContext<UserManager>((option, context) =>
{
return kernel.Get<UserManager>();
});
app.UseCors(CorsOptions.AllowAll);
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/token"),
Provider = new AuthorizationServerProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(6),
AllowInsecureHttp = true // TODO: Change when in production
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
NinjectConfig.cs
public static class NinjectConfig
{
public static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
}
}
IdentityModule.cs
public class IdentityModule : NinjectModule
{
public override void Load()
{
Bind<IUserStore<User>>().To<UserStore<User>>().WithConstructorArgument("context", new NeedsCollectorDbContext());
Bind<UserManager>().ToSelf();
}
}
UserManager.cs
public class UserManager : UserManager<User>
{
public UserManager(IUserStore<User> store) : base(store)
{
UserValidator = new UserValidator<User>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
var dataProtectionProvider = Startup.DataProtectionProvider;
if (dataProtectionProvider != null)
{
UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"));
}
}
}
The UserManager gets instantiated with the correct configuration within Startup.cs but when injected into the service later it's lost, please see the screenshots - the UserTokenProvider is null when injected.
Startup.cs
Within a service
Thanks
I am using OAuth token based authentication in my web api based project.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
//At this line, I am getting Error Object reference not set.
if (_membershipService.ValidateUser(context.UserName.Trim(), context.Password.Trim()))
{
var user = _membershipService.GetUser(context.UserName);
/* db based authenication*/
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", user.Email
},
{
"role", (user.Role).ToString()
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
else
{
context.Rejected();
}
}
While calling _membershipService.ValidateUser(), I am getting Object reference not set. Which is quite obvious.
So in order to fix this error, I defined the constructor as.
public SimpleAuthorizationServerProvider(IMembershipService membershipService)
{
_membershipService = membershipService;
}
And while calling the method, I am passing the reference.
Startup.cs
public void ConfigureOAuth(IAppBuilder app)
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//passing the membership object.
Provider = new SimpleAuthorizationServerProvider(_membershipService),
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
//Token Consumption
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
Object reference not set to an instance of an object.
So I tried to overload constructor of Startup.cs as below:
public Startup(IMembershipservice membership)
{
_membership = membership;
}
But this throws the below run time error
No parameterless constructor defined for this object.
Startup.cs
public Startup()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
RegisterIOC();
}
private void RegisterIOC()
{
container = new WindsorContainer();
container.Register(Component.For<SimpleAuthorizationServerProvider>().LifestyleTransient());
//rest of the stuff
}
How do I instanitate the Membership Class.
(Please note I am using Castle Windsor DI Container).
Any help/suggestion highly appreciated.
Thanks.
You say that you are using the Castle Windsor DI Container but there is no code that initializes container in the Startup.cs class.
If you use OWIN (Startup.cs class) then you don't need the application initialization in the Global.asax Application_Start method.
You should move the container initialization to the Startup.cs class and resolve the IMembershipservice instance after the initialization.
Something like this:
public void Configuration(IAppBuilder app)
{
var container = new WindsorContainer().Install(new WindsorInstaller());
container.Register(Component.For<IAppBuilder>().Instance(app));
var httpDependencyResolver = new WindsorHttpDependencyResolver(container);
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.DependencyResolver = httpDependencyResolver;
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorControllerActivator(container));
app.UseWebApi(config);
}
Then you can use the container to resolve the instance of your class.