I'm trying to create my custom identity implementation to use int values for my primary keys insted of default strings in ASP.NET 5 RC1 with EF7.
I have my custom User and Role classes as well as UserStore, UserManager, RoleStore, RoleManager. For now I have issues with UserStore being Unable to resolve service for type 'DAL.BgCarsDbContext' while attempting to activate 'DAL.Identity.UserStore'.
My code for User and UserStore classes:
public class User : IdentityUser<int>
{
}
public class UserStore : UserStore<User, Role, BgCarsDbContext, int>
{
public UserStore(BgCarsDbContext context, IdentityErrorDescriber describer = null)
: base(context, describer)
{
}
}
I know that's happening because of the default UserStore behaviour.
However, I would like to use the default implementation that UserStore provides because it implements a lot of interfaces
IUserLoginStore<TUser>, IUserStore<TUser>, IDisposable, IUserRoleStore<TUser>, IUserClaimStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserEmailStore<TUser>, IUserLockoutStore<TUser>, IUserPhoneNumberStore<TUser>, IQueryableUserStore<TUser>, IUserTwoFactorStore<TUser>
and I'd like to avoid having to write the implementation of every method from the interfaces. So is there any way to avoid that?
Related
I have an ASP.NET Core (v. 2.2) application that uses IdentityDbContext<User, Role, string>.
I'm adding Abp Framework to the application, I want to use AbpDbContext.
How to combine the functionality of two contexts?
You need to create a module class like below
public class BlogModule : AbpModule
{
}
and add your other module dependency in the attribute. Something like this
[DependsOn(typeof(BlogModule))]
public class WebAppModule : AbpModule
{
}
And read https://docs.abp.io/en/abp/latest/Module-Development-Basics
I am working on a legacy MVC application which initially had forms authentication via a custom class and implementation. I have modified it and utilised Asp.Net Identity which is working as expected.
My requirement now is that this MVC application no longer should have direct access to the database. So I have removed the connection string from the web.config and have been looking at making all database calls via calls to my web service (Asp.Net Web Api).
I have custom classes for:
UserStore
RoleStore
etc
Please note I have a custom user class as it is a custom user table.
Questions
1) Is the correct way to achieve my goal? It's likely I will be overriding many methods which had previously used the IdentityDBContext such as:
public override Task<CustomUser> FindByIdAsync(int usrID)
public override Task<Customer> FindByNameAsync(string userName)
2) I am finding that
FindByNameAsync()
is working as expected and the user is being passed to the method however FindByIdAsync() is passing userid as 0. Why would FindByIdAsync() not be passing my actual userid?
To call FindByIdAsync() I am implementing a UserStore:
public class CustomUserStore : UserStore
<
CustomUser,
CustomRole,
int,
CustomUserLogin,
CustomUserRole,
CustomUserClaim
>
{
and then overriding FindByIdAsync():
public override Task<CustomUser> FindByIdAsync(int usrID)
{
var response = client.GetAsync("api/user/" + usrID).Result.Content;
return response.ReadAsAsync<CustomUser>(
new List<MediaTypeFormatter> {
new XmlMediaTypeFormatter(),
new JsonMediaTypeFormatter()
});
//return base.FindByIdAsync(userId);
}
The issue is that usrID is 0.
I now have this working, so my mvc application is using AspNet.Identity but for all the database calls it is calling my api. The solution was indeed to implement IUserStore (and other interfaces as required) and there are a lot of answers and guides on the Internet for this. A good starting point is:
https://learn.microsoft.com/en-us/aspnet/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity
I've read the Armen Shimoon's article ASP.NET Core: Factory Pattern Dependency Injection and I've decided to solve my ASP.NET Core DI problem using the technique suggested by him.
I’ve got a generic interface:
public interface IItemRepository<out T> where T : BaseItem
and its generic implementation:
public class ItemRepository<T> : IItemRepository<T> where T : BaseItem
I register it with:
services.AddSingleton(typeof(IItemRepository<>), typeof(ItemRepository<>));
But for Currency I’ve got a specific implementation:
public class CurrencyRepository : ItemRepository<Currency>
(Curency is of the BaseItem type.) What I want is to register
CurrencyRepository
for
IItemRepository<Currency>
and
ItemRepository<T>
for all other items that implement BaseItem. I created a factory class to accomplish this:
public class ItemRepositoryFactory : IServiceFactory> where T : BaseItem
{
private readonly ApplicationDbContext _context;
public ItemRepositoryFactory(ApplicationDbContext context)
{
_context = context;
}
public ItemRepository Build()
{
if (typeof(T) == typeof(Currency))
return new CurrencyRepository(_context) as ItemRepository;
return new ItemRepository(_context);
}
}
but I don’t know how to register it with IServiceCollection. Or maybe I’m not on the right way at all?
You can't register it explicitly. ASP.NET Core DI is meant to be simple and offer out of box DI/IoC experience and easy for other DI/IoC containers to plugin.
So the built-in IoC doesn't offer Auto-Registrations, Assembly scanning, Decorators or registration of all interfaces/sub types of the class.
For concrete implementations, there is a "workaround"
services.AddScoped<IMyService,MyService>();
services.AddScoped<MyService>(provider => provider.GetService<IMyService>());
Then both MyService and IMyService inside a constructor would receive the same instance of the class.
But this doesn't work with open generics and is a manuell process. For automatic discovery or registration you need a 3rd party IoC container, such as Autofac, StructureMap etc.
But in your concrete sample it should be enough to register IItemRepository<Currency> inside your ConfigureServices method
services.AddScoped<IItemRepository<Currency>,CurrencyRepository>();
But actually the open generic should cover this case already, if you inject IItemRepository<Currency> into your services.
I have implemented a custom RoleStore and a custom UserStore for my project that is using ASP.NET 5, MVC 6, EF 7, and Identity 3. However - I can't quite figure out how to configure identity to use my custom RoleStore and custom UserStore instead of the usual offering. How can I reconfigure the system to use my custom classes?
PS: I also have custom User and Role class.
Solution
Here's what I ended up doing. First, I uninstalled the 'Identity Entity Framework' package from my project. This sent a few things missing, so I re-implemented them (read: copied them from here), and put them in a 'Standard' namespace to indicate they hadn't been customised. I now have a 'Security' namespace that contains the following:
Standard
IdentityRole.cs
IdentityRoleClaim.cs
IdentityUser.cs
IdentityUserClaim.cs
IdentityUserLogin.cs
IdentityUserRole.cs
BuilderExtensions.cs
IdentityDbContext.cs
Resources.resx
Role.cs
RoleStore.cs
User.cs
UserStore.cs
The items shown in bold contain project specific functionality.
The code that allows me to use the custom stores is in the 'BuilderExtensions' file, which contains the following class:
public static class BuilderExtensions
{
public static IdentityBuilder AddCustomStores<TContext, TKey>(this IdentityBuilder builder)
where TContext : DbContext
where TKey : IEquatable<TKey>
{
builder.Services.TryAdd(GetDefaultServices(builder.UserType, builder.RoleType, typeof(TContext), typeof(TKey)));
return builder;
}
private static IServiceCollection GetDefaultServices(Type userType, Type roleType, Type contextType, Type keyType)
{
var userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
var roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
var services = new ServiceCollection();
services.AddScoped(
typeof(IUserStore<>).MakeGenericType(userType),
userStoreType);
services.AddScoped(
typeof(IRoleStore<>).MakeGenericType(roleType),
roleStoreType);
return services;
}
}
This then allows me to write the following in my Startup.cs file:
services.AddIdentity<User, Role>()
.AddCustomStores<PrimaryContext, string>()
.AddDefaultTokenProviders();
And the custom store will be used. Note that PrimaryContext is the name of my whole-project DbContext. it inherits from IdentityDbContext.
Discussion
I could have probably kept the 'Identity Entity Framework' package and saved myself duplicating the contents of the 'Standard' namespace, but I chose not to so that I can keep my identifiers short and unambiguous.
Check out this section
Reconfigure application to use new storage provider in Overview of Custom Storage Providers for ASP.NET Identity
Specifically "If the default storage provider was included in your project, you must remove the default provider and replace it with your provider."
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new YourNewUserStore(context.Get<ExampleStorageContext>()));
...
}
I have an MVC3 app using Autofac and a custom membership provider.
If I try and inject the provider using the ctor, I get an error: 'No parameterless constructor defined for this object.'
public class MyMemberShipProvider : MembershipProvider
{
IUserRepository userRepository;
public MyMemberShipProvider(IUserRepository userRepository)
{
this.userRepository = userRepository;
}
You can not inject into the in-built providers (Membership / Roles).
You can use the MVC 3 DependencyResolver with Autofac.
A quick example...
public override bool ValidateUser(string username, string password)
{
var userRepo = DependencyResolver.Current.GetService<IUserRepository>();
return userRepo.ValidateUser(username, password);
}
Avoid resolving in application code (validate user, etc) as this is the service locator anti-pattern. You want to resolve only in your 'glue' code/low level code.
This below is for windsor, but the implementation can be easily adjusted. Here is one outlined for castle windsor, but the implementation should be similar. Its a bit cleaner as this resolves in the call to GetProvider - which is the 'glue' code here, thus avoiding the service locator anti-pattern usage in the actual membership functions (such as ValidateUser)
http://bugsquash.blogspot.com/2010/11/windsor-managed-membershipproviders.html
That´s because you should inject your userRepository as well. Something like:
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UserRepository>().As<IUserReposotory>();
builder.RegisterType<MyMembershipProvider>();
}