I am coding an API that uses the Identity feature on asp.net and right now I am trying to make a method that returns all the couples ( user.id, role.id ) which are stored on the table AspNetUserRoles when assigning a role to a user.
One solution would be to get the full list of users and find each role of that user in a double loop but having too much data, it wouldn't be optimal for my case. The perfect solution for me would be to access that table directly which contains exactly what I am looking for
Thanks for the help!
You will have to create class models and relationships explicitly as described in the documentation below:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.1#add-user-and-role-navigation-properties
This documentation expects you to at least create the ApplicationUser, ApplicationRole and ApplicationUserRole class models. Also, you will have to update your DBContext accordingly to correctly use the new models that you create. The names for the models can be changed. However, in any case, this should not change any DB schema and will not require any EF migrations.
Lastly,
Supposing I have two tables Meetings and Attendees.
My database looks like this.
// Table Meeting
Id
Description
// other properties ...
// Attendee
Id
Name
MeetingId
// other properties ...
I have two views that are mapped to these tables. One is ViewMeetings with just the meeting data the other ViewMeetingAttendees joined with Attendees.
I want to use table per type(TPT), mapping each table to a view.
public abstract class MeetingBase
{
// Some properties here
}
public class ViewMeeting : MeetingBase
{
}
public class ViewMeetingAttendee : MeetingBase
{
public String AttendeeName { get;set; }
}
// Configuration
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");
// NOTE fixed the ViewMeeting error as stated in HansVG answer below.
Every time I try to run this code I get an error Invalid Column Name "Discriminator"
I understand that the entity framework is trying to resolve the types as a table per hierarchy(TPH). However, I still want to map the properties using inheritance without the inferred TPH. This is reasonable since all the columns are the same except for one. I have ten other columns and two views. Also I have a single meeting entity needing most of the same columns for CRUD operations.
Is there a way to keep the inheritance but lose the discriminator error? [NotMapped] is not an option since I am still pulling the data from the database. Also, I don't prefer to join the tables locally using LINQ since there are joined entities that don't need to be mapped otherwise.
You defined 'ViewMeeting' twice and didn't configure 'ViewMeetingAttendee'.
Your configuration should be:
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");
I have the following:-
Visual Studio 2013.
i created a new asp.net MVC-5 web project.
the project is using asp.net identity 2.2.
for the authentication method i chose "Individual user accounts"
this process created a new database named aspnet-OurProjectNanme-number
inside the automatically generated database, i have a table named AspNetUSers which store the user info.
now i am working on building an ERP system. and inside the ERP system i want to add the following:-
a table named "Asset" to store the asset info.
the "Asset" table will have 2 columns named "CreatedBy" + "ModifiedBy" which should store the userId who created and modified the asset item.
now i am not sure how i need to achieve this? as i need to add a foreign key between my custom table "Asset" and the "AspNetUsers" table which have been created automatically.. so can i add my custom table "Asset" inside the automatically generated database, and build the foreign key between the Asset.CreatedBy and AspNetUsers.Id ??
if the answer is Yes then can this relation break in the future if we want to upgrade our aspnet identity version ? as upgrading the identity might result in creating new tables or renaming existing ones etc.. which might break the relation between the Asset table and the AspNetUsers table?
If the answer is No (i should not add custom tables inside the automatically generated database ) then how i can build the foreign key ?? and where i need to add the Asset table in this case??
The most common approach to what you want to do is simply to add your additional model as a DbSet in your ApplicationDbContext.
public class Asset
{
public string CreatedBy { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
public IList<Asset> Assets { get; set; }
}
public class ApplicationDbCotext : IdentityDbContext<ApplicationUser>
{
public DbSet<Asset> Assets { get; set; }
}
As I mentioned this is the most common approach as updating the Identity packages should have no impacting affects on your schema. That said you should always test updates before pushing to production.
UPDATE:
Note that when you're working with One to Many relationship's you will see in our Asset model a property for the User Id foreign key as well as the User object. Because of the relationship we are then able to create a List<Asset> in our User to complete the One to Many relationship. This will then allow us to directly query Assets belonging to a User.
As for Code First vs Database First the difference really comes down to how you define the mapping between Entity Framework and the Database.
As I mentioned below there is no one size fits all answer to should you separate the Identity context from your business context, or should you separate them into separate databases. The reality is that only you can answer that question for your needs. It is far more common to have all of the data in a single database. That said, there is something to be said for the security of having identifying information about a user such as their name, email and password hash separated from information like their address or payment information. The trade off is that you can find yourself trying to maintain objects that are supposed to be tied together but are only loosely related because they reside in different databases. Also you would then need to make sure you're using different users/passwords to connect to the different databases, and it's better to have the databases on different servers because if the server gets compromised you went through the entire exercise for nothing. The trade off to get the theoretical security ends up being so impractical with there consistently being another thing you have to do, that you end up seeing everything in one database where you can focus all of your hardening efforts.
Both the ApplicationDbContext and ApplicationUser objects should typically be created for you when you File -> New a project with Individual Authentication. You can add as many properties and relationships to your User as you require.
-- Update --
The answer is growing and growing and so is the discussion. I think I've shown all kinds of variations, which may not have helped to make it understandable. So here is a summary. For explanation, read the full answer and discussion.
Out of the box you have two contexts, identity and business. These are decoupled, so you can change your security without interfering with your business. This way, upgrading security won't break your application or other models. Since the contexts are seperate, changes to either one of them won't affect the other.
As a sidenote: you are not intended to directly access the AspNet identity tables. Implement the UserManager and use the avaiable methods of the manager to perform actions.
Now it comes to logic, where should information be stored? As a simple rule just ask yourself the question: is it part of security or business?
In both contexts you have users. For your requirement this is a logical 1:1 relation. But they are actually seperate. You can create people without supplying a login or delete a login, without deleting a user (people), e.g. for historical reasons.
All you want is to find all information for the current user. So all you need is the People.Id.
Without having to change the IdentityUser you can create the 1:1 relation by just overriding the AspNetUser.Id.
var appUser = new IdentityUser
{
UserName = model.Email,
Email = model.Email,
Id = Convert.ToString(People.Id)
};
var identityResult = await userManager.CreateAsync(appUser, model.Password);
You do not need the identity context for your business. All you need is People.Id. The identity context is only used when tokens are issued and users are created / modified.
To obtain the id use something like this:
var peopleId = int.Parse(Request.User.Identity.GetUserId());
Now you can query your business model using the Id.
When registering, extend the View and ViewModel with the People information you want to store. This will allow you to add both People and AspNetUser at the same time. Though this is not one transaction. But I think it is highly unlikely that creating either one would fail if you perform checks first.
You can validate the username and password (use the methods in the UserManager) and check the ModelState of the viewmodel before creating the user. Use attributes to force Required fields to be filled.
-- Original answer --
In order not to repeat myself, read my answer here.
In short, keep identity and business seperated.
Just in case the identity logic is removed from the same database, like when implementing IdentityServer.
It seems you have business information in AspNetUser. If so, create a Person table and move the information to that table. Relate to that table in your model. In table Person you can add a reference to AspNetUser.
-- update --
I think you understand correctly, but I will just add the details to this answer.
In most cases all tables are defined in one database. But that doesn't mean they are all part of the same model. There can be multiple contexts. In this case one for Identity and one (or more) for Business.
Now why seperate those two? The most important difference between the Business model and Identity model is that Identity tables are not to be called directly. We use the Owin context to call the UserManager / RoleManager.
That is why we cannot add these tables to the business model. Things can be altered in a way that is not secure. Also we do not want the business to have any knowledge about authorization. It shouldn't matter how this is done, as long as a user is identified and authorized.
Also you may want to implement OpenId and claim based authorization. In that case information doesn't have to be available in the database.
The idea is to create a 1:1 relation of the identity table AspNetUsers and business table People. There can be some redundancy, like email or (user)name. But that isn't a problem. The People table should contain all information you want to use in your business model. And the business tables should only relate to People, not AspNetUsers.
Now about the link between AspNetUsers and People. There are four options:
Set People.Id = AspNetUser.Id. Please note that AspNetUser.Id doesn't have to be a GUID. You can add your own value as key.
Set AspNetUser.Id = People.Id.
Add column AspNetUserId to People. No modifications to Identity are needed. You can add People to the Identity Model as well, but I don't think you can create both records in one transaction. You can use User.Identity.GetId() to get AspNetUser.Id. You may however ask yourself if the business should have knowledge about this information.
Add column PeopleId to AspNetUsers. You'll need to extend the IdentityUser to add PeopleId. An advantage is that you don't need the AspNetUser Id, but you can use the actual Id of People. When using OpenId or claims you can get People.Id from claims and you won't have to add AspNetUser.Id to the business. Optionally you can add People to the Model and as navigation property of the extended IdentityUser. When creating the user, you can do this in one transaction.
In case you are creating the user in seperate contexts, you'll need to handle the rollback yourself. But before adding a record to People, you can already test if an AspNetUser can be added: has a valid name/email and password.
Since your business model relates to the People table, you can query all assets and join with the People table for additional information. Or you can get all assets for the current user.
o yes, there are two contexts. The identity model, which contains the AspNet... tables + optionally People. And the business model, which contains all ERP tables + Asset + People.
You may consider to use code first for identity framework 2 and database first for the business model.
I hope that this helps. If not, let's continue in chat.
-- update --
The answer focused on seperation of domains: identity and business. That is why I didn't discuss one possible alternative concerning the AspNetUsers table.
The two models are representations of the database, which means that the database doesn't have to be an exact match. You are free to map tables and fields as you like, as long as they don't break database logic.
Since AspNetusers and People has a 1:1 relation and when both tables are present in the same database, you may as well merge the two into the AspNetUsers table. You can also add relations to the AspNetUsers table, though you may want to add an extra Id (int) column instead of using the current Id (string).
This does not mean the People class can be discarded, except that we have to change the table mapping: AspNetUsers.
Example:
[Table("AspNetUsers")]
public class People
{
[Required]
[StringLength(128)]
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
As you can see, the sensitive fields are not mapped. We need however the Id field. You can now read and update the mapped fields.
You don't have to extend IdentityUser. You can add an AspNetUser and then update the fields using People in the other context. But if you want to add a user in one single transaction it may be easier to extend the IdentityUser (make sure you'll define the new fields in both People and ApplicationUser):
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
There are multiple advantages:
There is only one transaction to add the user.
You cannot expose the sensitive fields since they are not mapped in People.
You cannot add People to the database, since some required fields are not mapped in People.
Please note that this may not work for all types of models (code first/database first + migrations).
for example I want to create two types of users
1. Teacher
2. Student
Teacher having properties of Fname, Lname and Salary made from a MemberType "CollegeMember"
I want to create student user with the properties Fname and Lname only.
So is it possible to create new Member type with using properties of old member type or has any other solution?
Thanks.
Member objects do not quite work like that in Umbraco. You can not inherited member objects in the same way that you can inherit document types or media types.
I would simply create two independent user types here with the properties you require. If you desperately want the inheritance, you could implement your own membership provider or extension to the API, but even then the inheritance would not be visible in Umbraco, only at the code level. In Umbraco the member types would still appear as independent objects. So it seems like little gain.
I want to create a relationship between a custom table (Websites) and the default aspnet tables related to Users.
I'm using code-first so for most FK relationships I would just do
public ModelName ModelName { get; set; }
With this, EF will automatically create the FK relationships. Very easy.
What's confusing is the most effective way to hook into the aspnet users/membership table. Do I create a new model Users that acts as an interface so that I can implement custom user code?
Is there a best way to do this that fits well into EF best practices? I basically just want to relate a user to the Websites table/model so that EF can do its thing.
"Do I create a new model Users that acts as an interface so that I can implement custom user code?"
If you want flexibility, I would say this is the way to go. This way it would be easier if you wanted to change to some sort of different Authentication DB structure in the future.
For example, have an "AppUser" Entity where the corresponding table has a foreign key to the "UserID" column of the aspnet_Membership table. This way you can simply add properties to your "AppUser" Entity instead of trying to change the MS table structure (which can be a real pain). You can still interact with the built-in MS Membership classes and functions from your MVC project using something like the MvcMembership starter Kit DLL's.
https://github.com/TroyGoode/MembershipStarterKit
Hope this helps!
This has few preconditions:
ASP.NET tables must be in the same database as your own tables
Previous precondition means that you must either create your database and tables manually (without automatic code-first generation) or you must use some custom initializer which will add non mapped ASP.NET tables as part of database recreation
If you want your model class to have relation with ASP.NET table you must model ASP.NET table as another entity. I'm not sure if you can use ASP.NET classes for that because for example MembershipUser doesn't have parameterless public constructor which is required for EF. So you will most probably need to create duplicate classes and their mappings and use these classes when referencing ASP.NET entities.