46

I am migrating a large codebase from Ninject to Autofac and am struggling on one of the bindings that I believe is causing an activation error based on some of my debugging.

Ninject:

Bind<ISecureDataFormat<AuthenticationTicket>>()
     .ToMethod(context =>
         {
             var owinContext = context.Kernel.Get<IOwinContext>();
             return owinContext
                    .Get<ISecureDataFormat<AuthenticationTicket>>("SecureDataFormat");
         });

Autofac (what I have):

builder.Register(
    context => context.Resolve<IOwinContext>()
        .Get<ISecureDataFormat<AuthenticationTicket>>("SecureDataFormat"))
        .As<ISecureDataFormat<AuthenticationTicket>>();

Startup.cs:

var container = RegisterIoC(app, config);

public IContainer RegisterIoC(IAppBuilder app, HttpConfiguration config)
    {
        var builder = new ContainerBuilder();
        builder = RegisterDependencies(builder);
        builder = RegisterFilters(builder, config);

        /*builder.RegisterModule<DebuggingRequestModule>();*/

        var container = builder.Build();

        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        app.UseAutofacMiddleware(container);
        app.UseAutofacMvc();
        app.UseAutofacWebApi(config);
        app.UseWebApi(config);

        return container;
    }

More:

builder.RegisterModule<ApiDependencyModule>().RegisterModule<AutofacWebTypesModule>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(typeof(AccountController).Assembly);

(seemingly) associated constructors:

AccountController.cs:

public AccountController(ILoginService loginService,
                             IBearerTokenStore tokenStore,
                             IRememberMeCookieService rememberMeCookieService,
                             IInternalSsoChallenge ssoChallenge)
{
}

LoginService.cs:

public LoginService(IBearerTokenStore tokenStore, 
        IGrantTypeProvider grantProvider, 
        IRememberMeCookieService rememberMeCookieService)
{
}

BearerTokenCookieStore.cs:

public BearerTokenCookieStore(IOwinContext owinContext, ISecureDataFormat<AuthenticationTicket> secureDataFormat, IAppSettingsReader appSettingsReader, ICookieService cookieService)
{
}

I have a logging module that is helping me debug, and this is the message list I have:

Resolving  _______.Login.Controllers.AccountController
--Resolving  _______.ApiGateway.Services.Auth.LoginService
----Resolving  _______.ApiGateway.Security.OAuth.BearerTokenCookieStore
------Resolving  Microsoft.Owin.Security.DataHandler.SecureDataFormat`1[Microsoft.Owin.Security.AuthenticationTicket]
Exception thrown: 'Autofac.Core.DependencyResolutionException' in Autofac.dll
Exception thrown: 'Autofac.Core.DependencyResolutionException' in Autofac.dll
Exception thrown: 'Autofac.Core.DependencyResolutionException' in Autofac.dll
The thread 0x1014 has exited with code 0 (0x0).

Edit:

The exception I am seeing:

An error occurred during the activation of a particular registration.
See the inner exception for details. Registration: 
Activator = AccountController (DelegateActivator), 
Services = [____.Login.Controllers.AccountController], 
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, 
Sharing = None, 
Ownership = ExternallyOwned ---> 
An error occurred during the activation of a particular registration. 
See the inner exception for details. 
Registration: 
Activator = AccountController (ReflectionActivator), 
Services = [____.Login.Controllers.AccountController], 
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, 
Sharing = None, Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of a particular registration. 
See the inner exception for details. 
Registration: 
Activator = LoginService (DelegateActivator), 
    Services = [____.ApiGateway.Services.Auth.ILoginService],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = ExternallyOwned ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = LoginService (ReflectionActivator),
Services = [____.ApiGateway.Services.Auth.ILoginService],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = BearerTokenCookieStore (DelegateActivator),
Services = [____.ApiGateway.Security.OAuth.IBearerTokenStore],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = ExternallyOwned ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = BearerTokenCookieStore (ReflectionActivator),
Services = [____.ApiGateway.Security.OAuth.IBearerTokenStore],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = Shared,
Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = ISecureDataFormat`1 (DelegateActivator),
Services = [Microsoft.Owin.Security.ISecureDataFormat`1[[Microsoft.Owin.Security.AuthenticationTicket,
Microsoft.Owin.Security,
Version=3.0.1.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35]]],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = ExternallyOwned ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = ISecureDataFormat`1 (DelegateActivator),
Services = [Microsoft.Owin.Security.ISecureDataFormat`1[[Microsoft.Owin.Security.AuthenticationTicket,
Microsoft.Owin.Security,
Version=3.0.1.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35]]],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
Sharing = None,
Ownership = OwnedByLifetimeScope ---> A delegate registered to create instances of 'Microsoft.Owin.Security.ISecureDataFormat`1[Microsoft.Owin.Security.AuthenticationTicket]' returned null. (See inner exception for details.) (See inner exception for details.) (See inner exception for details.) (See inner exception for details.) (See inner exception for details.) (See inner exception for details.) (See inner exception for details.) (See inner exception for details.)

Inner most:

A delegate registered to create instances of 'Microsoft.Owin.Security.ISecureDataFormat`1[Microsoft.Owin.Security.AuthenticationTicket]' returned null.
Laura White
  • 188
  • 2
  • 9
O'Mutt
  • 1,557
  • 1
  • 13
  • 27
  • I did include that, both the ninject setup that used to work and the autofac one that is failing in the question. – O'Mutt Jun 05 '18 at 14:57
  • 1
    can you add your startup? are you using autofac for owin? did you add `app.UseAutofac(container)`? – Alex Terry Jun 05 '18 at 15:13
  • @Mutmatt - can you include also your packages.xml (it is sufficient to include just owin/webapi/mvc/autofac dependencies). The thing that seems odd in startup is mixing MVC4-like bootstrapping with newer bootstrapping. – Ondrej Svejdar Jun 07 '18 at 04:32
  • We don't have a packages.xml file.... – O'Mutt Jun 07 '18 at 21:55
  • @Mutmatt - if not packages (which I recommend - nuget is great ;)) than for sure you have project file - i.e. .csproj with list of dependenices and versions - I'm interested in what is external assemblies you reference and what is version of your *Owin*, *AspNet* and *AutoFac* assemblies. – Ondrej Svejdar Jun 08 '18 at 09:08
  • Try to register the OwinContext into your container `builder.Register(ctx=>HttpContext.Current.GetOwinContext()).As();` Can you also post how does your old Ninject configuration "Bind" the `IOwinContext`? – nemesv Jun 08 '18 at 12:39
  • Have you tried registering whichever you are using as your ISecureDataFormat implementation as an open generic implementation of Microsoft.Owin.Security.ISecureDataFormat<> https://autofaccn.readthedocs.io/en/latest/register/registration.html#open-generic-components. You'd have to do some digging to work out what the default is for that, unless you're setting it somewhere, e.g. Microsoft.AspNetCore.Authentication. SecureDataFormat. Or if it's just Microsoft.AspNetCore.Authentication.TicketDataFormat it would just be a basic registration. – Jamie Gould Apr 27 '19 at 01:51
  • 1
    Honestly, I should have answered this question while I still had the codebase in my possession. I'd have to find someone who still works at my previous employer to find the solution I ended up with. I believe I changed the structure of the injection – O'Mutt Apr 27 '19 at 01:59

3 Answers3

2

The migration from Ninject to Autofac seems to be correct, but the binding for the ISecureDataFormat<AuthenticationTicket> interface may not be working as expected, leading to an activation error in some dependencies. Based on the debugging messages, the BearerTokenCookieStore class is failing to resolve this dependency.

In the Ninject version, ISecureDataFormat<AuthenticationTicket> is bound to a method that retrieves the IOwinContext instance and uses it to resolve the ISecureDataFormat<AuthenticationTicket> named "SecureDataFormat." However, in the Autofac version, the ISecureDataFormat<AuthenticationTicket> instance is directly resolved using the IOwinContext instance, but no name is provided.

It's possible that the ISecureDataFormat<AuthenticationTicket> named "SecureDataFormat" is not being registered correctly in the Autofac container. To fix this, you can try registering the named instance explicitly using the Named extension method:

builder.Register(context =>
context.Resolve<IOwinContext>()
    .Get<ISecureDataFormat<AuthenticationTicket>>("SecureDataFormat"))
    .Named<ISecureDataFormat<AuthenticationTicket>>("SecureDataFormat");

builder.Register(context =>
    context.ResolveNamed<ISecureDataFormat<AuthenticationTicket>>("SecureDataFormat"))
    .As<ISecureDataFormat<AuthenticationTicket>>();

Then, in the BearerTokenCookieStore constructor, you can resolve the named instance like this:

public BearerTokenCookieStore(
    IOwinContext owinContext,
    [Named("SecureDataFormat")] ISecureDataFormat<AuthenticationTicket> secureDataFormat,
    IAppSettingsReader appSettingsReader,
    ICookieService cookieService)
{ }

This should ensure that the BearerTokenCookieStore class correctly resolves the ISecureDataFormat<AuthenticationTicket> named "SecureDataFormat" and that it is used by other dependencies that require it.

O'Mutt
  • 1,557
  • 1
  • 13
  • 27
0

We can use autofac with those c# owin inversion-of-control (ninject to autofac) like below, genericly

Startup.cs

public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {

            var config = new HttpConfiguration();
            WebApiConfig.Register(config);    

            ConfigureIoC(app, config);
            Components.Config();

            Auth.ConfigureForApi(app);

            app.UseAutofacWebApi(config);
            app.UseCors(CorsOptions.AllowAll);
            app.UseWebApi(config);
            config.EnsureInitialized();
        }

        private void ConfigureIoC(IAppBuilder app, HttpConfiguration config)
        {
            IoC.RegisterModules(
                builder =>
                {
                    builder.Register<IAuthenticationManager>(c => c.Resolve<IOwinContext>().Authentication).InstancePerRequest();
                    builder.Register<IDataProtectionProvider>(c => app.GetDataProtectionProvider()).InstancePerRequest();
                    builder.RegisterApiControllers(typeof(Startup).Assembly).PropertiesAutowired();
                },
                container =>
                {
                    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
                    app.UseAutofacMiddleware(container);

                },
                true
                );
        }

    }

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

    }
}

Component config:

public static class Components
{
    public static void Config() {
        TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true;
        var registers = new List<IRegister>();
        registers.Add(new DtoMapping());
        registers.Add(new MappingRegistration());
        TypeAdapterConfig.GlobalSettings.Apply(registers);

        ObjectMapper.Current = new MapsterAdapter();
        DbConfiguration.Loaded += DbConfiguration_Loaded;

        var r = new Registrations();
        r.Run();
    }


    private static void DbConfiguration_Loaded(object sender, System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationLoadedEventArgs e)
    {
        e.ReplaceService<DbProviderServices>((s, k) => System.Data.Entity.SqlServer.SqlProviderServices.Instance);
    }

}

Authentication:

public class Auth
    {
        public static void ConfigureForMvc(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Login"),

                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, UserEntity, int>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentityCallback: (manager, user) =>
                        user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie),
                        getUserIdCallback: (Identity) => Identity.GetUserId<int>())
                }
            });
        }

        public static void ConfigureForApi(IAppBuilder app)
        {
            var OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/oauth2/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(120),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat(ConfigConstants.Issuer)
            };

            app.UseOAuthAuthorizationServer(OAuthServerOptions);

            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { ConfigConstants.AudienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(ConfigConstants.Issuer, ConfigConstants.AudienceSecurityKey)
                    }
                });

        }
    }
Hamit YILDIRIM
  • 4,224
  • 1
  • 32
  • 35
-1

Program.cs

builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new AutofacBusinessModule()));

AutoMapperProfil.cs

namespace BusinessKatman.AutoMappers
{
    public class AutoMapperProfil : Profile
    {
        public AutoMapperProfil()
        {
            CreateMap<PageVM, Page>().ReverseMap();
 }
    }
}

Controllers

private readonly IMapper _mapper;
 public AutoIllerManager(IMapper mapper)
        {            
            _mapper = mapper;
        }
 public IActionResult GetPageDetail
{
var p = _mapper.Map<PageVM>(result);

//and

var p = _mapper.Map<Page>(result);

// list

var p = _mapper.Map<List<Page>>(result);
}