简体   繁体   中英

Autofac: How to inject IPrincipal into Repository layer

I am designing an n-tier application using Repository Layer/Service Layer/Presentation Layer using c#.net web api and Autofac DI container. Here is my dilemma. I am trying to unit test my web api controllers but my repositories have a property dependency on IPrincipal which I would like to property inject into my repository layer. I would like to create a MockUser(IPrincipal) and inject this object into my repository. Here is my current hierarchy, my controllers are constructor injected with the service object, my service object is constructor injected with my repository object. This part seems to work. But for some reason, every time I run the test my Principal property is null. Please review the code below and let me know what I am doing wrong:

Repository Base Class:

protected IPrincipal Principal 
{
    get { return _principal; }
}


Autofac Config Static Method

public class AutofacConfig
{
    public static IContainer ConfigContainer()
    {
        var _builder = new ContainerBuilder();

        UserPrincipal principal = MemberFactory.GetTestUser();



        var _config = new HttpConfiguration();
        _builder.RegisterControllers(typeof(BillingController).Assembly);
        _builder.RegisterWebApiModelBinders(typeof(BillingController).Assembly);
        _builder.RegisterApiControllers(typeof(BillingController).Assembly);
        _builder.RegisterModelBinders(typeof(BillingController).Assembly);
        _builder.RegisterModelBinderProvider();
        _builder.RegisterModule(new AutofacWebTypesModule());

        _builder.RegisterSource(new ViewRegistrationSource());
        _builder.RegisterFilterProvider();
        _builder.RegisterWebApiFilterProvider(_config);

        //_builder.Register(x => principal).As<IPrincipal>().PropertiesAutowired();
        _builder.RegisterType<BillingRepository>().As<IBillingRepository>().PropertiesAutowired();
        _builder.RegisterType<UserPrincipal>().As<IPrincipal>().PropertiesAutowired();
        _builder.RegisterType<GroupRepository>().As<IGroupRepository>().PropertiesAutowired();
        _builder.RegisterType<BillingService>().As<IBillingService>().PropertiesAutowired();
        _builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
        _builder.Register(c => principal).As<IPrincipal>();

        var _container = _builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(_container));

        // Create the depenedency resolver.
        var resolver = new AutofacWebApiDependencyResolver(_container);

        // Configure Web API with the dependency resolver.
        _config.DependencyResolver = resolver;

        return _container;
    }
}

Test Controller (Get Method)

[TestClass]
public class BillingControllerTest
{
   [TestMethod]
    public async Task Get()
    {
        var _container = AutofacConfig.ConfigContainer();

        var _controller = _container.Resolve<BillingController>();

        var _bodyCompRecords = await _controller.GetMyOutstandingBills(1, 10);

        Assert.IsNull(_bodyCompRecords);
        Assert.IsNull(_bodyCompRecords.BillingList);
        Assert.IsNull(_bodyCompRecords.CurrentPage);
        Assert.IsTrue(_bodyCompRecords.BillingList.Count > 0);
    }
}

Did you try to add a protected set for this property? I am not sure about autofac but maybe you should decorate this property with some attribute to autoFac knows that is a injectable property.

On the other hand, all threads in .Net applications has a single principal. You can get/set it on the Thread.CurrentPrincipal static property. You could try to set a custom Principal and make your property to result it.

In the setup of your Unittest, you could define it, for sample:

void Setup()
{
   IPrincipal principal = new GenericPrincipal(new GenericIdentity("userName"), new string[] { "role1", "role2" });
   Thread.CurrentPrincipal = principal;
}

In your repository, you could have a property to expose it,

protected IPrincipal Principal 
{
    get { return Thread.CurrentPrincipal; }
}

In a Web application (asp.net webforms/mvc/web api), you could use the HttpContext.Current.User static property, so, just call:

HttpContext.Current.User = principal;

as a good pratice, you could set both HttpContext.Current.User and Thread.CurrentPrincipal . Remember the web api is stateless, so, implement a http module to read arguments from http header and set the principal to your application. I am not sure if it is your case but this article shows how to do this.

http://www.asp.net/web-api/overview/security/basic-authentication

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM