I have a project with both Mvc Controllers and Web Api controllers.
Everything is wired up using autofac 3 :
var builder = new ContainerBuilder();
builder.Register(x => NHibernateConfigurator.BuildSessionFactory()).SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.Register(x => new WebApiTransactionAttribute()).PropertiesAutowired();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
The problem is that when I add the [Authorize] attribute (the one from System.Web.Http) I get :
Cannot choose between multiple constructors with equal length 1 on type 'System.String'.
Select the constructor explicitly, with the UsingConstructor() configuration method, when the component is registered.
Can someone point me in the right direction ?
Thanks.
Apologies, this is somewhat of a guess but the AnyConcreteTypeNotAlreadyRegisteredSource here looks like it may be too broad a registration:
Provides registrations on-the-fly for any concrete type not already registered with the container.
The Autofac Filter Provider could then be registering all kinds of framework concrete types and getting in a muddle.
It may be if you take this out and put in more targeted registrations it will help, perhaps register by convention instead.
Related
I have a ASP.NET Core app that uses Autofac to inject Automapper.
Firstly, I'm trying to register Automapper profiles from Assembly:
builder.RegisterAssemblyTypes(assembly)
.AssignableTo<Profile>()
.OnActivated(e => System.Diagnostics.Debug.WriteLine(e.Instance.GetType()))
.AutoActivate();
I have introduced some debug logs to check whether my profiles are registred. And it works, I see my profiles in debug window.
Than I register Automapper and try to resolve previously registered profiles:
builder.Register(ctx => new MapperConfiguration(cfg =>
{
var resolvedProfiles = ctx.Resolve<IEnumerable<Profile>>(); // Length is 0
foreach(var resolvedProfile in resolvedProfiles)
{
cfg.AddProfile(resolvedProfile);
}
}).CreateMapper())
.SingleInstance();
Doesn't work unfortunantely. Autofac doesn't resolve any of the previously registered profiles. Why and how can I fix it?
Just add the type to your registration (via .As<Profile>()):
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AssignableTo<Profile>()
.As<Profile>()
.OnActivated(e => System.Diagnostics.Debug.WriteLine(e.Instance.GetType()))
.AutoActivate();
Trying to implement autofac with my WebApi ... but having some issues with lifetime for my objects...
My startup webapi class:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
container.RegisterType<MyConcreteClass>().As<IMyInterface>().InstancePerRequest();
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
But not works
Unable to resolve the type 'IMyInterface' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
- IMyInterface
Details ---> No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. (See inner exception for details.)
Removing this part .InstancePerRequest(); , then works, but the object is not disposing.
What am i doing wrong ?
Thanks!
I strongly suspect the problem lies with the following code:
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
The error message indicates you must resolve dependencies using the dependency resolver, but this is bypassing that and using the container itself.
On a side note, using a service locator is anti-pattern. You should be injecting dependencies into your controllers and other MVC extension points rather than using this approach.
Is there a way to map all my repository objects to its interfaces in a single line. I donĀ“t want to repeat my self in declarations like these:
services.AddScoped<Repository.Interfaces.IModeloRepository, Repository.ModeloRepository>();
services.AddScoped<Repository.Interfaces.IMunicipioRepository, Repository.MunicipioRepository>();
services.AddScoped<Repository.Interfaces.IPeriodoRepository, Repository.PeriodoRepository>();
services.AddScoped<Repository.Interfaces.IPlanRepository, Repository.PlanRepository>();
Here is a declaration of one of these repositories:
public interface IChatRepository : IRepository<Models.Chat>
I already tried something like this:
services.AddScoped(typeof(Repository.Common.IRepository<>), typeof(Repository.Common.BaseRepository<>));
But gets the following error:
Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
An unhandled exception has occurred: Unable to resolve service for type 'SqlExpress.Repository.Interfaces.IChatRepository' while attempting to activate 'SqlExpress.Helpers.LessonTagHelper'.
System.InvalidOperationException: Unable to resolve service for type 'SqlExpress.Repository.Interfaces.IChatRepository' while attempting to activate '
SqlExpress.Helpers.LessonTagHelper'.
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
Unfortunately, the built in DI container in ASP.NET Core is relatively simplistic. If you would like to use more advanced features like these, then you will need to use a different container.
The example below uses StructureMap as that's what I'm familiar with, but it is probably also possible with Autofac, Ninject etc.
Add the StructureMap library to project.json
"StructureMap.Dnx": "0.5.1-rc2-final"
Configure the DI container to use StructureMap, with naming conventions:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddControllersAsServices();
// other service configuration
// Create a new StructureMap container
var container = new Container();
container.Configure(config =>
{
//add all the services that are already configured
config.Populate(services);
config.Scan(_ =>
{
_.WithDefaultConventions();
_.AssemblyContainingType<Startup>();
_.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
});
});
//set ASP.NET Core to use the StructureMap container to build types
return container.GetInstance<IServiceProvider>();
}
It is worth checking our the documentation to see exactly how this works, but the default convention is to automatically register interface types such as IMyInterestingType with their implementation called MyInterestingType.
By using ConnectImplementationsToTypesClosing, each IRepository<> should also be registered to it's implementation.
I have a WCF client used in MVC application which can get data from multiple WCF services, the services are configured the same way and Implement the same Interface the only difference is the address of the exposed endpoint.
This is what I tried:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://service.com/Service")))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
The thing here is that IService will always get data from http://service.com/Service since the address is hardcoded somewhere in the Application_Start method of the MVC application.
Then i tried using metadata:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://foo.com/Service")))
.SingleInstance().WithMetadata("name", "fooservice");
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://bar.com/Service")))
.SingleInstance().WithMetadata("name", "barservice");
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
But this way I will have to edit the code every time I want to add the same WCF service
implemented on a different server.Instead I want to get the address from the database.
Is there any way I can change the address per service call or at least when the instance of the client is created.
Additional explanation:
Lets say I have five exact copies of a website each with it's own domain name and database I want to be able to do the following:
foreach(Provider provider in providers)
{
SetServiceAddress(provider.Address);//how can i do that
_service.GetData()
}
Under the assumptions that:
The binding doesn't change when the address changes (e.g., it doesn't switch from HTTP to HTTPS)
The address might change on a per-request basis
Then I'd probably solve it with a combination of lambdas and a small interface.
First, you'd want something that retrieves the address from your data store:
public interface IAddressReader
{
Uri GetAddress();
}
The implementation of that would read from the database (or environment, or XML config, or whatever).
Then I'd use that in my registrations:
builder
.RegisterType<MyDatabaseAddressReader>()
.As<IAddressReader>();
builder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
builder
.Register(c =>
{
var reader = c.Resolve<IAddressReader>();
var factory = c.Resolve<ChannelFactory<IService>();
var endpoint = new EndpointAddress(reader.GetAddress());
return factory.CreateChannel(endpoint);
})
.As<IService>()
.UseWcfSafeRelease();
That way you can just take in an IService (or Func<IService>) as a constructor parameter and your calling class won't know about Autofac, service location, or endpoints.
If the binding also changes, it gets a little more complicated. You probably don't want a brand new channel factory spun up for every channel, so you'd want to have some sort of caching mechanism where you:
Get the settings from the configuration source.
Compares those settings against the settings currently in use.
If the settings don't match...
Dispose of the previous channel factory.
Create a new channel factory with the new settings.
Cache the channel factory for later reuse.
Return the current channel factory.
If you can use cache dependencies on the settings, all the better, but not every configuration source supports that, so YMMV. I'd probably implement a custom module for that to encapsulate the logic, but I won't write all that out here.
If you want to set the endpoint just before the call each time, you can do this:
containerBuilder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
containerBuilder.Register((c, p) =>
{
var factory = c.Resolve<ChannelFactory<IService>>();
var endpointAddress = p.TypedAs<string>();
return factory.CreateChannel(new EndpointAddress(endpointAddress));
})
.As<IService>()
.UseWcfSafeRelease();
Then you inject this:
Func<string, IService> getService
Then call it like this:
string endpoint = getDataDependentEndpointFromSomewhere();
var service = getService(endpoint);
I have a service that is running on multiple sites, and at start-up the app needs to determine at which site it is running. It does so using a start-up parameter, and based on this the endpoint address can be set dynamically in a property or method, like GetEndPointAddressForService().
In your case it seems that you need to call n services at different sites consecutively. You could definitely configure these in a database or a simple configuration file on disk, load the service definitions including their endpoint addresses at start-up, keep them in a list and do a foreach when collecting data from all existing servers.
The key part of your logic is in the following part of your code:
new EndpointAddress("http://bar.com/Service")
Do a
foreach (ServiceDefinition sd in ServiceDefinitions)
{
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress(sd.EndPointAddress)))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
GoGetTheData();
}
At the end I have used the following implementation:
On application start I register the ChannelFactory type without the endpoint address.
And I use named parameter to register the client so i can be able to assign the address later when I actually call the service.
builder.RegisterType<ChannelFactory<IService>>(new BasicHttpBinding())
.SingleInstance();
builder.Register((c, p) => c.Resolve<ChannelFactory<IService>>().CreateChannel(p.Named<EndpointAddress>("address")))
.UseWcfSafeRelease();
and then I use the service client at runtime like this:
public Data GetData(string url)
{
EndpointAddress address = new EndpointAddress(url);
NamedParameter parameter = new NamedParameter("address", address);
var service = _autofacContainer.Resolve<IService>(parameter);//this is what I have been looking for
Response response = service.GetData();
return CreateDataFromResponse(response);
}
this way I can call the GetData method for each address in the database. And I'm going to able to add more addresses at runtime without code or configuration editing.
I'm using autofac in an asp.net mvc and webapi project.
In the configuration I'm doing this :
var builder = new ContainerBuilder();
builder.Register(x => NHibernateConfigurator.BuildSessionFactory()).SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Now the problem is that in an api controller if I inject ISession via the constructer and also call
DependencyResolver.Current.GetService<ISession>()
it will return 2 different instances.
I'm guessing the problem is because of these 2 lines :
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
But how can I make it return the same instance ?
Edit:
Just to be more clear - I'm expecting the same instance of ISession per HttpRequest. Right now I'm getting different instances on the same request.
Thanks
Ok, I think I found the answer - it can't be done .. at least not with DependencyResolver.
ASP .Net 4 Web Api RC + Autofac manual resolving
I did what it said in the comments, added IComponentContext to the constructor and used that to resolve what I needed.
It seems to be working. Thanks.