ASP.NET’s Profile DB schema
ASP.NET has built-in functionality to store profile information about a user. The DB table schema has several design trade-offs that make it somewhat inflexible for certain uses.
ASP.NET will write a custom class, given the properties you want, such as name and birthdate. It will also take care of hooking the plumbing up in the database (there is a little script to create the profile tables in the database). It then stores and retrieves the data on subsequent requests. The feature can handle both text and binary data, but for simplicity’s sake, I’ll just ignore the binary. Since the profile could contain arbitrary information, the table has to be designed to accommodate any type of data — essentially name/value pairs. Here’s the table schema:
CREATE TABLE dbo.aspnet_Profile (
UserId uniqueidentifier NOT NULL PRIMARY KEY CLUSTERED,
PropertyNames ntext NOT NULL,
PropertyValuesString ntext NOT NULL,
PropertyValuesBinary image NOT NULL,
LastUpdatedDate datetime NOT NULL
)
Hmmm, that’s an interesting schema. How do you store name/value pairs in that? I’d expect to see a UserID column and a Name column, with the primary key on UserID and Name, but it looks like they must be storing the data another way. For one thing, there can’t be multiple rows per user — all the values have to be in one row. I could see someone arguing that’s a good idea, because it would keep the data all on one page — but the columns are ntext and image so they’re not stored in-page anyway. That results in a compact table, with a small clustered index to seek for the user’s row, but then the DB has to seek to other pages and find the data stored in those three columns. So how is the data stored?
select top 1 UserId, PropertyNames, PropertyValuesString from aspnet_Profile;
Results:
| UserId | PropertyNames | PropertyValuesString |
|---|---|---|
| 017D…[snip]… | User.LName:S:0:5:User.FName:S:5:4: | SmithJohn |
Yuck! So the object just dehydrates itself in a similar fashion as PHP’s serialize and re-writes the entire row whenever it saves itself into the database (I’m guessing it re-writes the entire row; perhaps it’s smart enough to know that the binary data doesn’t need to be re-written if only the text has changed, though the design doesn’t instill much confidence about that). This is a very bad design. The table isn’t even first normal form. There is also no decent way to use this data except through the Profile objects. I can’t grab the data and query it for reports or whatnot. And finally, those ubiquitous Microsoft uniqueidentifier 128-bit surrogate keys are rearing their ugly heads.
I’m surprised and nonplussed. After all the gazillions of dollars that went into ASP.NET 2.0… I’ll give them some credit and say “it’s good that they found a way to store the data in the table without customizing the table schema based on the desired profile properties,” but this design is barely a step up from that. This schema is missing all the obvious benefits of normalization.


Normalization is a funny thing for me. I usually take it for granted, because OOP comes naturally to me. When I first took a databases class, I didn’t understand the first-, second-, and third-normal forms — I thought it was something you were supposed to know how to do by “instinct”, like preferring constants over “magic numbers” in a program. (This is not to claim that I do it right — just that I do it…)
That being said, I’m not clear on exactly why this isn’t normalized. That’s no doubt because I’m not clear on what each column is supposed to represent.
I’m not familiar with the PHP serialize function. But it looks like it stores the variable name, type, start, and length? So, “S” means string data and “B” means binary data, telling you which of PropertyValuesString and PropertyValuesBinary to look in?
So I guess one solution would be to have UserID, PropertyName, PropertyValue, with a composite primary key on the UserID and PropertyName. PropertyValue would be binary data, interpreted as ASCII or image or whatever by the server code.
Tim McCormack
9 Jan 06 at 2:45 am
One of the nice things about the Profile system (as well as the Login, Roles and Site Navigation system) is that it is pluggable via a provider model. What this means is that you can replace the schema and back-end implementations with your own preference.
We are actually going to be publishing a cool sample/implementation later this week that shows how to map the profile object directly against a table in your database. If you want to bind your profile against a normalized table, that might be the easiest approach to take — you should be able to just download it, updated your provider definition in your web.config file, and away you go (no code changes required). If you want to customize it further, you can either write your own provider, or take the source in the same and tweak it to map however you want.
I’ll be posting a link to the new Profile download on my blog later this week once it is out: http://weblogs.asp.net/scottgu
Hope this helps,
Scott
ScottGu
9 Jan 06 at 9:54 am
Tim: one of the requirements of first normal form says separate bits of data need to be stored in separate columns. I should have linked to it before, but you can find lots of great explanations of normal forms elsewhere on the web.
Scott: my main point was the default provider should be written differently. I’m not making the decisions and I don’t know what thinking goes into them, but if MS provides default functionality, it would be better for it to be more useful for the people who really need it, rather than just a “code sample” — which is what it feels like right now. The default implementation isn’t very useful for anyone building a serious site in this case. ASP.NET certainly isn’t aimed at script kiddies, so I’d expect it to ship with a Profile feature designed for sites that want to do big business. If the table schema were
UserID,PropertyName,PropertyValuewith a clustered primary key on UserID/PropertyName, and binary values encoded in base64 or something, that would be much more useful out of the box for a serious website.That’s just a suggestion — there are lots of ways it could be done. My point is, any serious enterprise needs to use that data in lots of places, not just in the ASP.NET code. I just can’t imagine a website not wanting to analyze it to capture metrics about user behavior, site and page effectiveness and so forth. That requirement is going to be mandatory up-front, so immediately the default provider gets ruled out, and you have to write your own. I know you do that reasonably easily, but I think you could help a lot of people not have to do it at all. Maybe the functionality you allude to will fill that gap nicely, but it’s not “in the box” with the rest of the ASP.NET bits.
Thanks for commenting, both of you!
Xaprb
9 Jan 06 at 10:08 am
Hi Xaprb,
Unfortunately there are two downsides with requiring that the schema be defined as a database table up-front:
Having said that — I do wish we had a table-backed profile provider in the box. Hopefully this web-download will be easy for people to find and use. There are no code changes to use it (just re-configure the provider) — so it should be easy to enable.
Hope this helps,
Scott
ScottGu
9 Jan 06 at 10:20 pm
Scott: thanks for providing more details. You have a very good point about developing the site, then switching over to a custom provider. I’m still not convinced that we’re talking about exactly the same thing, so I’ll draw some tables. Here’s how I might create the schema if I knew my requirements would never change, ever:
create table aspnet_Profile ( UserID uniqueidentifier primary key, FirstName varchar(50), LastName varchar(50), OtherColumns... )I think we’re on the same page so far. But if I were Microsoft, I might consider the following, or something similar, for the default provider:
create table aspnet_Profile ( UserID uniqueidentifier, PropertyName varchar(50), PropertyValue varchar(8000), primary key (UserID, PropertyName) )I’m waving my hands a bit because I’m not intimately familiar with ASP.NET’s internals, and I know there are probably lots of other factors I’m not thinking of. With the first schema, my data would look like
That would be ideal if you knew ahead of time the exact data types and columns needed. If you don’t, which Microsoft obviously doesn’t when they ship ASP.NET, I think the second might be better than the current default. In that case, the data would look like
Hopefully that makes my objection clearer. Microsoft can’t design the first schema, because they don’t know the requirements, but the second one might be possible, and might be superior. The schema is fully normalized, so things other than the Profile functionality could consume it. And then if you decided later to de-normalize into the first schema I’m proposing in this comment, it would be easy to re-code any stuff already written, migrate existing data, etc etc. The default schema presents some pretty serious obstacles to any of the things I just mentioned. Data migration alone would be a heck of a job!
I haven’t really responded to what I think you raised as the main issue with this approach, which is “profile data is often a bunch of blobby stuff, not just neat name/value pairs.” I’ll admit that never crossed my mind! Is there a happy(er) medium?
Thanks again for the lively and interesting discussion on this.
Xaprb
9 Jan 06 at 10:45 pm
i’m gettin crazy with this :(
i’ve foud some articles but it’s a mad idea to download code to manage this creazy way to organize user’s table
Coding it’s not getting easyer :O
http://www.asp.net/sandbox/samp_profiles.aspx?tabindex=0&tabid=1
Marco
6 Jun 07 at 10:39 am