C
C#3mo ago
hutoanhill

✅ unregistered services

I am implementing my own version of MapIdentityAPI<>() to add some custom functionality. to do this I've copied the code for the method and dependencies out of Microsoft.AspNetCore.Routing into a separate class from where i can call this CustomMapIdentityAPI<>(). i have finally purged all the errors and I've gotten to testing, but now i am getting this error:
System.InvalidOperationException: No service for type 'Microsoft.AspNetCore.Identity.IEmailSender1[Microsoft.AspNetCore.Identity.IdentityUser]' has been registered.
System.InvalidOperationException: No service for type 'Microsoft.AspNetCore.Identity.IEmailSender1[Microsoft.AspNetCore.Identity.IdentityUser]' has been registered.
pointing at this line:
var emailSender = endpoints.ServiceProvider.GetRequiredService<IEmailSender<TUser>>();
var emailSender = endpoints.ServiceProvider.GetRequiredService<IEmailSender<TUser>>();
I've tried reverting to the old version of MapIdentityApi<>() but i get the same error, so i assume its not something I've changed on accident. This leads me to conclude that my set up is flawed. i did just change my Main to be async Task<int> Main instead of int Main, is this it? if so, how do i seed my roles without async methods?
17 Replies
hutoanhill
hutoanhill3mo ago
heres my set up code:
private static async Task SeedRolesAsync(RoleManager<IdentityRole> roleManager){
string[] roles = new[] { "registering", "registered" };

foreach (string role in roles){
if (!await roleManager.RoleExistsAsync(role)){
await roleManager.CreateAsync(new IdentityRole(role));
}
}
}
...
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpContextAccessor();

builder.Services.AddDbContext<DataContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("AuthConnection")));

builder.Services.AddControllersWithViews();


// for roles
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
})
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();

// This disables the conformed email requirement. We should Re-enable this eventually.
builder.Services.Configure<IdentityOptions>(options => {
options.SignIn.RequireConfirmedEmail = false;
});

builder.Services.AddAuthorization();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => {
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});

WebApplication app = builder.Build();

// seed roles
using (var scope = app.Services.CreateScope()) {
IServiceProvider services = scope.ServiceProvider;
RoleManager<IdentityRole> roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await SeedRolesAsync(roleManager);
}
private static async Task SeedRolesAsync(RoleManager<IdentityRole> roleManager){
string[] roles = new[] { "registering", "registered" };

foreach (string role in roles){
if (!await roleManager.RoleExistsAsync(role)){
await roleManager.CreateAsync(new IdentityRole(role));
}
}
}
...
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpContextAccessor();

builder.Services.AddDbContext<DataContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("AuthConnection")));

builder.Services.AddControllersWithViews();


// for roles
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
})
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();

// This disables the conformed email requirement. We should Re-enable this eventually.
builder.Services.Configure<IdentityOptions>(options => {
options.SignIn.RequireConfirmedEmail = false;
});

builder.Services.AddAuthorization();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => {
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
options.OperationFilter<SecurityRequirementsOperationFilter>();
});

WebApplication app = builder.Build();

// seed roles
using (var scope = app.Services.CreateScope()) {
IServiceProvider services = scope.ServiceProvider;
RoleManager<IdentityRole> roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await SeedRolesAsync(roleManager);
}
:/ probubly figured it out
hour
hour3mo ago
To my understanding your problem is currently that nothing is registering an implementation of IEmailSender.
hutoanhill
hutoanhill3mo ago
builder.Services.someStuff
hour
hour3mo ago
I think you need to write and register an implementation of IEmailSender
hutoanhill
hutoanhill3mo ago
for posterity, registering a service is using builder.Services.ReliventRegistrationMethod() like builder.Services.AddIdentity or builder.Services.AddControllersWithViews() already on it. found right documentation thanks
hour
hour3mo ago
👍 Good luck
hutoanhill
hutoanhill3mo ago
the bigest question is why it worked in the first place :) i had this working for hours without an email service registered um...
hour
hour3mo ago
Something probably registers an implementation of IEmailSender when using .MapIdentityAPI
hutoanhill
hutoanhill3mo ago
shouldent this work??
builder.Services.AddTransient<IEmailSender, EmailServices>();

...

public interface IEmailSender {
Task SendEmailAsync(string email, string subject, string message);
}

public class EmailServices : IEmailSender {
private readonly string _logFilePath = "fakeMail.txt";

public EmailServices() {

}

public async Task SendEmailAsync(string email, string subject, string message)
{
// Format the log entry
string logEntry = $"[{DateTime.Now}] To: {email}\nSubject: {subject}\nMessage: {message}\n\n";

// Append the log entry to the file
await File.AppendAllTextAsync(_logFilePath, logEntry);
}
}
builder.Services.AddTransient<IEmailSender, EmailServices>();

...

public interface IEmailSender {
Task SendEmailAsync(string email, string subject, string message);
}

public class EmailServices : IEmailSender {
private readonly string _logFilePath = "fakeMail.txt";

public EmailServices() {

}

public async Task SendEmailAsync(string email, string subject, string message)
{
// Format the log entry
string logEntry = $"[{DateTime.Now}] To: {email}\nSubject: {subject}\nMessage: {message}\n\n";

// Append the log entry to the file
await File.AppendAllTextAsync(_logFilePath, logEntry);
}
}
my current implimentation of that method is litarly coppied and pasted.
hour
hour3mo ago
Should work yes.
hutoanhill
hutoanhill3mo ago
need to get it to work before i can change it so beans now it complanes that it has been registered??? Unhandled exception. System.InvalidOperationException: No service for type 'Microsoft.AspNetCore.Identity.IEmailSender'1[Microsoft.AspNetCore.Identity.IdentityUser]' has been registered.
hour
hour3mo ago
That's the same error as you got before
hutoanhill
hutoanhill3mo ago
yea... but now ive registed it... weird aaand now my it dosent like my EmailServices class... man computers can be weird.
hour
hour3mo ago
Think it's because you're not implementing the generic IMailSender<TUser> class Think this is as far as my knowledge goes tbh, hope someone else has a concrete answer 😭
hutoanhill
hutoanhill3mo ago
so emailServices : IMailSender?
hour
hour3mo ago
Yes Don't write your own IEmailSender Try just inheriting from the Microsoft.AspNetCore.Identity.IEmailSender
hutoanhill
hutoanhill3mo ago
ok. the exact service asked for is IEmailSender<TUser> but when i add <TUser> to the the extention declaration it freaks out.
Want results from more Discord servers?
Add your server