6
votes

I have a custom MembershipProvider class that inherits from MembershipProvider that takes two parameters:

public class CustomMembershipProvider : MembershipProvider
{
    private readonly ISecurityRepository _securityRepository;
    private readonly IUserRepository _userRepository;

    public CustomMembershipProvider(ISecurityRepository securityRepository, IUserRepository userRepository)
    {
        ...
    }

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        ...
    }

    ... etc
}

The config file for this looks similar to this:

<membership defaultProvider="CustomMembershipProvider">
  <providers>
    <clear />
    <add name="CustomMembershipProvider" type="Library.Membership.CustomMembershipProvider" />
  </providers>
</membership>

This works fine mostly throughout my web application for logging in and logging out. I am using Unity for DI and have the necessary classes setup in my Boostrapper.cs class.

However I recently ran into an issue when I wanted to create a custom User class and called the Membership.GetUser method. I get the following exception when I do:

{"No parameterless constructor defined for this object. (C:\\*app path*\\web.config line 43)"}

Line 43 in my config file points to the custom membership provider that I posted above. I think that elsewhere the app is using Unity to resolve those parameters but when using the Membership class it doesn't.

Is there any way I can tell the application how to resolve those dependencies or if not is there a way of adding those dependencies to my membership provider without using the concrete implementation?

EDIT 1:

Here is the custom User class:

public class User : MembershipUser
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public DateTime LastLoggedOnDate { get; set; }
    ...         
}

EDIT 2:

In my custom membership provider class this is what the GetUser method looks like:

public override MembershipUser GetUser(string username, bool userIsOnline)
{
    return _userRepository.GetUser(username);
}
3
You haven't posted your custom User Class code? This is where the error is right?codingbadger
No, the error is that it can't resolve the custom membership provider class. The User class is a very simple user template with properties (name, email, userid etc)Serberuss
What code is in your GetUser method? This is where the error is being thrown correct? You state that in your question called the Membership.GetUser method. I get the following exception when I do:codingbadger
It never actually gets to that code due to the fact it can't resolve the class. But I will update the post with that code inSerberuss
This is painful.. I did it this week. I ended up passing the DI container into the custom MembershipProvider. Saved so much hassle.. but its dirty.Simon Whitehead

3 Answers

6
votes

The problem is that you can't inject into the Membership provider via constructor. Refer to this question

I implemented a Custom Membership Provider using Ninject and I used the ServiceLocator to get the instance of the service.

public class AccountMembershipProvider : MembershipProvider
{
    private readonly IUsers _users;

    public AccountMembershipProvider()
    {
        _users = ServiceLocator.Current.GetInstance<IUsers>();
    }

    public override bool ValidateUser(string username, string password)
    {
        return _users.IsValidLogin(username, password);
    }
...
}

In your case, you need to get the IUserRepository and ISecurityRepository.

When you wire your Interfaces/services

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IUsers>().To<UsersService>();
        kernel.Bind<IRoles>().To<RolesService>();

        kernel.Bind<MembershipProvider>().To<AccountMembershipProvider>().InRequestScope();
        kernel.Bind<RoleProvider>().To<AccountRoleProvider>().InRequestScope();

    }

You can check a complete example of it working (using Ninject but you can adapt it to Unity) here: https://github.com/lopezbertoni/SampleApp

Hope this helps,

4
votes

I also use Unity and implemented a custom membership provider, but used a slightly different approach. Check the code sample:

/// <summary>
/// Defines the custom membership provider class.
/// </summary>
public class SsoMembershipProvider : MembershipProvider
{


    private IApplicationsRepository _appsRepo;
    private IUsersRepository _usersRepo;
    private IMembershipsRepository _membershipsRepo;



    /// <summary>
    /// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class
    /// using injectionConstructor attribute in order to get the repositories needed.
    /// </summary>
    /// <param name="appsRepo">The apps repo.</param>
    /// <param name="usersRepo">The users repo.</param>
    /// <param name="membershipsRepo">The memberships repo.</param>
    [InjectionConstructor]
    public SsoMembershipProvider(IApplicationsRepository appsRepo, IUsersRepository usersRepo, IMembershipsRepository membershipsRepo)
    {
        _appsRepo = appsRepo;
        _usersRepo = usersRepo;
        _membershipsRepo = membershipsRepo;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class.
    /// which calls the internal contructor.
    /// </summary>
    /// <remarks>This is happening due to the fact that membership provider needs a
    /// parametless constructor to be initialized</remarks>
    public SsoMembershipProvider()
        : this(DependencyResolver.Current.GetService<IApplicationsRepository>(),
        DependencyResolver.Current.GetService<IUsersRepository>(),
        DependencyResolver.Current.GetService<IMembershipsRepository>())
    { }


}
2
votes

This worked for me

public ICustomerRepository CustomerRepository { 
 get { return DependencyResolver.Current.GetService<ICustomerRepository>(); } 
}

and then use

public override bool ValidateUser(string username, string password)
{
    var abc = CustomerRepository.ValidateCustomer(username, password);
}