An error occurred while accessing the Microsoft.Extensions.Hosting services. After Adding ASP.NET identity - entity-framework

I have DataContext and StartUp class in different projects and to add a new migration in Data project I used the below command:
dotnet ef migrations add IdentityAdded -s ..\API\API.csproj
And here is project structure:
I just added ASP.Net Core Identity to the project based on .Net 5 and configured it as below:
public class DataContext : IdentityDbContext<AppUser, AppRole, int,
IdentityUserClaim<int>, AppUserRole, IdentityUserLogin<int>,
IdentityRoleClaim<int>, IdentityUserToken<int>>
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
}
... DbSets
... protected override void OnModelCreating(ModelBuilder modelBuilder)
{ ... }
}
IdentityServiceExtension.cs:
public static class IdentityServiceExtension
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddIdentityCore<AppUser>(opt =>
{
opt.Password.RequireNonAlphanumeric = false;
})
.AddRoles<AppRole>()
.AddRoleManager<RoleManager<AppRole>>()
.AddSignInManager<SignInManager<AppUser>>()
.AddRoleValidator<RoleValidator<AppUser>>()
.AddEntityFrameworkStores<DataContext>();
}
}
I just inherited some classes such as AppUser, AppRole and AppUserRole from Identity Classes like this:
public class AppRole : IdentityRole<int>
{
public ICollection<AppUserRole> TheUserRolesList { get; set; }
}
After running the migration I get the following error:
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.RoleManager1[Core.Models.Entities.User.AppRole] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.RoleManager1[Core.Models.Entities.User.AppRole]': Implementation type 'Microsoft.AspNetCore.Identity.RoleValidator1[Core.Models.Entities.User.AppUser]' can't be converted to service type 'Microsoft.AspNetCore.Identity.IRoleValidator1[Core.Models.Entities.User.AppRole]')
What's wrong with this implementation?

You didn't register properly, instead of:
.AddRoleValidator<RoleValidator<AppUser>>()
add:
.AddRoleValidator<RoleValidator<AppRole>>()
Your error points out that it can't instantiate Microsoft.AspNetCore.Identity.RoleValidator with the Core.Models.Entities.User.AppUser, instead it requires Core.Models.Entities.User.AppRole.

Related

Using a DB context factory in Blazor server

I'm having a few issues setting up the EF database connection for my server side Blazor app. It was working with the standard DbContext setup until I noticed a few issues with connections not closing properly due to the nature of Blazor using the same context throughout. My research led me to look at DbContextFactory, but the interface IDbContextFactory is now deprecated in favour of IDesignTimeDbContextFactory.
I've set up a class to implement the interface:
public class FIS2ContextFactory : IDesignTimeDbContextFactory<FIS2_DbContext>
{
private readonly DbContextOptions<FIS2_FranklinContext_AutoGenerated> options;
public FIS2ContextFactory(DbContextOptions<FIS2_FranklinContext_AutoGenerated> contextOptions)
{
options = contextOptions;
}
public FIS2_DbContext CreateDbContext(string[] args)
{
return new FIS2_DbContext(options);
}
}
The DbContext I'm wanting to use is this, which inherits and expands on the DbContext generated by EF Power Tools:
public partial class FIS2_DbContext : FIS2_FranklinContext_AutoGenerated
{
public FIS2_DbContext()
{
}
public FIS2_DbContext(DbContextOptions<FIS2_FranklinContext_AutoGenerated> options) : base(options)
{
}
public virtual DbSet<StudentBasicDetailsWithCurrentTg> StudentBasicDetailsWithCurrentTgs { get; set; }
public virtual DbSet<CurriculumSearchBasicDetails> CurriculumSearchBasicDetails { get; set; }
public virtual DbSet<StudentAllEnrolments> StudentAllEnrolments { get; set; }
}
In my startup.cs I have it set up like this in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<FIS2_DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("FIS2")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<IFileService, FileService>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<ITimetableService, TimetableService>();
services.AddScoped<ICurriculumService, CurriculumServiceEf>();
services.AddScoped<IStudentService, StudentServiceEf>();
services.AddScoped<ICollectionService, CollectionsServiceEf>();
services.AddHttpContextAccessor();
services.AddHttpClient();
services.AddAuthenticationCore();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization();
services.AddSyncfusionBlazor();
services.AddScoped<SessionState>();
}
My issue is that when it gets to setting up the services that utilise this database connection, I am met with this error message in the program.cs:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ITimetableService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.TimetableService': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.TimetableService'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ICurriculumService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CurriculumServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.CurriculumServiceEf'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.IStudentService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.StudentServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.StudentServiceEf'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ICollectionService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CollectionsServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.CollectionsServiceEf'.)
For reference, here is an example of how the TimetableService is set up (the others are instantiated in the same way):
using DataLibrary.Data.Interfaces;
using DataLibrary.Models;
using DataLibrary.Models.timetable;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DataLibrary.Data.BusinessLayer
{
public class TimetableService : ITimetableService
{
private FIS2ContextFactory _contextFactory;
public TimetableService(FIS2ContextFactory db)
{
_contextFactory = db;
}
public async Task<List<spGetHolidaysBetweenDatesResult>> GetHolidaysBetweenDatesAsync(DateTime startDate, DateTime endDate)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
return await procedures.spGetHolidaysBetweenDatesAsync(startDate, endDate);
}
public async Task<List<PeriodsBetweenDates>> GetPeriodsBetweenDatesAsync(DateTime startDate, DateTime endDate)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
var toReturn = new List<PeriodsBetweenDates>();
var results = await procedures.spGetPeriodsBetweenDatesAsync(startDate, endDate);
foreach (var item in results)
{
toReturn.Add(new PeriodsBetweenDates(item.Date, item.Timetable, item.BlockCode, item.StartTime, item.EndTime));
}
return toReturn;
}
public async Task<List<StudentTimetable>> GetStudentTimetableAsync(DateTime startDate, DateTime endDate, string studentID)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
var results = await procedures.spGetStudentTimetableAsync(startDate, endDate, studentID);
List<StudentTimetable> studentTimetables = new List<StudentTimetable>();
foreach (var item in results)
{
studentTimetables.Add(JsonConvert.DeserializeObject<StudentTimetable>(item.timetable));
}
return studentTimetables;
}
}
}
Is it because I'm using the wrong method to create the context factory in the startup, or is it something later on that I've got wrong?
If you want to resolve a specific factory type, you must register with this overload, AddDbContextFactory<TContext,TFactory> documented here:
This overload allows a specific implementation of
IDbContextFactory to be registered instead of using the
default factory shipped with EF Core.
so
services.AddDbContextFactory<FIS2_DbContext,FIS2ContextFactory>(options => options.UseSqlServer(Configuration.GetConnectionString("FIS2")));

How do I register DbContext EF Core in ServiceStack Core?

With EF Core, DbContext is registered as Scoped by EF service extension. This is desirable because DbContext is not thread-safe and therefore it should be created per request.
ServiceStack IOC treats any Scoped registration in Startup as singleton, which contradicts with the point above.
One possible solution is to not use EF Core's service extension, but that seems to bring a lot of boilerplate code and reduce maintainability. Is there any better way?
--
UPDATE
I'd like to provide sample code for clarity
I added a private Guid to the DbContext class so that I can tell whether we have the new instance.
public class BloggingContext : DbContext
{
private readonly Guid _instance;
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
_instance = Guid.NewGuid();
}
public DbSet<Blog> Blogs { get; set; }
}
With .NET Core MVC, the controller code looks like
public class BlogsController : Controller
{
private readonly BloggingContext _context;
public BlogsController(BloggingContext context)
{
_context = context;
}
// skip for readability
}
For each request hitting the controller, the _instance inside BloggingContext returns an unique value. However, when using within a ServiceStack service, _instance always returns the same value.
public class BlogService : ServiceStack.Service
{
private readonly BloggingContext _context;
public BlogService(BloggingContext context)
{
_context = context;
}
// skip for readability
}
This behaviour is consistent with ServiceStack documentation about .NET Core Container Adapter that scoped dependencies registered in .NET Core Startup is singleton within ServiceStack. However, it is not desirable because we want DbContext to be created per request.
My solution is that I move the DbContext registration into AppHost code as below
public override void Configure(Container container)
{
container.AddScoped(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
return new BloggingContext(optionsBuilder.Options);
});
}
This code works as I expect. Every instance of BloggingContext injected into my BlogService is now unique. However, I find myself unable to use any service collection extension which is very handy in .Net Core Startup anymore. For example, I want to use Entity Framework Unit Of Work and I couldn't call
services
.AddUnitOfWork<BloggingContext>();
Instead, I have to wire up all dependencies of that library myself like
public override void Configure(Container container)
{
container.AddScoped(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
return new BloggingContext(optionsBuilder.Options);
});
container.AddScoped<IRepositoryFactory, UnitOfWork<BloggingContext>>();
container.AddScoped<IUnitOfWork, UnitOfWork<BloggingContext>>();
container.AddScoped<IUnitOfWork<BloggingContext>, UnitOfWork<BloggingContext>>();
}
You should be able to register it in .NET Core's IOC like any .NET Core App:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BloggingContext>(options =>
options.UseSqlite("Data Source=blog.db"));
}
Then reference like a normal dependency in your ServiceStack Services:
public class MyServices : Service
{
public BloggingContext BloggingContext { get; set; }
}
Which uses ServiceStack's .NET Core Container Adapter to resolve any dependencies not in ServiceStack's IOC, in .NET Core's IOC.

EntityFrameworkCore - How to inject IPrincipal when DbContextPool is enabled

I have an aspnet core MVC application, and I need to inject the IPrincipal of the current request so that I know who is the current logged in user. I followed this article with no problem.
Now I need the IPrincipal in my Dbcontext to auto-populate audit fields (CreatedBy, UpdatedBy, etc). Being naive I could inject the IPrincipal in my DbContext constructor:
public MyDBContext(DbContextOptions<MyDBContext> options, IPrincipal principal)
But I am using the DBContextPool, which reuses DbContext instances across WebAPI requests.
What's the proper way to inject IPrincipal in this scenario?
------------------- Update ----------------------
I am thinking I can follow the same approach as the HttpContextAccesor, and create a IClaimsPrincipalAccessor, register it as a singleton and inject it into my Dbcontext.
For asp net core applications I need an HttpContextClaimsPrincipalAccesor, where ClaimsPrincipal comes from the HttpContext.User
You can register the IPrincipal as a Transient service on your Startup, like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IPrincipal>(provider =>
provider.GetService<IHttpContextAccessor>().HttpContext.User);
// ...
}
Then on your DbContext constructor you can inject the IServiceProvider that will allow you to get the current IPrincipal instance, for example:
public class YourDbContext : DbContext
{
private IServiceProvider _services;
public YourDbContext(IServiceProvider services)
{
_services = services;
}
public void SomeMethod()
{
var principal = _services.GetService<IPrincipal>();
// ...
}
}

get_CurrentTransaction in InMemory.TransactionManager does not have an implementation

I'm working on a .NET Core application that implements the InMemoryDatabase for integration testing. I'm following the instructions from this link.
Basically it shows how to configure the service (in Startup.cs) using the AddDbContext method which is what I did and I make the call to UseInMemoryDatabase:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase());
services.AddMvc();
services.AddSingleton<ITodoRepository, TodoRepository>();
services.AddScoped<IAssetRepository, AssetRepository>();
}
My AppDbContext is defined in another project in the solution and is defined as such:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> dbContextOptions) :
base(dbContextOptions)
{
}
public DbSet<Asset> Assets { get; set; }
public DbSet<TodoItem> Todos { get; set; }
}
Back to the Startup.cs file, according to the link I need to code the Configure method like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
var repository = app.ApplicationServices.GetService<IAssetRepository>();
InitializeDatabaseAsync(repository).Wait();
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
This snippet runs ok and executes the call to GetService. This call runs the constructor for AppDbContext but the constructor crashes with the folling exception. It crashes when calling the base constructor: base(dbContextOptions).
Method 'get_CurrentTransaction' in type
'Microsoft.EntityFrameworkCore.Storage.Internal.InMemoryTransactionManager'
from assembly 'Microsoft.EntityFrameworkCore.InMemory,
Version=1.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'
does not have an implementation.
I can't seem to figure out what is causing this exception. Can someone give me some direction?
Thanks !
After some more investigating, I found out that this error occurred because the project that contains the configuration code (Startup.cs) had a reference to version 1.0.1 of "Microsoft.EntityFrameworkCore.InMemory".
However, I changed it to 1.1.0 and it suddenly worked. So I'm assuming that the implementation was added to version 1.1.0.
Hope this helps anyone in the future.

Entity Framework Core DbContext and Dependency Injection

I'm building a service application using Web API, .Net Core and EntityFramework Core.
For configuring options in my DbContext I'm using these lines in "ConfigureServices" method in Startup.cs
var connection = #"Server=ISSQLDEV;Database=EventManagement;Trusted_Connection=True;";
services.AddDbContext<EMContext>(options => options.UseSqlServer(connection));
I know that if I add the context as a constructor parameter in the controller .Net will inject the context in the constructor.
But this is not the behavior I want. I don't want my web api to know anything about the dbcontext. I have a DataAccess Project with a repository class that handles all CRUD operations.
This means that I just want to say Repository.AddEvent(evt) in my controller and then repository knows how to handle that.
On the other hand, repository uses a simple dependency resolver to get the right "IDataAdapter" implementation. One of those implementations is SQLDataAdapter. This is the point I need my context.
How can I pass my context all the way to this point?
You can solve this by adding your dbcontext via constructor injection to your classes from your data access layer.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(o => o.UseSqlServer(myConnStr));
services.AddScoped<Repository>(); // 'scoped' in ASP.NET means "per HTTP request"
}
}
public class MvcController
{
private Repository repo;
public MvcController(Repository repo)
{
this.repo = repo;
}
[HttpPost]
public void SomeEndpoint()
{
this.repo.AddFoo(new Foo());
}
}
public class Repository
{
private DbContext db;
public Repository(ApplicationDbContext db)
{
this.db = db;
}
public void AddFoo(Foo obj)
{
this.db.Set<Foo>().Add(obj);
this.db.SaveChanges();
}
}
If you want to further customize how your DbContext is injected into your DI container, I suggest you look at what .AddDbContext is actually doing. See https://github.com/aspnet/EntityFramework/blob/1.0.0/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L142-L158