Thats my DI and Automapper setup:
[RoutePrefix("api/productdetails")]
public class ProductController : ApiController
{
private readonly IProductRepository _repository;
private readonly IMappingEngine _mappingEngine;
public ProductController(IProductRepository repository, IMappingEngine mappingEngine)
{
_repository = repository;
_mappingEngine = mappingEngine;
}
}
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
//WebApiConfig.Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
// Filter
config.Filters.Add(new ActionExceptionFilter());
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
// DI
// Register services
var builder = new ContainerBuilder();
builder.RegisterType<ProductRepository>().As<IProductRepository>().InstancePerRequest();
builder.RegisterType<MappingEngine>().As<IMappingEngine>();
// AutoMapper
RegisterAutoMapper(builder);
// FluentValidation
// do that finally!
// This is need that AutoFac works with controller type injection
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
private static void RegisterAutoMapper(ContainerBuilder builder)
{
var profiles =
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(GetLoadableTypes)
.Where(t => t != typeof (Profile) && typeof (Profile).IsAssignableFrom(t));
foreach (var profile in profiles)
{
Mapper.Configuration.AddProfile((Profile) Activator.CreateInstance(profile));
}
}
private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
}
Thats the exception I get when I go to a certain route:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'AutoMapper.MappingEngine' can be invoked with the available services and parameters:
Cannot resolve parameter 'AutoMapper.IConfigurationProvider configurationProvider' of constructor 'Void .ctor(AutoMapper.IConfigurationProvider)'.
Cannot resolve parameter 'AutoMapper.IConfigurationProvider configurationProvider' of constructor 'Void .ctor(AutoMapper.IConfigurationProvider, AutoMapper.Internal.IDictionary`2[AutoMapper.Impl.TypePair,AutoMapper.IObjectMapper], System.Func`2[System.Type,System.Object])'.
QUESTION
What is wrong with my code?
The error come from this line :
builder.RegisterType<MappingEngine>().As<IMappingEngine>();
This line tell Autofac to instanciate a MappingEngine when you need a IMappingEngine. If you look at the available constructor of MappingEngine you will see that Autofac can't use any of them because it can't inject required parameters.
Here are the available constructor of MappingEngine
public MappingEngine(IConfigurationProvider configurationProvider)
public MappingEngine(IConfigurationProvider configurationProvider,
IDictionary<TypePair, IObjectMapper> objectMapperCache,
Func<Type, object> serviceCtor)
One of the solution to fix this issue is to tell Autofac how to create your MappingEngine you can do it by using a delegate registration.
builder.Register(c => new MappingEngine(...)).As<IMappingEngine>();
You can also register a IConfigurationProvider by doing so, Autofac will be able to automatically find the good constructor.
The easiest way to fix this issue is to register a IConfigurationProvider in Autofac
builder.Register(c => new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers))
.As<IConfigurationProvider>()
.SingleInstance();
builder.RegisterType<MappingEngine>()
.As<IMappingEngine>();
You can also find further information here : AutoMapper, Autofac, Web API, and Per-Request Dependency Lifetime Scopes
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/
In a project, I'm using Nancy/TinyIoC for Dependency Injection. I had no problems thus far.
I added SignalR to my project and setup my hubs so that I'm injecting IHubContext into my hub.
I'm running into a problem that when TinyIoC tries to resolve one of its dependency trees, it runs into an ASP.NET type and cannot resolve such. How do I work around this? My first guess was to register the type within TinyIoC, but that seems tedious.
Here's what I have:
public class Startup
{
public void Configure(IApplicationBuilder builder)
{
// Register types from ASP.net
// Pass instances to UseNancy
var hubContext = builder.ApplicationServices.GetService<IHubContext<MessageSender>>();
builder
.UseCors(AllowAllOrigins)
.UseSignalR(HubRegistration.RouteRegistrations)
.UseOwin(x => x.UseNancy());
}
public virtual void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(AllowAllOrigins,
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
services.AddSignalR();
}
}
public class MessageRepo : IRepository<Message>
{
private readonly IDatabase<Message> _database;
private readonly IValidator<Message> _messageValidator;
private readonly IMessageSender<Message> _hubContext;
public MessageRepo(IDatabase<Message> database, IValidator<Message> messageValidator, IMessageSender<Message> hubContext)
{
_database = database;
_messageValidator = messageValidator;
_hubContext = hubContext;
}
}
public class MessageSender : Hub, IMessageSender<Message>
{
public MessageSender(IHubContext<MessageSender> context)
{
_context = context;
}
}
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
Everyone
I am creating a Project using the repository pattern. I am stuck while implementing Dependency Injection using Autofac Library, Please help me How to implement it in Solution.
I have created a console Library Project where I registered all my component like below
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<TestService>().As<ITestService>().InstancePerLifetimeScope();
base.Load(builder);
}
}
But My Question How to tell MVC Project that I have registered my components,
Do I need to call in Global.asax file or there is other best way to do it.
I didn't find any solution that helps me to implement it
Please help me out to implement it.
Github Repository Link - https://github.com/Harshk16/WebApi.
Thank you
You can
Create a Bootstrapper.cs file under the Start_App folder and paste the following code.
Just replace the YOUR_REPOSITORY and YOUR_IREPOSITORY for your implementations.
public static class Bootstrapper
{
public static void Run()
{
SetAutofacContainer();
}
private static void SetAutofacContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// Repositories
builder.RegisterType<YOUR_REPOSITORY>().As<YOUR_IREPOSITORY>().InstancePerRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
In the global.asax
protected void Application_Start()
{
// standard code
//....
// Autofac and Automapper configurations
Bootstrapper.Run();
}
}
I used Dapper in example.
Edit: Make sure you are using appropriate AutoFac MVC Integration DLL in your project.
Repository Configuration
public interface IOrmRepository
{
IDbConnection Connection { get; }
}
public class DapperRepository : IOrmRepository
{
private readonly IDbConnection _context;
public DapperRepository(IDbConnection context)
{
_context = context;
}
public IDbConnection Connection
{
get { return _context; }
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
this.RegisterDependencies(); // these are my dependencies
}
private void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.Register(ctx =>
{
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"];
if (string.IsNullOrWhiteSpace(connectionString.ConnectionString))
throw new InvalidOperationException("No ConnectionString");
return new System.Data.SqlClient.SqlConnection(connectionString.ConnectionString);
}).As<System.Data.IDbConnection>().InstancePerLifetimeScope();
builder.RegisterType<DapperRepository>().As<IOrmRepository>().InstancePerLifetimeScope();
// Other dependencies is here
builder.RegisterModelBinders(typeof(MvcApplication).Assembly);
builder.RegisterModelBinderProvider();
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterSource(new ViewRegistrationSource());
builder.RegisterFilterProvider();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
You need to set the container as below in MCV application.
Refer the link https://autofaccn.readthedocs.io/en/latest/integration/mvc.html# for detailed information.
public static class IocConfigurator
{
public static void ConfigureDependencyInjection()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<Repository<Student>>().As<IRepository<Student>>();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
I have more projects in solution (mostly class library and one WebAPI project) and I have problem in Common project in resolving dependencies inside this project. Here is example how I setting up dependency
[Dependency]
public IDbContextFactory DbContextFactory
{
get { return _factory; }
set
{
_factory = value;
DbContext = _factory.Create();
}
}
[Dependency]
public IUnitOfWorkManager Manager
{
get { return _manager; }
set
{
_manager = value;
_manager.AddOpenUnitOfWork(this);
}
}
and in same project I have registration interfaces and classes into Unity container via this class
public class CommonUnityConfiguration : IUnityConfiguration
{
public void Configurate(UnityContainer container)
{
container
.RegisterType<IUnitOfWorkManager, UnitOfWorkManager>()
.RegisterType<IDbContextFactory, DbContextFactory>();
}
}
and finally in WebAPI I have WebApiConfiguration, where I'm creating Unity container and register all classes.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Create instance for Unity container - let's try to run!
var container = new UnityContainer();
container.RegisterType<IUserRepository, UserRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
// Add project classes to IoC container
new CommonUnityConfiguration().Configurate(container);
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Problem is, IDbContextFactory property and IUnitOfWorkManager aren't resolve. Do you have any advice, what should I check?
I think you need to switch these two lines:
config.DependencyResolver = new UnityResolver(container);
new CommonUnityConfiguration().Configurate(container);
You're setting the DependencyResolver to the UnityREsolver before you fully configure it in your CommonUnityConfiguration class