This is similar to Dependency Injection with Custom Membership Provider, but the responses there don't solve the issue for me.
I have a custom membership provider which has a dependency to a repository class. ASP.NET will always instantiate this using the parameter-less constructor, so to resolve the dependency to the repository I have a kind of service locator method ... my ctor looks like this:
public CustomMembershipProvider()
{
_userRepository = AppStart_NinjectMVC3.Resolve<IUserRepository>();
}
And that Resolve method looks like this..
public static T Resolve<T>() where T : class
{
return _kernel.Get<T>();
}
This works fine when I run the web app, because _kernel is correctly setup. However, I need to test the methods on my membership provider.. So when my test code tries to invoke the methods on membership provider it will instantiate a new membership provider class with the paramter-less ctor, which errors because _kernel is not setup.
What I want to do is somehow inject my FakeUserRepository class instead, but how can I achieve that?
I think I have a work round for this...
I've added a ctor to the membership provider which accepts a repository instance, and then I've manually instantiated my membership provider in my test class like this:
var prov = new CableSenseMembershipProvider(new FakeUserRepository());
var config = new NameValueCollection();
config.Add("applicationName", "ddd");
config.Add("name", "CustomMembershipProvider");
config.Add("requiresQuestionAndAnswer", "false");
config.Add("requiresUniqueEmail", "false");
prov.Initialize(config["name"], config);
Once I've dont this I can then invoke that instance and not worry about the parameter-less ctor being called.
As an aside, you still need to add the membership section to your test project app.config or it wont work - which is somewhat confusing!
Why not extract everything out of your custom membership provider into an implementation class and instantiate that class via the service locator then pass all your calls through to that? The implementation class can then be unit-testable and the ugly Membership stuff can be 'right by inspection'.
Related
I am using .Net Core, using the built-in dependency injection. In my login screen, I need the user to also choose a departmental database - we have different databases with the same structure to use the same application with different data. However, I can't figure out how to add/modify the dbContext that late. Startup.cs has the DI, but I don't know which connection string to read from the config until the user has chosen the department. It is a small database, and the company is not concerned about the management of the duplicate databases.
How can I add the service late
services.AddDbContext<my_accountingContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("CorrectDepartmentConfig")));
when I actually know what CorrectDepartmentConfig is?
Or, if that can't be done, how can I do a smelly change of the my_accountingContext after Startup.cs?
You can use an implementation factory overload of IServiceCollection in ConfigureServices method form Startup class:
//First register a custom made db context provider
services.AddTransient<ApplicationDbContextFactory>();
//Then use implementation factory to get the one you need
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext());
The implementation of CreateApplicationDbContext depends on your specific needs, but a base implementation should look like the following:
public ApplicationDbContext CreateApplicationDbContext(){
//TODO Something clever to create correct ApplicationDbContext with ConnectionString you need.
}
After this implementation, you can inject the correct ApplicationDbContext in your controller, action...
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult([FromServices] ApplicationDbContext dbContext){
}
You can always set the connection string from inside the protected OnConfiguring method. You can get access to the IConfiguration instance from there (the DbContext class has a service locator, Instance property), retrieve the connection string, and then call UseMySql extension method with the appropriate connection.
Something like this:
protected virtual void OnConfiguring(DbContextOptionsBuilder builder)
{
var configuration = (this as IInfrastructure<IServiceProvider>).GetService<IConfiguration>();
var connectionString = configuration.GetConnectionString("<name>");
builder.UseMySql(connectionString);
base.OnConfiguring(builder);
}
For the strongly-typed version of GetService do not forget to reference namespace Microsoft.Extensions.DependencyInjection.
I have problem with Simple Injector in my Web Api project. I user default AccountController generated by VS.
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
In my configuration file I register:
var container = new Container();
// This is an extension method from the integration package.
container.RegisterWebApiFilterProvider(config);
container.RegisterWebApiControllers(config);
container.Register<IInitializeService, InitializeService>();
container.Register<IFolderRepository, FolderRepository>();
container.Register<IUserRepository, UserRepository>();
container.Register<ILogger, Logger>();
//Authentication Wrap
container.Register<IUserStore<User, Guid>, ApplicationUserStore>();
container.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>();
container.Register<ISecureDataFormat<AuthenticationTicket>,
SecureDataFormat<AuthenticationTicket>>();
container.Register<IDataProtector>(
() => new DpapiDataProtectionProvider().Create("ASP.NET Identity"));
container.Verify();
// 4. Register the container as MVC3 IDependencyResolver.
DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
I though that Simple Injector will be smart enough to handle all build in dependences responsible for default identity and i wont need to register them manually, but I'm getting exceptions if I wont register them.
Still getting an exception:
The constructor of type SecureDataFormat contains the parameter of type ITextEncoder with name 'encoder' that is not registered. Please ensure ITextEncoder is registered in the container, or change the constructor of SecureDataFormat.
Is there any way to handle that automatically?
I implemented a Web Api and I wrote this code.
This works for me
container.RegisterWebApiRequest<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>();
container.RegisterWebApiRequest<ITextEncoder, Base64UrlTextEncoder>();
container.RegisterWebApiRequest<IDataSerializer<AuthenticationTicket>, TicketSerializer>();
container.RegisterWebApiRequest<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"));
How is the container supposed to know which implementation of ITextEncoder you want to use in the constructor of SecureDataFormat?
You have to tell it which one to use. I think the rules basically go something like this:
if an interface is required (by a constructor) then it needs to know which implementation to use. If a concrete class is required it will automatically build an instance of that class (assuming it can resolve all the types that class needs).
As your SecureDataForms needs an interface you have to register one, otherwise its only option would be to 'guess' at which implementation you want and this could then go wrong silently if more than one implementation existed.
I just came up against this issue. I'm using ninject but you'll get the idea. Here is my binding:
kernel.Bind<ITextEncoder>().To<Base64UrlTextEncoder>();
According to the source, the only thing I ever see being used to new up an instance of SecureDataFormat is Base64UrlTextEncoder. So it seemed like a safe bet to use, but it's certainly not clear to me at first glance how to appropriately use this constructor overload.
I created a custom membership provider
public class MyMembership : MembershipProvider
{
private IRepository<User> Repo;
}
I figured out how to inject MyMembership using autofac:
builder.Register(c => Membership.Provider);
However, this doesn't work if I have a constructor that takes an IRepository (it only calls the parameterless constructor.) I tried doing changing from a private field to a public property and calling:
builder.Register(c => Membership.Provider).AutoWireProperties();
The problem is, MembershipProvider doesn't have a Repo property, it's only my class.
For the time being, I've not injected anything, and just created an empty constructor where I simply create a new instance of my Repo. But it makes it harder for testing.
So, is there any way that I can use AutoFac to inject my MyMembership, and have it use the injected Repo?
No. It's not possible.
The DependencyResolver is not used for the providers (roles/membership) etc.
I've made a membership provider which uses DependencyResolver internally. All you need to do is to implement IAccountRepository and register it in your container.
http://blog.gauffin.org/2011/09/a-more-structured-membershipprovider/
It's an old post, so i'll just post here my solution as I struggle with it a bit myself.
public class AutofacBootstrapperImp : AutofacBootstrapper
In your autofac builder setup include register for your interface
builder.RegisterType<YourRepo>().As<IRepository<User>>().SingleInstance();
public class CustomMembershipProvider : MembershipProvider
Overload init method and use autofac scope to resolve your type
private IRepository<User> Repo;
public override void Initialize(string name, NameValueCollection config)
{
// use autoface scope to resole service
using (var scope = AutofacBootstrapper.Container.BeginLifetimeScope())
{
Repo = scope.Resolve<IRepository<User>>();
}
if (config == null)
throw new ArgumentNullException("config");
// Initialize the abstract base class.
base.Initialize(name, config);
}
You should also register the IRepository<User>. But to do that you need to access your custom provider by its concrete type.
So something like this:
builder.Register( c => Membership.Provider );
builder.Register( c => ((MyMembership) Membership.Provider).Repo );
That could be made nicer (avoiding casts by registering your implementation) as follows, but not sure on how it then fits in with ASP.NET and its management of provider lifecycles:
builder.RegisterType<MyMembership>();
builder.Register( c => c.Resolve<MyMembership>() ).As<MembershipProvider>();
builder.Register( c => c.Resolve<MyMembership>().Repo );
EDIT:
but from a design standpoint, it looks like your MyMembership class has a dependency on the IRepository<User>, therefor something like this is probably best:
builder.RegisterType<YourRepositoryImplementation>().As<IRepository<User>>();
builder.RegisterType<MyMembership>();
builder.Register( c => c.Resolve<MyMembership>() ).As<MembershipProvider>();
That way the IRepository will be injected into the MyMembership as needed, but also be available for direct consumption by other components, and they all have the lifetime management handled by Autofac.
This scenario is entirely possible. In my Bootstrapper.cs class, I just inject the properties into the providers as such:
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
var lifetimeScope = DependencyResolver.Current.GetService<ILifetimeScope>();
lifetimeScope.InjectProperties(Membership.Provider);
lifetimeScope.InjectProperties(Roles.Provider);
I also have the following:
builder.Register(context => Membership.Provider).ExternallyOwned();
builder.Register(context => Roles.Provider).ExternallyOwned();
however I'm not entirely sure if it's even necessary. I believe the Registers are there in case you want to inject the provider into a type.
I am using the MVC AccountController that implements the ASP.NET Membership Provder.
I have a repository with all my database access in which I have added a Countries property that returns a list of countries. I want to add a country dropdown to the Register page so I need to be able to get this data from my repository and pass it to the View.
I have been using contructor injection in my other controllers but I dont know how to apply this to the existing AccountController.
// This constructor is used by the MVC framework to instantiate the controller using
// the default forms authentication and membership providers.
public AccountController()
: this(null, null)
{
}
// This constructor is not used by the MVC framework but is instead provided for ease
// of unit testing this type. See the comments at the end of this file for more
// information.
public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}
Can I change the existing AccountController constructor to access my repository?
Register the services in your IoC engine and then remove the default constructor.
If you have already registered your repository with ninject, you should be able to just add a third parameter to the constructor of the controller. I saw your earlier comment about ninject, but I'm not using NinjectModule. If you're using MVC 3, would suggest that you take a look at nuget (http://nuget.codeplex.com) and download the Ninject.MVC3 packge which adds a AppStartNinjectMvc3 class to your project where you can register services with the kernel.Bind methods:
kernel.Bind<IThingRepository>().To<SqlThingRepository>();
Hope this helps.
If your using MVC2 you should take a look at http://mvcstarter.codeplex.com/ it's also using Ninject. Like #Johan said you simply have to put the parameter and bind it in the global.asax.cs.
Hope it helps!
I'm using Ninject for dependency injection in my application. Here's an example of one of my controllers:
public class DepartmentsController : Controller
{
private IDepartmentsRepository departmentsRepository;
public DepartmentsController(IDepartmentsRepository departmentsRepository)
{
this.departmentsRepository = departmentsRepository;
}
...
}
I'm also trying to follow this tutorial to use ELMAH in an MVC application. The idea is to use a custom controller factory to handle errors from every controller. You then set the controller factory to the custom one in the global.asax.cs file.
The only problem is that it is expecting a parameterless constructor in each controller, which I can't do (that I know of) with dependency injection with Ninject.
How can I get around this?
If you are using MVC3, you should ignore the part about the Controller Factory and use Global Filters to apply the custom attribute to each controller.
If you aren't using v3 yet and you can modify their code to inherit from the Ninject Controller factory.