How can I add relationships to the aspnetdb database in MVC? - c#

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

Related

Saving and retrieving CreatedBy User information with EF7, MVC6, ASP.NET Identity

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.

Is it possible to implement user profiles without using SimpleMembership?

I need some advice. I'm currently using MVC 4 & SimpleMemberhip with LDAP to authenticate users. The issue is, I don't want to store their usernames and passwords in the
webpages_Membership table due to security concerns. The second issue is I want to provide user-editable profiles.
Here's what works so far:
User logs for the first time and a new entry is created in webpages_Membership
An individualized link to edit the user profile is displayed on the homepage
Username is added to the UserProfiles table when profile is accessed for the first time
Certain user details are fetched from LDAP server and written to profile
Users can then customize their profiles
I'm currently using SimpleMembership with an override to the ValidateUser method. Everything works as it should but I don't need to store the LDAP usernames & passwords. Can this be done?
p.s. I know there is a better way to create new users & profiles besides on first time log in but I'm still working on it.
If you don't want to store the passwords (which SimpleMembership would do by default), you are better off deriving your own custom provider from ExtendedMembershipProvider (or maybe from SimpleMembership, but that would get complex) and write the LDAP implementation, or using one of the ones on NuGet. There's no built-in LDAP support in SimpleMembership, so any approach you do would be a nasty hack which will probably bite you later on.
As for the UserProfile, it doesn't sound like your requirement is that different to the usual UserProfile use case - create custom properties on the UserProfile model, update the database accordingly, and build a UI to allow the user to edit whichever of those properties they should be able to directly edit.
(edit)
Footnote. In my answer to "How do I use my own database with SimpleMembership and WebSecurity? What is MVC4 security all about?" I examine the history of membership, how ExtendedMembershipProvider fits into this, and how the new classes such as WebSecurity work on the basis of a provider being a concrete implementation of ExtendedMembershipProvider (which SimpleMembershipProvider is, for example). For anyone looking to derive their own provider to use with WebSecurity, that answer is worth reading.
I've managed to bypass storing user details in the Membership provider by creating the required tables with Code First. I'm now able to create new users and store them in the UserProfile table.

ASP .NET Membership customizing

After I was learning about ASP .NET Membership built-in framework I have decided that this stuff is almost suitable for me. But there are couple features and edits I would like to have:
Two step registration: after user have typed all account information, verification letter should be send to typed email. Before email address is verified it impossible to log in (some message should appear, telling user that email verification is needed before it's allowed to use account).
Membership DB Scheme:
There is no need to store user question for restoring password.
Illegal attempts to login is uneccessary.
Default aspnet_ prefix is likely to be changed.
... and so on
For the first item I know that I could use own class derived from SqlMembershipProvider. Am I right about this? Could you point me at some good post where I could get learned.
For the second improvement it's seems like a trouble. In this book I have read that isn't so easy:
• The built-in SQL storage providers need direct access to your database, which
feels a bit dirty if you have a strong concept of a domain model or use a particular
ORM technology elsewhere.
• The built-in SQL storage providers demand a specific data schema
that isn’t easy to share with the rest of your application’s data
schema.
The biggest problem I've encountered with subclassing SqlMembershipProvider is it doesn't give you the connection string to work with. You have to hack the class to pieces to get anything useful for the way most modern login systems work.
I'm not sure about the database tables names - I don't think that's controlled by the SqlMembershipProvider but is actually inside an ASP.NET installer class.
My advice would be to create your own from scratch, and use the built in FormsAuthentication helpers. It's really not a big task compared to hours of annoyance having to conform to the providers. I did this with Roadkill after going down the Membership Provider route, and discovering it a cul-de-sac particularly for Active Directory support.
You can completely control your membership DB schema by Implementing Custom Membership User (of course you also need to implement Membership Provider for the User).
Customize user creation steps by configuring CreateUserWizard control. You will change its' template and handle events, I don't think you need to override it.

ASP.NET: Custom MembershipProvider with a custom user table

I've recently started tinkering with ASP.NET MVC, but this question should apply to classic ASP.NET as well. For what it's worth, I don't know very much about forms authentication and membership providers either.
I'm trying to write my own MembershipProvider which will be connected to my own custom user table in my database. My user table contains all of the basic user information such as usernames, passwords, password salts, e-mail addresses and so on, but also information such as first name, last name and country of residence.
As far as I understand, the standard way of doing this in ASP.NET is to create a user table
without the extra information and then a "profile" table with the extra information. However, this doesn't sound very good to me, because whenever I need to access that extra information I would have to make one extra database query to get it.
I read in the book "Pro ASP.NET 3.5 in C# 2008" that having a separate table for the profiles is not a very good idea if you need to access the profile table a lot and have many different pages in your website.
Now for the problem at hand... As I said, I'm writing my own custom MembershipProvider subclass and it's going pretty well so far, but now I've come to realize that the CreateUser doesn't allow me to create users in the way I'd like. The method only takes a fixed number of arguments and first name, last name and country of residence are not part of them.
So how would I create an entry for the new user in my custom table without this information at hand in CreateUser of my MembershipProvider?
I think you should go on with your approach and add a new function in your implementation, I mean, overload the CreateUser method and have a CustomMembershipUser (that extends the MembershipUser) as a parameter.
In that way, before using the provider, cast it to your CustomMembershipProvider and use the overloaded method.
I agree with your analysis that you should keep both membership and profile information in the same table. Since you are correct that you are restricted by the number of parameters that CreateUser takes, you will need to design your field so that non-membership profile attributes are nullable. This does not mean that you will have required fields that are null in the database, however. Instead, you can you the below snippet:
string username = .../ retrieve username here
Membership.CreateUser(username , password, email);
ProfileBase newProfile = Profile.Create(username); //since the user has just been created, all properties will be blank
//set all entered properties
newProfile.SetPropertyValue("MyProp1", myProp1Value);
...
newProfile.SetPropertyValue("MyPropN", myPropNValue);
newProfile.Save();
In this way, you leverage ASP.NET's membership providers to create the user and save profile data, but to your end user it is a single atomic operation.

Do most people use .NET's SqlMembershipProvider, SqlRoleProvider, and SqlProfileProvider?

Do most people use .NET's SqlMembershipProvider, SqlRoleProvider, and SqlProfileProvider when developing a site with membership capabilities?
Or do many people make their own providers, or even their own membership systems entirely?
What are the limitations of the SQL providers that would make you roll your own?
Is it easy to extend the SQL providers to provide additional functionality?
For Reference
Per Scott Gu's Blog, Microsoft provides the source code for the SqlMembershipProvider so that you can customize it, instead of starting from scratch. Just an FYI.
We use everything except the Profile Provider. The Profile Provider is completly text based and does full text seearches - this becomes exceedingly slow as you user base gets larger. We have found it a much better solution to "role our own" profile section of the membership api database that is keyed to the userid in membership.
I've rolled my own MembershipProvider classes using derived MembershipUser types to wrap the custom user schema, so profile-style properties are now available everywhere as part of the derived user via a cast.
I normally use the providers that come out of the box, the main problem I have is querying across profile attributes across users. For example finding all users that have a profile attribute called Car that equals true. This is down to the way they are stored in the underlying structure.
I've used SqlMembership before and it's quite nice, unless you need something custom. I remember needing something like firstname and lastname info and I realised there're no fields for that. In the end instead of extending I've used Comment field of the provider and added name info to there. This is probably a bad practice/lazy/hack way but it worked for me in a tight situation..
In theory they sound nice, but not a chance if you do any unit testing without creating lots of abstract wrappers.
If you only need the basic user support (roles, profiles, etc.) then the default providers will work great.
If you need more customized support (data storage in a database not supported by the default providers [like Oracle], provider on a database that already exists, a heavily customized schema) then you should roll your own providers.
As for me, my current site only needed basic Roles support (and minimal Profiles support), so I went with the default providers.
I have used both the custom classes and built in. When you need to get to a different database or schema or need to have extra info.
I abstracted out the layers so that it would work on the logic layer and have a DAL layer that used the data.common.dbprovider bit so it was reasonably generic.

Categories