Querying Profile Values from default Profile Provider
Hey all,
As most of you know if you’ve been involved with the new Membership features of .NET 2.0, there’s a handy way to store user specific settings called the Profile. This allows you to specify in your config files certain attributes you wish to store on a per user basis. At compile time, the .NET compiler will adjust its native profile object to account for the profile properties you configure so that you can access them directly with code.
Let’s assume that you are going to store First Name and Last Name on the user profile. Your configuration would look like this:
<system.web>
<profile>
<properties>
<add name=”FirstName” type=”System.String”/>
<add name=”LastName” type=”System.String”/>
</properties>
</system.web>
Once that was done, you could reference those properties directly off of your code:
protected void Page_Load(object sender, EventArgs e)
{
Profile.FirstName = ‘John’;
Profile.LastName = ‘Smith’;
}
However, there’s a small problem with doing this, and it’s in how the values are stored in the data store that you are using for your Membership and Profile providers. The names and values of the profile properties are stored in a formatted string format like so:
The propertynames field:
FirstName:S:0:4:LastName:S:4:5
The propertyvalues field:
JohnSmith
In essence, the propertynames field contains the field name, the datatype, the index, and the length of each field as it exists in the propertyvalues field.
This makes it very difficult to pull data out with simple Sql.
There are several alternatives, including using a SqlTableProvider (found at http://www.asp.net/sandbox/samp_profiles.aspx?tabindex=0&tabid=1), and those are useful if you are looking to do a full blown search mechanism. But what if you don’t want to replace your entire provider model and do a conversion, and only want to see what data you have?
One thing you can do is construct a user-defined Sql function to parse the propertynames field, and then use that parsing to do an appropriate substring on the propertyvalues field. Here’s an example.
I’m going to build a user-defined function that takes the text I wish to parse, the field name I am looking for, and the indexed value I am after. In this example, if I pass in an indexed value of ‘2’ I will get the datatype, ‘3’ will return me the start position, and ‘4’ will get the field length.
CREATE FUNCTION [dbo].[GetProfileParameters](@text varchar(8000), @searchKey varchar(8000), @returnIndex int)
RETURNS varchar(8000)
AS
BEGIN
declare @keyfound bit
set @keyfound = 0
DECLARE @pos smallint,
@index smallint,
@i smallint,
@j smallint,
@s varchar(8000)
SET @pos = 1
set @index = 1
WHILE @pos <= LEN(@text)
BEGIN
SET @i = CHARINDEX(‘ ‘, @text, @pos)
SET @j = CHARINDEX(‘:’, @text, @pos)
IF @i > 0 OR @j > 0
BEGIN
IF @i = 0 OR (@j > 0 AND @j < @i)
SET @i = @j
IF @i > @pos
BEGIN
— @i now holds the earliest delimiter in the string
SET @s = SUBSTRING(@text, @pos, @i – @pos)
if (@s = @searchKey)
begin
–found the key
set @keyfound = 1
end
if (@keyfound = 1 and @index = @returnIndex)
begin
RETURN @s
end
set @index = @index + 1
if (@index > 4)
set @index = 1
END
SET @pos = @i + 1
WHILE @pos < LEN(@text) AND SUBSTRING(@text, @pos, 1) IN (‘ ‘, ‘:’)
SET @pos = @pos + 1
END
ELSE
BEGIN
if (@s = @searchKey)
begin
–found the key
set @keyfound = 1
end
if (@keyfound = 1 and @index = @returnIndex)
begin
RETURN @s
end
set @index = @index + 1
if (@index > 4)
set @index = 1
SET @pos = LEN(@text) + 1
END
END
RETURN ”
end
Now that we have that, we can get the start index and length of the field we want and get it out of the table by using a substring:
select substring(propertyvaluesstring, convert(int, dbo.getprofileparameters(propertynames, ‘FirstName’, 3)) + 1, convert(int, dbo.getprofileparameters(propertynames, ‘FirstName’, 4))) as FieldValue from aspnet_Profile
That will give us all of the first names in the user profiles by doing an appropriate substring on the propertyvalues field, using the data provided in the propertynames field.
Happy querying!
Rob
SqlBulkCopy
If you are ever in a scenario where you are going to be doing tons of repetitive inserts, consider using the new SqlBulkCopy command that’s found in .NET 2.0. It uses an insert bulk to do all of your inserts in a single round trip, and has settings which you can use to batch up or break up the inserts if needed, and a timeout setting as well. The potential performance savings are huge, especially if you are doing things like importing data from outside vendors or processing data submissions from third parties via FTP or web services.
Here’s an example of how it’s used.
First you would create a bulk copy object given the connection string.
SqlBulkCopy oBulk = new SqlBulkCopy(yourconnectionstring);
Then you would create a datatable to import…in this case I’ll call some DB method
DataTable dtBulk = GetAllZipsOutMyDatabase();
Next set the destination table name where you want the bulk copy to go:
oBulk.DestinationTableName = “MyDestinationTable”
Now this next step is important, IF the columns in your datatable do not exactly match the column structure in the destination table. If your local datatable is structured exactly like the destination table, meaning all of the columns are represented and they are the correct datatype, you can do without this step. However, if your table has an identity column, or some columns are not represented, you MUST map your columns to the destination table. In this example I am using the names of the local datatable columns and mapping them onto the destination table, since I named my local datatable columns the same as they are in the destination table.
oBulk.ColumnMappings.Clear();
foreach(DataColumn oCol in dtBulk.Columns)
{
oBulk.ColumnMappings.Add(new SqlBulkCopyColumnMapping(oCol.ColumnName, oCol.ColumnName));
}
Now that that’s done I can set the batch size and the timeout:
oBulk.BatchSize = 1000;
oBulk.BulkCopyTimeout = 6000;
Now I’m all set up and I can write to the server.
oBulk.WriteToServer(dtBulk);
And that’s it. Rather than looping on a bunch of inserts, I can do all of the inserts in a single round trip.
Happy bulk copying,
Rob
SLO .NET User Group has first meeting…and gets recognized by INETA
Hi everyone,
Sorry I’ve been a bit silent lately, but I went to VSLive! in Boston the last week in October and spent some time on either side of the conference visiting my family up in New Hampshire. Then I came back and it took me a bit to catch up with everything. One of the things I had to catch up on was my first User Group meeting.
Last night the first meeting was held and by all accounts is was a great success. 10 people showed up for pizza, lively discussion, and networking. I wasn’t sure what to expect, but I was very pleased with the turnout and everyone’s interaction.
I give myself a B on my presentation. I tried to pick an interesting topic, but I didn’t have a whole lot of time to prepare and so I was a bit slow during the code samples. But it was my first time through the talk so I already know what I need to do to improve on it. Plus I got some good feedback on the talk from the group.
On top of that, today INETA, the International .NET Association, marked our group as Active and now it can be found on their site. Plus we get access to speakers and I get to put the logo on the UG website. Needless to say, I’m very excited about this turn of events.
I hope to get back to my blog topics pretty soon…going to try and get a synopsis of my last talk up here pretty quickly.
Talk to you later!
Rob
