I have an ASP.NET Web API project using ASP.NET Identity for authentication/authorization. I've inserted my own custom Identity implementation, namely my own UserStore to talk with Azure Tables, and removed the EF/SQL Server stuff. However, most of the docs I see out there recommend creating an instance of the UserManagerFactory within the Startup.Auth.cs class as follows:
public static Func<UserManager<CustomIdentityModel>> UserManagerFactory { get; set; }
UserManagerFactory = () => new UserManager<CustomIdentityModel>(new CustomUserStore<CustomIdentityModel>());
My CustomUserStore has a dependency on a repository in another project, which I dependency inject into the constructor.
private readonly IRepository _repo;
public CustomUserStore(IRepository repo)
{
_repo = repo;
}
However, creating a "new CustomUserStore()" in the Startup.Auth.cs is an anti-pattern and cannot resolve the dependency. What is the right way to resolve this dependency when creating a UserManager? I'd like to either not have to "new up" a UserManagerFactory, or somehow resolve the dependency inline:
new UserManager<CustomUserModel>(container.Resolve<IRepository>());
Then the question becomes, how to get the single IOC container instance...
Create an static field in your global.asax or owin startup class to hold a reference for ioc container, in your container bootstrap setup set the static field value.
after that you can access the ioc container from somewhere, in your Startup.Auth.cs class you can read the ioc static field container and initialize inline UserStore.
Owin Class:
public class Startup {
public static IUnityContainer Container; //Static field for hold ioc container reference.
public void Configuration(IAppBuilder app)
{
//Code Omitted for brevity
Container = YourIoCContainerReference:
}
}
}
So you can do an inline initialization:
var repository = Startup.Container.Resolve<IRepository>();
and get your dependency.
Related
I am using ASP.NET Core. How can I use session variables in a static method?
In ASP.NET, this looked like this:
protected static string AssignSession()
{
return HttpContext.Current.Session["UserName"].ToString();
}
protected void Page_Load(object sender, EventArgs e)
{
Session["UserName"] = "super user";
}
When I try that in ASP.NET Core, I get the following error:
An object reference is required for the non-static field, method,
or property 'ControllerBase.HttpContext'.
The answer is generally: You don’t.
In ASP.NET Core, you pretty much avoid static code. Instead, ASP.NET Core uses dependency injection to make services available as dependencies and to control their lifetime.
A static utility class in ASP.NET would probably translate to a singleton service in ASP.NET Core. Using that is very straightforward; you start by creating a non-static service that does whatever you want to do. Since this is using dependency injection, you can just depend on other services as well:
public class MyService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void SetSomeSessionValue(string value)
{
var httpContext = _httpContextAccessor.HttpContext;
httpContext.Session["example"] = value;
}
}
You can do whatever you want there. The IHttpContextAccessor is used to retrieve the current HttpContext.
Then, you need to register your service with the dependency injection container. You do that in the ConfigureServices method in your Startup.cs:
services.AddSingleton<MyService>();
// we also add the HttpContextAccessor, in case it wasn’t already (implicitly) registered before
services.AddHttpContextAccessor();
And now, you can depend on this MyService within controllers or other services by simply adding it as a constructor argument:
public class HomeController
{
private readonly MyService _myService;
public HomeController(MyService myService)
{
_myService = myService;
}
public IActionResult Index()
{
_myService.SetSomeSessionValue("test");
return View();
}
}
Now, you have a non-static service that has clear dependencies and which you can test properly.
That all being said, many constructs already have access to the current HttpContext and as such to the session. For example, in controllers, Razor pages, or even Razor views, you can just access the HttpContext directly as it is an instance variable.
So if you are not building some reusable utility code, you don’t actually need to create a service for this. You could just for example create a (non-static) utility method within your controller that then accesses the HttpContext directly.
The UnityMvcActivator is called right out of the gate when starting my MVC application, and it instantiates, configures, and sets the container to the DependencyResolver:
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));
Which immediately registers all the types via:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterMvcComponents();
}
...but I'm trying to register types that aren't created until a little further down the road:
public static IUnityContainer RegisterMvcComponents(this IUnityContainer container)
{
var lifetimeManager = new HierarchicalLifetimeManager();
container.RegisterInstance<HttpSessionStateBase>(
new HttpSessionStateWrapper(HttpContext.Current.Session), lifetimeManager);
container.RegisterInstance<HttpContextBase>(
new HttpContextWrapper(HttpContext.Current), lifetimeManager);
container.RegisterInstance<HttpServerUtilityBase>(
new HttpServerUtilityWrapper(HttpContext.Current.Server), lifetimeManager);
container.RegisterInstance(HttpContext.Current.User.Identity, lifetimeManager);
return container;
}
I can't get the container back from the DependencyResolver when I finally make it to my OWIN Startup class - which is where all the other initialization is taking place - so how can I register these types?
EDIT:
Thinking I was clever, I tried adding some post-start action to the activator by adding this assembly directive and moving my configuration method call to the newly created method:
[assembly: WebActivatorEx.PostApplicationStartMethod(
typeof(CCCS.Admin.Web.Ui.UnityMvcActivator),
nameof(CCCS.Admin.Web.Ui.UnityMvcActivator.PostStart))]
public static void PostStart() => UnityConfig.Container.RegisterMvcComponents();
... and that got me halfway, but the User and Session still aren't available.
This is more of an XY problem related to your design, as all the HttpContext related members wont be available at startup.
You would better off creating abstractions to defer the access to those implementation concerns.
public interface IHttpContextAccessor {
HttpContextBase HttpContext { get; }
}
public class HttpContextProvider : IHttpContextAccessor {
public virtual HttpContextBase HttpContext {
get {
return new HttpContextWrapper(HttpContext.Current);
}
}
}
Now all those registrations can be replaced with the one abstraction which would provide access to all the other related types.
public static IUnityContainer RegisterMvcComponents(this IUnityContainer container) {
var lifetimeManager = new HierarchicalLifetimeManager();
container.RegisterType<IHttpContextAccessor, HttpContextProvider>(lifetimeManager);
return container;
}
Note that the container should ideally only be accessed in the composition root of the application and not passed around as a dependency. That is seen as a code smell and an indicator that the design should be reviewed and refactored if possible.
When access is needed to HttpContext related members it is now a matter of injecting the accessor
private readonly IHttpContextAccessor accessor;
public MyDependent(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public void SomeMethodAccessedInAnAction() {
var context = access.HttpContext; // HttpContextBase
var session = context.Session; // HttpSessionStateBase
var server = context.Server; // HttpServerUtilityBase
var user = context.User; // IPrincipal
//...
}
I've been looking through examples and documentation for Autofac, and can't see to get this working.
We have a helper class, Core.Helpers.Tokens with a property set up like this:
namespace Core.Helpers
{
public static class Tokens
{
private static IConfigurationManager ConfigurationManager;
public static string GetToken()
{
var sessionTokenName = ConfigurationManager.GetAppSetting("SessionTokenName");
return (string) HttpContext.Current.Session[sessionTokenName];
}
}
}
The configuration is designed like this:
namespace Core.Config
{
public interface IConfigurationManager
{
//...
}
public class WebConfigConfigurationManager : IConfigurationManager
{
//...
}
}
In our MVC Web app (which references and uses Core.Helpers, Startup.cs I'm trying to register IConfigurationManager for property injection.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterFilterProvider();
builder.RegisterType<WebConfigConfigurationManager>().As<IConfigurationManager>().PropertiesAutowired();
RegisterTypes(builder);
// BUILD THE CONTAINER
var container = builder.Build();
var webConfig = container.Resolve<IConfigurationManager>();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// REGISTER WITH OWIN
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
}
When the web code calls GetToken, ConfigurationManager is null. What am I missing?
One error is that your Tokens.ConfigurationManager is a private field, whereas property injection works with public properties.
Also, it must be an instance property, not static. So in the end it should be
public IConfigurationManager ConfigurationManager{get;set;}
But that would mean you'd also have to inject a Tokens instance, and that would make no longer a static helper class, you'll have to do some redesign, there are some options:
If you have an IConfigurationManager instance everywhere you expect to call GetTokens from you can pass that in as an input parameter to GetTokens()
You promote this static helper to a dependency (e.g. ITokenService ? ) that will be injected to everywhere it's needed. Instead of making it static,
you can use Autofac lifetime management to make it a singleton. (Probably the best solution)
The worst solution, but the smallest change, one that works without having to give up this being a static helper class, is to make the property use the DependencyResolver instead of injection, something like:
private static IConfigurationManager ConfigurationManager{ get { return DependencyResolver.Current.GetService();} }
You are resolving IConfigurationManager, You should be resolving WebConfigConfigurationManager.
If you have 5 classes that utilize IConfigurationmanager, resolving the interface does not tell autoface which concrete class you are wanting to utilize which has this interface.
Within my Web API I have linked Autofac as IoC container, and I do it like this:
Domain level
public class Autofac
{
protected ContainerBuilder Builder { get; set; }
public Autofac()
{
this.Builder = new ContainerBuilder();
}
public virtual IContainer Register()
{
// Register dependencies
SetUpRegistration(this.Builder);
// Build registration.
var container = this.Builder.Build();
// End
return container;
}
private static void SetUpRegistration(ContainerBuilder builder)
{
// === DATALAYER === //
// MyRepository
builder.RegisterType<MyRepository>()
.As<IMyRepository>()
.InstancePerLifetimeScope();
// === DOMAIN === //
// MyManager
builder.RegisterType<MyManager>()
.As<IMyManager>()
.InstancePerLifetimeScope();
}
}
Web API
public class Autofac : Domain.IoC.Autofac
{
public IContainer Register(HttpConfiguration config)
{
// Register your Web API controllers.
base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
// Complete registration and get container instance.
var container = base.Register();
// Set the dependency resolver to be Autofac.
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Done.
return container;
}
}
As you see it inherits from the base class from Domain and sets up Web API specific config.
Usage
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}
Which is at global.asax, as you know.
The question
This works fine for Web API, but I haven't got a clue what I need to do to register all this within a UnitTest project context.
The idea is that I would create a similar implementation to the Autofac class at Web API level, but than with mocks (completely ignoring the base class from Domain).
Any pointers?
Personally I never see the need (and I struggle to comprehend how viable or helpful it would be) to setup my IoC container directly within a unit test.
As a unit test is used to test a logical piece of code that can be quickly built, easily ran and doesn't require much (I'd advocate no) tear-down. It should not require all of your application to be be setup for the test to run.
Remember that your unit test is simply testing the flow of data through the system i.e that your DomainManager is actually going to call a IRepository when you expect that it should. Then you would have separate test classes for all your repositories to determine that they would correctly add to the database etc.
I'm not sure how you use the DBContext class but as an example of a wrapper this is what it would sort of look like.
interface IDBSetWrapper
{
object Add(object entity);
}
interface IDBContextWrapper
{
...
IDBSet Set(Type entityType);
...
}
class DBContextWrapper : IDBContextWrapper
{
private readonly DBContext context;
public DBContextWrapper()
{
context = new DBContext();
}
...
public IDBSet Set(Type entityType)
{
var dbSet = context.Set(entityType);
return new DBSetWrapper(dbSet);
}
...
}
It's not much but I hope that it demonstrates what I mean about a thin wrapper. Basically the wrapper is the DBContext and will contain an instance of it within the class, the actual DBContext will be called when you request the wrapper to do anything.
I have shown what would happen when returning another object (in this case a DBSet), this will also be wrapped in a separate object with an interface. This is so that you can mock the returns from this class easily.
You can add this new wrapper into your IoC a little better now as it provides an interface.
One thing to note is that you won't be able to and probably wouldn't wish to test the wrapper class, there would be very little point as I see it. But previously I've seen colleagues do an integration test on these sort of classes.
I have configured Unity in my ASP.NET application and the configuration is loaded when the first request is received in Application_BeginRequest. then the Unity container is stored in the Global.ascx as a property so that my other class can access it:
public static IUnityContainer ContainerHolder { get; set; }
IUnityContainer IContainerAccessor.Container
{
get { return ContainerHolder; }
}
ContainerHolder, holds the container instance across application and Container property allows access to this property in each session.
Then I have a UnityLocator class which enables me access this property across the application:
public static class UnityLocator
{
private static IUnityContainer Container
{
get
{
return ((IContainerAccessor)HttpContext.Current.ApplicationInstance).Container;
}
}
}
Everything works fine!
I have also a method to access the instance from Unity:
UnityLocator.GetInstance<IThemeManager>();
protected Repository(ICustomCacheManager customCacheManager)
{
this.Cache = customCacheManager;
}
protected Repository()
: this(UnityLocator.GetInstance<ICustomCacheManager>())
{
}
this has been used in my app so that I can retrieve an existing instance from Unity so that I can inject it to other classes. For example my view (asp.net page) injects this to its Presenter class as a dependency.
Now, I'd like to configure my Unit tests to run.
How could I do that?! global.ascx doesn't exist there obviously so I thought I should create a BaseTest class and let all my tests inherit it. then at the constructor of this BaseTest class, I build up my instances. Is it the right way to do it?
How to configure unit tests with Unity now?
Thanks
UPDATE:
UnityLocator.GetInstance added.
You shouldn't worry about accessing your IoC container. That is a violation of Unit Tests.
Unit tests you should not worry about any concrete implementation or dependency (other than the class under test).
To me, having your IoC globally available is a bad design choice. You should have your dependencies injected via properties or constructors.
Probably using the global application class for storing the service locator was not a good idea. Why don't you use the built-in ServiceLocator class? It is available from anywhere in the code and doesn't depend on global application / HttpContext.
Whether or not using the container in unit tests is another story. Personally I am not against it as long as you put stub implementations of your services into the container.
Edit: the way to configure your container using ServiceLocator:
private void ConfigureUnity()
{
UnityServiceLocator locator = new UnityServiceLocator( ConfigureUnityContainer() );
ServiceLocator.SetLocatorProvider( () => locator );
}
private IUnityContainer ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
// this loads container's configuration, comment or uncomment
container.LoadConfiguration();
return container;
}
You can then access the container from within the locator like:
var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
In your page, try doing things like this:
public class DepartmentReportPage : Page
{
private readonly DepartmentReportPresenter _presenter;
public DepartmentReportPage()
{
this._presenter =
UnityLocator.GetInstance<DepartmentReportPresenter>();
this._presenter.View = this;
}
}