I am creating an ASP.Net MVC application related to design, buildings and etc. I need to have two types of users - Normal People and Companies. Here I read how I can do it. I have created basic class ApplicationUser. The classes PersonUser and CompanyUser inherit this class and have some more properties. I am using MSSQL Database. I have thee following questions:
(My main question) Should I create DbSet<PersonUser> and DbSet<CompanyUser> or I should create DbSet<ApplicationUser>?
When I create database connections should the foreign key point to PersonUser and CompanyUser or to ApplicationUser?
In controllers when I user UserManager<TUser> or SignInManager<TUser> depending on the user I need to use different properties. Do I have to have two UserManagers or I should parse like that every time var user = (PersonUser) await this.userManager.GetUserAsync(this.User);?
Here are my classes:
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
this.Notifications = new HashSet<Notification>();
}
// Audit info
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
// Deletable entity
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
[Required]
public string Password { get; set; }
public UserType UserType { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
}
public class PersonUser : ApplicationUser
{
public PersonUser() : base()
{
this.Bids = new HashSet<Bid>();
this.Buildings = new HashSet<Building>();
}
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public DesignerType DesignerType { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
public class CompanyUser : ApplicationUser
{
public CompanyUser() : base()
{
}
[Required]
public string CompanyName { get; set; }
// That is like Id of the company
[Required]
public string Bulstat { get; set; }
}
There's two different scopes of problems related to having different "Types" of users in your application.
Permissions / Authorization
This is the scope for what different types of Users are allowed to do. What endpoints are they allowed to call? What are they allowed to acces?
For this problem scope you want to use IdentityRoles in order to distinguish said users
Database Relationships
This is the scope when the different types of Users will have genuinely distinct links in the database to different tables. For example for your SchoolDatabase, you may have TeacherUsers which have One to Many relationship with StudentUsers, and vise versa, but both types of users can login.
In this case you actually want to have different classes to distinguish them that inherit from a base User class.
Table links both types of users share, for example perhaps both a Teacher and a Student have a Classroom they belong to, you would put on the base class. Also chances are things like Password, Login, UserName, Email, etc would all go on the BaseUser class since everyone has such things.
Table links that are *uniqueto one type of user would go on that users table. So if Teachers have aCurriculum` then you would add a link between Teacher<->Curriculum, not BaseUser<->Curriculum, and if Students have Homework (but Teachers dont), then that link would be to the Student table specifically.
Related
Microsoft Identity introduces UserManager<T> and RoleManager<T> classes to interact with AspNetUsers, AspNetRoles and the other database tables. But why we should use them? Maybe it is a better solution to scaffold the database and work with its tables directly using the Entity Framework and forget about classes UserManager<T> and RoleManager<T>?
Why am I asking?
I my application I want to follow the multitier architecture.
I started with creating the DTO-class for the user:
public class UserDto
{
public string Id { get; set; }
public string UserName { get; set; }
...
public List<RoleDto> Roles { get; set; }
}
public class RoleDto
{
public string Id { get; set; }
public string Name { get; set; }
...
}
Type IdentityUser I want to map into UserDto, type IdentityRole - into RoleDto.
As you can see, in UserDto I want to store roles of a user.
Now let's have a look at the IdentityUser type: link.
It contains a lot of properties, but it doesn't have roles of the user.
So, when mapping IdentityUser into the UserDto, I need to work with RoleManager<T> to get roles of the user. I think, it's a dirty solution.
My idea
We can forget about UserManager<T> and RoleManager<T> types. We can simply scaffold the database and work with it using the Entity Framework.
After scaffolding of database I have the following:
public partial class AspNetUser
{
public string Id { get; set; }
public string UserName { get; set; }
public string NormalizedUserName { get; set; }
public string Email { get; set; }
public string NormalizedEmail { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string ConcurrencyStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual ICollection<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual ICollection<AspNetUserRole> AspNetUserRoles { get; set; }
public virtual ICollection<AspNetUserToken> AspNetUserTokens { get; set; }
}
// AspNetUserClaim, AspNetUserLogin, AspNetUserRole and AspNetUserToken
I introduced only AspNetUser class to be short. As you can see, it has a property AspNetUserRoles - roles of the user. It's great, because now it's really simple to map this class into the UserDto.
Question
Is it a good idea to scaffold the database and don't work with UserManager and RoleManager classes?
Maybe you can introduce a better solution?
Why do you think that this is a good idea? What would you gain by re-writing something like identity?
From the Introduction to Identity on ASP.NET Core
ASP.NET Core Identity:
Is an API that supports user interface (UI) login functionality.
Manages users, passwords, profile data, roles, claims, tokens, email confirmation, and more.
It's not just database access. It is also code that manages login functionality, secure token creation, secure password management and much more.
You need to take all of the above into consideration if you create a custom system, have an external auditor to pen-test your solution (even though this is a good idea whatever choice you make), unit test, performance test etc.
All the above is already done. You can easily customize the identity with various hook points too.
BTW, identity uses ef to access the datastore already by default.
Do structure your multilayer application, but leave identity out of it. It is a horizontal concern and it's presence is there to simplify your development and let you worry about your business needs only.
Let's say you have a Tenant that has many Users that have many Accounts that have many transactions.
How far down the tree do you add a TenantId property?
How far down do the tree do you add a UserId?
Or do you only ever need to have the parent id?
Assume that the user will never intentionally access a child entity without first accessing it's parent. In a slug, it would be something like:
baseurl.com/accounts/{accountId/transactions/{transactionId}
public class Tenant
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
}
public class User
{
public long Id { get; set; }
public long TenantId { get; set; }
public string Name { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
public class Account
{
public long Id { get; set; }
public long TenantId { get; set; }
public string UserId { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
}
public class Transaction
{
public long Id { get; set; }
public long TenantId { get; set; }
public string UserId { get; set; }
public string AccountId { get; set; }
}
I tend to see examples using TenantId on everything that falls under a tenant. I imagine this is for security, but my natural assumption would be that UserId would suffice. For exampple, even though Transaction is two levels below User, I don't believe I should allow anyone who know the Transaction ID to access a transaction without also being the user that owns the account.
The norm that we have followed for adding the TenantId is to all the main entities. for example, Users and Accounts should have the TenantId. Since the transaction is a dependent entity on both the User and Accounts and as such since it cannot be fetched with out referring to the base (Account / User), there is no mandate for a TenantId.
The rule of thumb is to classify the main business entity to be grouped / categorized by a tenant. (User, Accounts etc) so that when the data is being retrieved or updated in the database, the proper tenant filtering happens prior to any operation being performed on the mapping or child tables. Hence, based on the domain objects, please include the TenantId column wisely.
My application uses ASP.NET Identity 2 with tables stored in SQL Server 2012 Relational tables. Here's a view of two classes that I would like to use for reporting:
public partial class AspNetUser
{
public AspNetUser()
{
this.AspNetRoles = new List<AspNetRole>();
}
public string Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
public partial class AspNetRole
{
public AspNetRole()
{
this.AspNetUsers = new List<AspNetUser>();
}
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AspNetUser> AspNetUsers { get; set; }
}
I created the following LINQ to give me a report:
var users = await db.AspNetUsers
.Include(u => u.AspNetRoles)
.ToListAsync();
This is inside a WebAPI method and when I check the result it gives me a
result that I don't expect. What I see is an array of user objects and
then inside that an array of Roles. But inside the array of Roles is
another array of users!
Here's the output that I see with some fields ommitted to make it show
easily:
[{"id":"27cd003a-fb6a-4a2d-8df9-f502bc10a583"
"aspNetRoles":[
{
"id":"53724f55-af7a-448b-9ae2-1fe295b109fd",
"name":"Admin",
"aspNetUsers":[
{
"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
"aspNetRoles":[]
},
{
"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b",
"aspNetRoles":[]
}
]},
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
How can I change the LINq so that it gives me an array of AspNetUser objects and inside a simple array of AspNetRole objects? I also need the query to show me the users even if they have no roles. Something like this:
User1
Role1
Role2
User2
Role1
User3
User4
Role2
As you said, you have no control over the domain classes -- they belong to Microsoft. So, what you're really saying is that you don't want to expose your domain objects directly to the rest of the world (google that and you'll find all sorts of people with opinions on that subject). So, one option is to define a set of classes which are the ones you want to expose (data exchange classes). Something like:
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
Then, when you get your domain objects back, map them into your data exchange objects for serialization and exposure to the rest of the world.
I have two entities and these two entities have two different relationships between one another. The first relationship is a one to many relationship and the second relationship is a many to many relationship. See the entities below.
public class User
{
public int Id { get; set; }
public string Password { get; set; }
public string Name { get; set; }
// Collection that represent this user's attached social providers.
public virtual ICollection<SocialProvider> SocialProviders { get; set; }
// Collection representing this user's friends from her attached social providers.
public virtual ICollection<SocialProvider> SocialContacts { get; set; }
}
public enum Provider
{
Google,
Facebook,
Twitter
}
public class SocialProvider
{
public int Id { get; set; }
public Provider Provider { get; set; }
// Granted access token for talking to the provider API on user's behalf.
public string AccessToken { get; set; }
// User's unique ID at the social provider.
public string ProviderId { get; set; }
// The user that this SocialProvider belongs to.
public virtual User User { get; set; }
// The users that have this social provider as a contact.
public virtual ICollection<User> Contacts { get; set; }
}
The workflow is as follows:
I sign up within the application. This creates a User.
I attach social providers (Google, Facebook, etc). This creates one or more SocialProvider and adds it to user.SocialProviders.
I import my contacts/friends from my attached social providers. This pulls down a list of social contacts and creates a SocialProvider for each one if it does not already exist and adds them to user.SocialContacts. In this case the SocialProvider will not have a parent user that "owns" it, nor will it have an AccessToken until a user signs up with the application and adds a social provider that matches it, in which case those things will be populated.
Will this work? I have a feeling EF is about to get very confused. I'm not very familiar with the fluent API or even most of the data annotations. Any suggestions for what I can do to make this work, or should I just make SocialContacts be a new entity called SocialContact that mostly looks like SocialProvider just to avoid any confusing relationship stuff?
Try this:
User
public class User
{
public int Id { get; set; }
ublic string Password { get; set; }
public string Name { get; set; }
// Collection that represent this user's attached social providers.
public virtual ICollection<SocialProvider> SocialProviders { get; set; }
// Collection representing this user's friends from her attached social providers.
public virtual ICollection<SocialProvider> SocialContacts { get; set; }
}
SocialProvider
public class SocialProvider
{
public int Id { get; set; }
public string AccessToken { get; set; }
[ForeignKey("Provider"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public string ProviderId { get; set; }
public Provider Provider { get; set; }
[ForeignKey("User"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public string UserId { get; set; }
// The user that this SocialProvider belongs to.
public virtual User User { get; set; }
// The users that have this social provider as a contact.
public virtual ICollection<User> Contacts { get; set; }
}
Then in your DbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany<SocialProvider>(r => r.SocialContacts)
.WithMany(u => u.Contacts)
.Map(m =>
{
m.ToTable("UsersInSocialContacts");
m.MapLeftKey("UserId");
m.MapRightKey("ProviderId");//If not ProviderId try Id
});
}
I can't and haven't test this, so let me know the results so i can improve it. Ideally, you may have to pick this design apart and restructure it into one or more entities.
I'm working on a basic application in C# Web.
I would like to have 2 objects:
- User
- Group
Here's the class:
public class User {
public int Id; { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public virtual List<Group> Groups { get; set; }
}
public class Group{
public int Id { get; set; }
public String Name{ get; set; }
public virtual List<User> Users { get; set; }
}
My problem is that when i use this code there's no relation many to many created. I've got a column name "Group_Id" in my table User and a column name "User_Id" in my table Group.
When I use my DbContext class to retrieve Data like this:
List groups = db.Groups.ToList();
The attribute "Users" of all my object in "groups" or set to null. So not load by the database.
Can someone explain me how to make this relation many to many work fine ?
If you are using Entity Framework, use the ObjectQuery<T>.Include method:
List groups = db.Groups.Include("Users").ToList()
A reference to the method on MSDN is here.