Retrieving data from classes that use TPH in entityframework - asp.net

So I started my project using the identity scaffolding and created an application user class that inherits from identity user
using Microsoft.AspNetCore.Identity;
namespace test6.Models
{
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
and some classes that inherit from this class
using Microsoft.AspNetCore.Identity;
namespace test6.Models
{
public class Teacher : ApplicationUser
{
public string Course { get; set; }
}
}
I have set up my roles and they seem to be working fine, however my problem is that I am trying to retrieve data from users and when retrieving data that is specific to a class that has inherited from applicationuser like Course from Teacher I get an error which is because my usermanager is initialised with ApplicationUser
private readonly UserManager<ApplicationUser> _userManager;
The method I'm using to retrieve users is this
[HttpGet]
public async Task<IActionResult> ListTeacher()
{
var users = await _userManager.GetUsersInRoleAsync("Teacher");
return View(users);
}
So I have tried to initialise usermanager with Teacher to test but I get an error I think it's because in the dependancy I used ApplicationUser and I don't think you can use more than one. So my question is what possible solutions are there for this.(Sorry if my question isn't great or my explanation is poor)

Ok I think I've found a solution, turns out you can add another dependancy.
builder.Services.AddIdentityCore<Teacher>()
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<Teacher, IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
With this I can initialise usermanager with Teacher and so far it has worked.

Related

How to get Identity User outside Razor Pages in Blazor Server-side?

I am working on a Blazor Server-Side application, using Microsoft Identity, Entity Framework and a multitenant approach with shared Db.
I have extended the IdentityUser class so that I could have the TenantId in the AspNetUser Table
public class ApplicationUser : IdentityUser
{
public int TenantId { get; set; }
}
}
Then I have applied a general query filter to my dbModel based on the TenantId
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().HasQueryFilter(a => a.TenantId == TenantId);
}
In my blazor page I can call this function
public async Task SetTenant()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
ApplicationUser = await UserManager.FindByNameAsync(user.Identity.Name);
var TenatId = ApplicationUser.TenantId;
}
Finally in my service I can get a list of Employees with the right TenantId
public Task<Employee[]> GetEmployees(int TenatntID)
{
using (var ctx = new ProgramDbContext(TenantId))
{
return Task.FromResult(ctx.Employee.Select(d => new Employee
{
Id = d.Id,
TenantId = d.TenantId,
Name= d.Name,
}).ToArray());
}
}
With this approach, everytime I want to call a function to get DB's Data, I need to identity the user and get the TenantId, then call the specific function and pass the tenantID to it.
I would like to know if my approach is completely wrong to implement this type of solution, for example:
Is it possible to add a Singleton service of an ApplicationUser, so that once is is identified after login, i can inject the service in every class where i need the ApplicationUser.TenantId?
Is it possible to identify and authenticate the Application User outside a blazor class? for example a plain C# class? I was able to pass the AuthenticationStateProvider and UserManager in the constructor of my Service class, but I cant await a function inside the constructor to actually get the ApplicationUser object.
public CaronteWebService(AuthenticationStateProvider authenticationStateProvider, UserManager userManager)
{
_AuthenticationStateProvider = authenticationStateProvider;
_userManager = userManager;
}
UserManager<ApplicationUser> _userManager;
public ApplicationUser ApplicationUser { get; set; }
AuthenticationStateProvider _AuthenticationStateProvider { get; set; }

FriendSystem in Asp.net core using IdentityUser and EntityFramework

So I would like to create a table which contains two foreign keys. Both pointing to a userId in the AspNetUsers table. I don't know how to reference the AspNetUsers' Id Column since I didn't create a model for that myself.
It should look something like this:
UserId | FriendId
-----------------
ada656 | abes7e
Any help is greatly appreciated.
Simply create folder called claim and add new class called ApplicationUser, this class will inherit from IdentityUser class as showing below:
using Microsoft.AspNetCore.Identity;
namespace YourApplication.Claims
{
public class ApplicationUser: IdentityUser
{
}
}
Now you can put any column that you need to apply inside it like this:
namespace YourApplication.Claims
{
public class ApplicationUser: IdentityUser
{
public int FriendId { get; set; }
public string FullName { get; set; }
}
}
After you applied this, goto controller and initialize user manager:
private readonly UserManager<ApplicationUser> _userManager;
Then assign the constructor to pass the data like this:
public ClassName(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
Then you can read the users data like this:
_userManager.Users.ToListAsync();
or read specific user data like this:
_userManager.FindByIdAsync(id);
or you can delete the user like this:
var user = await _userManager.FindByIdAsync(id);
_userManager.DeleteAsync(user)
Hope this help you solve the problem.

Email Validation with IdentityUser in ASP.NET

I recently updated my API to support Asp.NetCore.Identity. Therefor i had to remove a lot of fields in my User class because they already exist in the IdentityUser class, from which User inherits.
In my old User class i validated for string length and email using DataAnnotations.
[Required]
[EmailAddress]
public string Email { get; set; }
In my new class i tried overwriting the existing fields with my own and add the Annotations but this does not work.
How do you validate fields with IdentityUser?
Jakob
You have to use UserValidator
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
UserManager.UserValidator = new UserValidator<ApplicationUser>(UserManager) { };
//you can override validator or validation for specific property
}
}

Extending Identity3 in MVC6

using the latest (current) RC1 of asp.net5 I'm looking at creating a simple relationship between a User entity and a WorkLog entity.
Is it possible to use the ApplicationUser Class from Identity as a starting point and use the ApplicationUser key which is defined as the linking key? I have had problems extending the ApplicationUser in the past and therefore generated a seperate dbcontext (pointing to the same database) and created my own plumbing in order to pass the IdentityUsers Id into my seperate dbcontext. Does anyone have any examples of extending the IdentityDbContext adding foreign key tables mapping to the IdentityUser Class?
Example below
//DBContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<WorkLogItem> WorkLogItems { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<WorkLogItem>(
e =>
{
e.Property(p => p.id).IsRequired().UseSqlServerIdentityColumn();
});
}
}
//WorkLogItem
public class WorkLogItem
{
public int id { get; set;}
public String UserId { get; set; }
public int Hours { get; set; }
public String Description { get; set; }
}
//ApplicationUser
public class ApplicationUser : IdentityUser
{
public ICollection<WorkLogItem> WorkLogItems { get; set; }
}
Doing what you've asked is expected to work out of the box. You can look at this commit to see the difference between a newly created MVC 6 project with Identity and your schema above.
Registering a user, and refreshing /Home/Index causes WorkLogItems to be added as expected. Note you don't need a separate DB context for this.
public IActionResult Index()
{
var user = _db.Users.Include(p => p.WorkLogItems).FirstOrDefault();
if (user != null)
{
user.WorkLogItems.Add(new WorkLogItem { Description = "New item added" });
_db.SaveChanges();
ViewBag.WorkItems = user.WorkLogItems.ToList();
}
else ViewBag.WorkItems = new WorkLogItem[] { };
return View();
}
The key items to be aware of when you add any collection to an existing entity are;
Make sure you add the migration and update the databse
Make sure you use Include on the query because EF7 does not support Lazy Loading.

How can I extend ApplicationUser in ASP.NET Identity but with properties that can be null?

I would like to extend Application User. Here's an example:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
I tried this but it seems like when I login if any of these properties is set to a null in the database then when the /Token call is made it returns with an error.
Can someone tell me. Do I need to make a change to the way these properties are set here or in another place?
If i understand your question correctly, a simple solution to your problem could be to create a nullable type. Can you please share what you would like to do? Create a custom identity provider, for example?

Resources