Browse Source

Implemented role store.

pull/81/head
Halil İbrahim Kalkan 10 years ago
parent
commit
78abed7312
  1. 2
      src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs
  2. 2
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs
  3. 24
      src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AddAbpDbContextOptions.cs
  4. 1
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityRoleRepository.cs
  5. 6
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs
  6. 5
      src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs
  7. 28
      src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityRole.cs
  8. 2
      src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityRoleManager.cs
  9. 18
      src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUser.cs
  10. 333
      src/Volo.Abp.Identity/Volo/Abp/Identity/RoleStore.cs
  11. 6
      src/Volo.Abp.Identity/Volo/Abp/Identity/UserStore.cs
  12. 13
      test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/Initialize_Tests.cs

2
src/AbpDesk/AbpDesk.EntityFrameworkCore/AbpDesk/EntityFrameworkCore/AbpDeskEntityFrameworkCoreModule.cs

@ -11,7 +11,7 @@ namespace AbpDesk.EntityFrameworkCore
{
services.AddAbpDbContext<AbpDeskDbContext>(options =>
{
options.AddDefaultRepositories();
options.WithDefaultRepositories();
});
services.AddAssemblyOf<AbpDeskEntityFrameworkCoreModule>();

2
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs

@ -24,7 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection
var options = new AddAbpDbContextOptions();
optionsAction?.Invoke(options);
services.AddRepositories<TDbContext>(options.Repositories);
services.AddRepositories<TDbContext>(options.RepositoryOptions);
return services;
}

24
src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AddAbpDbContextOptions.cs

@ -8,11 +8,11 @@ namespace Microsoft.Extensions.DependencyInjection
{
public class AddAbpDbContextOptions
{
internal RepositoryRegistrationOptions Repositories { get; set; }
internal RepositoryRegistrationOptions RepositoryOptions { get; set; }
public AddAbpDbContextOptions()
{
Repositories = new RepositoryRegistrationOptions();
RepositoryOptions = new RepositoryRegistrationOptions();
}
/// <summary>
@ -22,18 +22,24 @@ namespace Microsoft.Extensions.DependencyInjection
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
public void AddDefaultRepositories(bool includeAllEntities = false)
public void WithDefaultRepositories(bool includeAllEntities = false)
{
Repositories.RegisterDefaultRepositories = true;
Repositories.IncludeAllEntitiesForDefaultRepositories = includeAllEntities;
RepositoryOptions.RegisterDefaultRepositories = true;
RepositoryOptions.IncludeAllEntitiesForDefaultRepositories = includeAllEntities;
}
public void AddCustomRepository<TEntity, TRepository>()
/// <summary>
/// Registers custom repository for a specific entity.
/// Custom repositories overrides default repositories.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TRepository">Repository type</typeparam>
public void WithCustomRepository<TEntity, TRepository>()
{
AddCustomRepository(typeof(TEntity), typeof(TRepository));
WithCustomRepository(typeof(TEntity), typeof(TRepository));
}
private void AddCustomRepository(Type entityType, Type repositoryType)
private void WithCustomRepository(Type entityType, Type repositoryType)
{
if (!ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IEntity<>)))
{
@ -45,7 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new AbpException($"Given repositoryType is not a repository: {entityType.AssemblyQualifiedName}. It must implement {typeof(IRepository<,>).AssemblyQualifiedName}.");
}
Repositories.CustomRepositories[entityType] = repositoryType;
RepositoryOptions.CustomRepositories[entityType] = repositoryType;
}
}
}

1
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityRoleRepository.cs

@ -7,7 +7,6 @@ using Volo.Abp.Identity.EntityFrameworkCore;
namespace Volo.Abp.Identity
{
//TODO: Register for all repositories!
public class EfCoreIdentityRoleRepository : EfCoreRepository<IdentityDbContext, IdentityRole>, IIdentityRoleRepository
{
public EfCoreIdentityRoleRepository(IDbContextProvider<IdentityDbContext> dbContextProvider)

6
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs

@ -10,12 +10,6 @@ using Volo.Abp.Identity.EntityFrameworkCore;
namespace Volo.Abp.Identity
{
//TODO: Register for;
//- IIdentityUserRepository OK, by convention
//- IRepository<IdentityUser> -> none!
//- IRepository<IdentityUser, string> -> OK, by AddDefaultEfCoreRepositories but note to EfCoreIdentityUserRepository!
//- IQueryableRepository<IdentityUser> -> none!
//- IQueryableRepository<IdentityUser, string> - OK, by AddDefaultEfCoreRepositories, but not to EfCoreIdentityUserRepository!
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser>, IIdentityUserRepository
{
public EfCoreIdentityUserRepository(IDbContextProvider<IdentityDbContext> dbContextProvider)

5
src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs

@ -11,8 +11,9 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
{
services.AddAbpDbContext<IdentityDbContext>(options =>
{
options.AddDefaultRepositories(true);
options.AddCustomRepository<IdentityUser, EfCoreIdentityUserRepository>();
options.WithDefaultRepositories(true);
options.WithCustomRepository<IdentityUser, EfCoreIdentityUserRepository>();
options.WithCustomRepository<IdentityRole, EfCoreIdentityRoleRepository>();
});
services.AddAssemblyOf<AbpIdentityEntityFrameworkCoreModule>();

28
src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityRole.cs

@ -4,8 +4,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Security.Claims;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Identity
{
@ -15,7 +17,7 @@ namespace Volo.Abp.Identity
/// <summary>
/// Represents a role in the identity system
/// </summary>
public class IdentityRole : AggregateRoot
public class IdentityRole : AggregateRoot, IHasConcurrencyStamp
{
/// <summary>
/// Gets or sets the name for this role.
@ -53,6 +55,23 @@ namespace Volo.Abp.Identity
Name = name;
}
public void AddClaim([NotNull] Claim claim)
{
Check.NotNull(claim, nameof(claim));
Claims.Add(new IdentityRoleClaim(Id, claim));
}
public void AddClaims([NotNull] IEnumerable<Claim> claims)
{
Check.NotNull(claims, nameof(claims));
foreach (var claim in claims)
{
AddClaim(claim);
}
}
/// <summary>
/// Returns the name of the role.
/// </summary>
@ -61,5 +80,12 @@ namespace Volo.Abp.Identity
{
return Name;
}
public void RemoveClaim([NotNull] Claim claim)
{
Check.NotNull(claim, nameof(claim));
Claims.RemoveAll(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value);
}
}
}

2
src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityRoleManager.cs

@ -9,7 +9,7 @@ namespace Volo.Abp.Identity
public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
{
public IdentityRoleManager(
IRoleStore<IdentityRole> store,
RoleStore store,
IEnumerable<IRoleValidator<IdentityRole>> roleValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,

18
src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUser.cs

@ -159,13 +159,20 @@ namespace Volo.Abp.Identity
return Roles.Any(r => r.RoleId == roleId);
}
public void AddClaim([NotNull] Claim claim)
{
Check.NotNull(claim, nameof(claim));
Claims.Add(new IdentityUserClaim(Id, claim));
}
public void AddClaims([NotNull] IEnumerable<Claim> claims)
{
Check.NotNull(claims, nameof(claims));
foreach (var claim in claims)
{
Claims.Add(new IdentityUserClaim(Id, claim));
AddClaim(claim);
}
}
@ -187,10 +194,17 @@ namespace Volo.Abp.Identity
foreach (var claim in claims)
{
Claims.RemoveAll(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type);
RemoveClaim(claim);
}
}
public void RemoveClaim([NotNull] Claim claim)
{
Check.NotNull(claim, nameof(claim));
Claims.RemoveAll(c => c.ClaimValue == claim.Value && c.ClaimType == claim.Type);
}
public void AddLogin([NotNull] UserLoginInfo login)
{
Check.NotNull(login, nameof(login));

333
src/Volo.Abp.Identity/Volo/Abp/Identity/RoleStore.cs

@ -0,0 +1,333 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Uow;
using Volo.DependencyInjection;
namespace Volo.Abp.Identity
{
/// <summary>
/// Creates a new instance of a persistence store for roles.
/// </summary>
public class RoleStore :
IRoleStore<IdentityRole>,
IRoleClaimStore<IdentityRole>,
ITransientDependency
{
private readonly IIdentityRoleRepository _roleRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
/// <summary>
/// Constructs a new instance of <see cref="RoleStore"/>.
/// </summary>
public RoleStore(
IIdentityRoleRepository roleRepository,
IUnitOfWorkManager unitOfWorkManager,
IdentityErrorDescriber describer = null)
{
_roleRepository = roleRepository;
_unitOfWorkManager = unitOfWorkManager;
ErrorDescriber = describer ?? new IdentityErrorDescriber();
}
private bool _disposed; //TODO: Remove!
/// <summary>
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
/// </summary>
/// <value>
/// True if changes should be automatically persisted, otherwise false.
/// </value>
public bool AutoSaveChanges { get; set; } = true;
/// <summary>Saves the current store.</summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
protected Task SaveChanges(CancellationToken cancellationToken)
{
if (!AutoSaveChanges || _unitOfWorkManager.Current == null)
{
return Task.CompletedTask;
}
return _unitOfWorkManager.Current.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Creates a new role in a store as an asynchronous operation.
/// </summary>
/// <param name="role">The role to create in the store.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
public virtual async Task<IdentityResult> CreateAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
await _roleRepository.InsertAsync(role);
await SaveChanges(cancellationToken);
return IdentityResult.Success;
}
/// <summary>
/// Updates a role in a store as an asynchronous operation.
/// </summary>
/// <param name="role">The role to update in the store.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
public virtual async Task<IdentityResult> UpdateAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
await _roleRepository.UpdateAsync(role);
try
{
await SaveChanges(cancellationToken);
}
catch (AbpDbConcurrencyException ex)
{
//TODO: Log...?
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
/// <summary>
/// Deletes a role from the store as an asynchronous operation.
/// </summary>
/// <param name="role">The role to delete from the store.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
public virtual async Task<IdentityResult> DeleteAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
try
{
await _roleRepository.DeleteAsync(role);
await SaveChanges(cancellationToken);
}
catch (AbpDbConcurrencyException ex)
{
//TODO: Log...
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
/// <summary>
/// Gets the ID for a role from the store as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose ID should be returned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the ID of the role.</returns>
public Task<string> GetRoleIdAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
return Task.FromResult(role.Id);
}
/// <summary>
/// Gets the name of a role from the store as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose name should be returned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
public Task<string> GetRoleNameAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
return Task.FromResult(role.Name);
}
/// <summary>
/// Sets the name of a role in the store as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose name should be set.</param>
/// <param name="roleName">The name of the role.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public Task SetRoleNameAsync([NotNull] IdentityRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
role.Name = roleName;
return Task.CompletedTask;
}
/// <summary>
/// Finds the role who has the specified ID as an asynchronous operation.
/// </summary>
/// <param name="id">The role ID to look for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
public virtual Task<IdentityRole> FindByIdAsync([NotNull] string id, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(id, nameof(id));
//TODO: Add cancellationToken to Repository.FindAsync method as overload
return _roleRepository.FindAsync(id);
}
/// <summary>
/// Finds the role who has the specified normalized name as an asynchronous operation.
/// </summary>
/// <param name="normalizedName">The normalized role name to look for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
public virtual Task<IdentityRole> FindByNameAsync([NotNull] string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(normalizedName, nameof(normalizedName));
return _roleRepository.FindByNormalizedNameAsync(normalizedName, cancellationToken);
}
/// <summary>
/// Get a role's normalized name as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose normalized name should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
public virtual Task<string> GetNormalizedRoleNameAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
return Task.FromResult(role.NormalizedName);
}
/// <summary>
/// Set a role's normalized name as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose normalized name should be set.</param>
/// <param name="normalizedName">The normalized name to set</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetNormalizedRoleNameAsync([NotNull] IdentityRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
role.NormalizedName = normalizedName;
return Task.CompletedTask;
}
/// <summary>
/// Throws if this class has been disposed.
/// </summary>
protected void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
/// <summary>
/// Dispose the stores
/// </summary>
public void Dispose()
{
_disposed = true;
}
/// <summary>
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
/// </summary>
/// <param name="role">The role whose claims should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
public Task<IList<Claim>> GetClaimsAsync([NotNull] IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
return Task.FromResult<IList<Claim>>(role.Claims.Select(c => c.ToClaim()).ToList());
}
/// <summary>
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
/// </summary>
/// <param name="role">The role to add the claim to.</param>
/// <param name="claim">The claim to add to the role.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public Task AddClaimAsync([NotNull] IdentityRole role, [NotNull] Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
Check.NotNull(claim, nameof(claim));
role.AddClaim(claim);
return Task.FromResult(false);
}
/// <summary>
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
/// </summary>
/// <param name="role">The role to remove the claim from.</param>
/// <param name="claim">The claim to remove from the role.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public Task RemoveClaimAsync([NotNull] IdentityRole role, [NotNull] Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
Check.NotNull(role, nameof(role));
Check.NotNull(claim, nameof(claim));
role.RemoveClaim(claim);
return Task.CompletedTask;
}
}
}

6
src/Volo.Abp.Identity/Volo/Abp/Identity/UserStore.cs

@ -197,9 +197,9 @@ namespace Volo.Abp.Identity
await _userRepository.UpdateAsync(user);
await SaveChanges(cancellationToken);
}
catch (AbpDbConcurrencyException ex) //(DbUpdateConcurrencyException)
catch (AbpDbConcurrencyException ex)
{
//TODO: Log...
//TODO: Log...?
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
@ -224,7 +224,7 @@ namespace Volo.Abp.Identity
await _userRepository.DeleteAsync(user);
await SaveChanges(cancellationToken);
}
catch (AbpDbConcurrencyException ex) //(DbUpdateConcurrencyException)
catch (AbpDbConcurrencyException ex)
{
//TODO: Log...
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());

13
test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/Initialize_Tests.cs

@ -9,12 +9,21 @@ namespace Volo.Abp.Identity
public class Initialize_Tests : AbpIntegratedTest<AbpIdentityTestModule>
{
[Fact]
public void Should_Resolve_Services()
public void Should_Resolve_UserManager()
{
ServiceProvider.GetRequiredService<IdentityUserManager>();
}
//TODO: Move this service to Volo.Abp.EntityFrameworkCore.Tests since it's actually testing the EF Core repository registration!
[Fact]
public void Should_Resolve_RoleManager()
{
ServiceProvider.GetRequiredService<IdentityRoleManager>();
}
//TODO: Move this service to Volo.Abp.EntityFrameworkCore.Tests since it's actually testing the EF Core repository registration!
[Fact]
public void Should_Resolve_Repositories()
{
(ServiceProvider.GetRequiredService<IIdentityUserRepository>() is EfCoreIdentityUserRepository).ShouldBeTrue();
(ServiceProvider.GetRequiredService<IRepository<IdentityUser, string>>() is EfCoreIdentityUserRepository).ShouldBeTrue();

Loading…
Cancel
Save