We have an existing ASP.net Core 2.1 Application which has UserAccounts associated with the Identity-System.
Now we would like to add Admin-Users which shall not share the UserAccountsTable which is already used by the UserAccounts. (Yeah i know, we could simply add a bool column like isAdmin but we opted for seperated tables).
So my thinking was, that i need to create a new Identity-Instance which is using our AdminUser and AdminRole classes (Both deriving from IdentityUser and IdentityRole accordingly).
In the DBContext i can now change the Table-Names via the Fluent-API of AdminUser and AdminRole. But how would i now change the names of the needed "infrastructure" tables created by Identity automatically?
I've found this documentation by Microsoft. But they are only using "generic" types to rename the tables for e.G. the Link-Table between Users and Roles (IdentityUserRole). This type would probably "conflict" with my already configured UserAccounts-Identity, therefore "renaming" both tables, making troubles again, or not?
An obvious solution could be to create a new AdminUserContext, which would not share the UserAccounts-Information. But then we would lose the Links to the Entities we actually would want to administrate? And linking DBContexts doesn't feel "right" to me.
Anybody got some ideas to this?
Related
My solution is based on Clean Architecture.
The Domain project should be completely ignorant about the Infrasctrure layer.
The Infrastructure contains a Data Project and an Identity Project.
The Data Project is supposed to implement my repositories. It contains a class called ApplicationDbContext that inherits from DbContext;
The Identity Project is supposed to implement the AspNet Core Identity and all its needs. It contains a class called PortalUser that inherits from IdentityUser and also contains a class called ApplicationIdentityDbContext that inherits from IdentityDbContext;
In the Domain, I have an Entity called User.
User is referenced by several other Entities, like Company (A company has several users).
I'd like to have only one table for both my entities User and the PortalUser.
When I try to apply the migrations, I have several problems referencing User.
What I tried so far:
Create an owned Property called User in PortalUser. But I can't map User in Company entity to an Owned Entity;
Map both User and Portal User to the same table called User, using different DbContexts. But when I apply the migrations to the Database, the migration fails and says that "A table User already exists";
Implement all properties from User in the PortalUser. But when adding a migration it fails and says that other entities like Company can't refer to User.
Make ApplicationDbUser inherit from ApplicationIdentityDbUser but two problems: 1st, it's conceptually wrong to the Clean Architecture; 2nd, The same issues related to having only the PortalUser being created and all other entities referencing User.
I'm stuck on this problem and the project is not going far. All examples that I found so far are raw and don't show what happens when I have navigation properties referencing the User.
The better solution I could think of so far is to have two different tables, one for Identity and another for user needs and references. However, I'm migrating this solution from a messy solution to a more well-organized one, using Clean Architecture. The old version uses only one table for both situations and I can't have a new table and migrate the data.
PLEASE, anyone knows how to solve this problem?
I have 2 questions... I am using the Identity Framework and the Entity Framework for storing user data.
The default name of the User-Table is "AspNetUsers". Normally I use data annotations to change the table name to "Users" with:
[Table("Users")]
But this does not work here. Instead, I have to use the fluent API to achieve my goal:
modelBuilder.Entity<User>().ToTable("Users");
This works perfectly fine, but it's not the preferred way since I want to have my code consistent and renaming tables works fine with other data-classes with data annotations.
2) When I have changed the table-name via fluent-API I got another problem. The UserManager of the Identity-Framework can't find the table anymore. Is the table name hard-wired in the code? Can't believe that!?
Every time I am using the UserManager, for example, to create a new user, I get an exception that the table "AspNetUsers" can't be found. How can I fix that?
I am using the latest MVC6 and Entity Framework 7, however I am sure many techniques used in MVC5 and Entity Framework 6 could help answer my question also.
Almost all of the tables in my database have the following 4 fields for auditing: CreatedDate, CreatedBy, ModifiedDate, ModifiedBy.
I am trying to figure out which field from the built in IdentityUser (AspNetUsers table) I should be storing in the CreatedBy field when saving items to the database.
I started by trying to use 'Username' since it is easily accessible by calling User.Identity.Name and passing it down to the repository when saving. Here is how I configured EF using Fluent API to help with retrieving the User who created an item along with all of their fields:
builder.Entity<BlogPost>()
.Property(bp => bp.CreatedBy)
.HasMaxLength(256);
builder.Entity<BlogPost>()
.HasOne(bp => bp.CreatedByUser)
.WithMany()
.HasForeignKey(bp => bp.CreatedBy)
.HasPrincipalKey(u => u.UserName);
But then I noticed that Entity Framework yells when trying to add migrations and create the database because Username needs to be set as a primary key or it can't be used as a foreign key in another table.
Then I got to the point where I figured I would just use the actual Id which is a GUID. The problem with this technique is that the Id of the current logged in user isn't easily available when trying to save: 'User.Identity.Name' is all that is there.
Here are a couple of questions that I would like someone with EF and Auditing experience to try and answer for me:
Do people even use Audit fields in all of their tables anymore? I don't see many others asking questions about this and Microsoft definitely doesn't make it easy to work with their new Identity system and custom audit fields.
Should I be storing Username or Id in my CreatedBy field. Some say this might be preference but I really want to know what direction Microsoft might be pushing with the new Identity. The problem with storing Id is that it is hard to get it when saving and the problem with storing Username is that it isn't a primary key in AspNetUsers table.
I really would just like to know of a good pattern in general when using EF that handles auditing when saving, and retrieving the User and setting it as a Navigation property on my entities that need it when pulling records from the database.
I am trying to figure out which field from the built in IdentityUser
(AspNetUsers table) I should be storing in the CreatedBy field when
saving items to the database.
The user name of the person using the site (on Thread.CurrentPrincipal, accessed in ASP.Net via User.Identity.Name). Who else?
That is the identity of the current authenticated user using your site, and it is what you should be putting in the audit tables.
Do people even use Audit fields in all of their tables anymore? I
don't see many others asking questions about this and Microsoft
definitely doesn't make it easy to work with their new Identity system
and custom audit fields.
Yes, people do! All the time! If you are storing data that can be edited in any way by an end user (whether they are in your company or not), audit it. Always. I was going to say that all enterprises audit stuff to the extreme (and they do), but I even do that on my own personal projects. Metrics are extremely important!
And one important thing to remember is that just because people aren't asking about it on StackOverflow or some other site doesn't mean that it isn't prevalent and critical in our industry.
Should I be storing Username or Id in my CreatedBy field. Some say
this might be preference but I really want to know what direction
Microsoft might be pushing with the new Identity
Microsoft (and the team behind their Identity framework) are doing a great job with providing us a secure and robust security framework. Maybe they would recommend their approach to this problem, but their framework isn't really meant to address those nuances (which can and will differ from system to system). At the end of the day, pick whichever suits the schema of your database. I think most of the time that the Username would be appropriate to store (if it is unique within your system). After all, they both represent the same information (unless your usernames are not unique, which begs further questions).
I really would just like to know of a good pattern in general when
using EF that handles auditing when saving, and retrieving the User
and setting it as a navigation property on my entities that need it
when pulling records from the database.
It is not, and most likely never will be EF's concern to help you with something like this. Sorry, that's just the way it is. Each application is unique, and EF (or any other ORM) can't be expected to meet everyone's needs.
I realize all of this doesn't really provide you with concrete answers, but I had to drop some advice.
I have two tables, Users and TempUsers and I need to do operations on both of them. I already have my users type defined and I want to add it to the DbContext for both tables. Problem is, it either uses convention to map the type name to a table or using the TableAttribute with the table name specified. Either way I can't see how to add two dbsets mapping the type to different table names.
I could duplicate the type using either copy + paste or through a UserBase class and two derived User and TempUser classes. Both ways will work but really in the code I want to deal with Users and not have the complexity of Users and TempUsers in the code. After all it's the repository's responsibly to deal with where to put the user objects and the business logic shouldn't have to deal with it.
Advice would be much appreciated. Thanks!
[Explanation Based On Comments]
The reason I have two tables is because the TempUsers is to support a bulk import/update but though atomic transactions on each user. So externally some active directory export or some such will result in calling a service for each user. I have to create/update users and figure out what ones are not being imported but already exist in my database and then delete them. Would be much simpler to truncate the Users table and write directly to that but the Id's would be different and it would break all the links the users have to different tables, like shipping history for example.
That is not possible. EF cannot map same class twice within same context. Your single User class can be only mapped to Users table or TempUsers table in single context type. You need either two user classes or two different context types (with different mapping configuration) - one providing access to Users table and second providing access to TempUsers table.
This reminds me of my own question: Querying data using Entity Framework from dynamically created table
Actually, you can use EF and ExecuteStoreQuery to retreive objects, but you cannot use the LINQ features of EF.
I'm having a hard time wrapping my head around how to use the Memberships in MVC. I know there is the built in ASPNETDB database which has all the basic tables for users and such. But what if I wanted to add a relationship between one of my custom tables and this built in user table?
If I had a database table that contained blog comments, we'll call it Comment. And each Comment had a userID associated with it could I do something like?
User.Comments.Add(someCommentObj)
Anyone know of a good article on this? Is this even possible?
Thanks
Have a look at this extensive article on the MembershipProvider:
https://web.archive.org/web/20211020202857/http://www.4guysfromrolla.com/articles/120705-1.aspx
Look at Part 6 and 7, you'll probably want to implement a custom ProfileProvider and store the comment reference in the Profile.
Part 6 - capture additional user-specific information using the
Profile system. Learn about the
built-in SqlProfileProvider.
Part 7 - the Membership, Roles, and Profile systems are all build using
the provider model, which allows for
their implementations to be highly
If you want to use your own custom membership tables then you'll need to build your own MembershipProvider. Matt Wrock has a walkthrough:
You'll notice that the default AccountModel allows you to inject your own provider:
public AccountMembershipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
Nerdinner has an example of dependency injection that you would probably find useful:
Warning Here are two solutions that will work. The first one is easy. The 2nd one, I think is what you're after, but take it for what it's worth. Make sure you realize what you're doing, since this will take the membership provider data and access it directly, which could result in some hidden bombs if you're not careful (like deleting data).
The membership data is meant to just be used for authenticating; roles for authorizing; profiles for user speicific data (like time zone or favorite color.
Solution One
If you wanted to add a comment under the current user (or any user) you could do:
var comment = new Comment(....);
comment.userId = User.Identity.Name; //for user name
or
comment.userId = new Guid(Membership.GetUser().ProviderUserKey); //for guid in table
That's the eays way and you never really have direct access to the aspnet tables, you just use its info.
Solution Two
I'm assuming that you're using L2S and the designer in VS.
By adding the membership table(s) to your L2S design, you will get access to its data. This may even be preferable for some quick querying (like dates, lock out info, etc. since you don't have to use the built-in sprocs which have some serious over-kill and heavy code). If you create a relationship in the DB or in the L2S designer, you'll have a relationship that you can access like your question asks.
At this point, the designer has created your classes for comments and users. If you do anything with the actual user table you just created, you're circumventing the membership provider's design - don't do this unless you now what you're doing. When you add a comment to the User, it will add the comment to the comments tables with the correct relationship intact.
You should now be able to do:
var user = MyUser.GetById(userId);
user.Comments.Add(comment);
Remember, that the User in this case is different than when you do
var user = Membership.GetUser(userId);