Getting related entities to an ASP.NET Identity User - asp.net

Using asp.net Identity, I'm trying to work out if my AppUser has any Businesses attached (using a one to many relationship)
In the database I have an AppUser with many Businesses connected, but when I run the query to retrieve my user, Businesses is always null.
If I manually call the AspNetUsers table via a context and Include() the Businesses then they show up ok, but I thought the virtual keyword would retrieve the properties regardless?
Here is my user class:
public class AppUser : IdentityUser
{
public AppUser() {
Businesses = new List<Business>();
}
public bool HasABusinessProfile
{
get
{
return Businesses.Any();
}
}
public virtual ICollection<Business> Businesses { get; set; }
}
On login:
public async Task<IActionResult> Post([FromBody]UserLoginViewModel credentials)
{
var user = await GetClaimsIdentity(credentials.UserName, credentials.Password);
The problem is that at this point in the code, user.HasABusinessProfile is always false.

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; }

ApplicationUser has a list of ApplicationUser

I have built a new Web Application that uses the template Visual Studio provides and included MVC and Web API. The default authorization mechanism is Identity and the database interaction is done using Entity Framework with Code-first method of creating the database.
I have three requirements:
A user can have a list of Children objects
I do not want to use a "relationship" object
All users already exist on the AspNetUsers table, because they all need to be able to login, so I do not want another table to maintain user data
In theory, multiple parents could have reference to multiple children, but for this example, we will just consider it a one-to-many relationship.
In my application, I need to have an ApplicationUser have a list of ChildUsers as a collection of ApplicationUser such as shown below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string ShirtSize { get; set; }
public ICollection<ApplicationUser> Children { get; set; }
}
I want these users to be accessible as shown above (a collection of ApplicationUser), not a collection of Relationship object that ties them together such as:
public class Relationship
{
public String ParentId { get;set; }
public String ChildId { get;set; }
}
Can a new table be created and exist on the database without having a code-first model for it to know how to create a relationship table?
What are available solutions to this problem?
After some research, and experimentation, I have found bits and pieces of guidance to arrive at a solution that works.
In order for an intermediate table to be created to maintain the relationship, the ApplicationDbContext OnModelCreating function needs to know what it should look like. I have told it to create a new table that is not bound to an object by using the modelBuilder shown in the code below. Unfortunately, I do not have the links to the articles that guided me to this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DefaultConnection", throwIfV1Schema: false )
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<ApplicationUser>()
.HasMany( p => p.ChildUsers )
.WithMany()
.Map( m =>
{
m.MapLeftKey( "Father_Id" );
m.MapRightKey( "Son_Id" );
m.ToTable( "father_son_relation" );
} );
}
}
Additionally, when you need to add Children to the parent ApplicationUser, you will need to do some tweaking as you are about to insert so that it updates the database correctly. I definitely want the UserManager to do the creation of the user for me, but that means that when I go to add the user to my list of Children with the code below, it tries to add it again and throws an exception because it already exists.
var result = await UserManager.CreateAsync( user, model.Password );
var myUserId = User.Identity.GetUserId();
var users = AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.ChildUsers );
var u2 = users.First();
u2.ChildUsers.Add( user );
await AppDbContext.SaveChangesAsync();
After finding this question, I researched the EntityStates and found that adding the following line before calling SaveChanges resolved the exception and it no longer attempts to add it again.
AppDbContext.Entry( user ).State = EntityState.Unchanged;
TADA!!! Now to select them from the database using EF, you can then use the following code:
AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.Children ).First();
Since I am only getting one level of Children this will work ok, after that you risk circular references.
Comments and ideas to improve the code are welcome.

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?

Are there any implementations of ASP.NET Identitity that have another level above account?

I am using ASP.NET Identity. It works well but I would like to add in a parent to the AspNetUsers table. In my case I would like to have each user belong to an organization. At this point I am just looking for some ideas to see if others have seen implementations that would allow this.
Has anyone seen any implementations that do this. I would like to get some tips on how I could implement this functionality.
I'm presuming you are using default EF implementation of Identity storage.
Identity is very flexible and can be bent into many shapes to suit your needs.
If you are looking for a simple parent-child relationship, where every user would have a parent record (such as Company), one of the ways to implement that is to add company reference to user class:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNet.Identity.EntityFramework;
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
}
[ForeignKey("CompanyId")]
public Company Company { get; set; }
public int CompanyId { get; set; }
}
public class Company
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public String Name { get; set; }
public virtual ICollection<ApplicationUser> Users { get; set; }
}
This will put a foreign key on users to companies. But from here next course of action depends on what is required in your application. I would imagine that you'll have some sort of restriction for users depending on what company they belong to. For quick company retrieval you can store CompanyId in a claim when logging in the user.
Default implementation of ApplicationUser has GenerateUserIdentityAsync method. You can modify this as following:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
identity.AddClaim(new Claim("CompanyId", CompanyId.ToString()));
return userIdentity;
}
Then on every request you'll be able to access this CompanyId claim from the cookie:
public static int GetCompanyId(this IPrincipal principal)
{
var claimsPrincipal = principal as ClaimsPrincipal;
//TODO check if claims principal is not null
var companyIdString = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == "CompanyId");
//TODO check if the string is not null
var companyId = int.Parse(companyIdString); //TODO this possibly can explode. Do some validation
return companyId;
}
And then you'll be able to call this extension method from almost anywhere of your web application: HttpContext.Current.User.GetCompanyId()

Resources