As Remus Rusanu said, you can get the data you want in the format you require by using the PIVOT relational operator, as far as performance of PIVOT is concerned, I've found that it will depend on the indexing of your tables and the variability and size of the data set. I would be greatly interested in hearing more from him about his opinion of PIVOTs as we are all here to learn. There is a great discussion on PIVOT vs JOINS here.
If the DataFields table is a static set then you may not need to worry about generating the SQL dynamically and you can build yourself a stored procedure; if it does vary you may need to take the performance hit of dynamic SQL(here is an excellent article on this) or use a different approach.
Unless you have further need for the data try to keep the returned set to the minimum you need for display it's a good way to reduce overhead as everything will need to go over the network unless your db is on the same physical server as the web server.
Make sure that you perform as few separate data calls as possible will reduce that time you spend raising and dropping connections.
You should always double-check of data calls within a loop when the control for the loop is based on a (probably related?) data set as this screams JOIN.
When you are experimenting with your SQL try to become familiar with execution plans these will help you figure out why you have slow running queries check out these resources for more info.
Whatever you approach you decide you need to figure out where the bottlenecks are in your code, something as basic as stepping through the execution can help with this as it will allow you to see for yourself where problems lie, this will also allow you to identify for yourself possible problems with your approach and build good design choice habits.
Marc Gravel has some interesting points to make about c# data reading here the article is a bit old but worth a read.
PIVOTing your data.(Sorry Remus ;-) )
Bases on the data example you have provided, the following code will get what you need with no in-query recursion:
--Test Data
DECLARE @Users AS TABLE ( Id int
, Username VARCHAR(50)
, Name VARCHAR(50)
, Email VARCHAR(50)
, [Role] INT --Avoid reserved words for column names.
, Active INT --If this is only ever going to be 0 or 1 it should be a bit.
);
DECLARE @DataFields AS TABLE ( Id int
, Name VARCHAR(50)
, [Type] INT --Avoid reserved words for column names.
);
DECLARE @DataFieldsValues AS TABLE ( Id int
, UserId int
, DataFieldId int
, Value VARCHAR(50)
);
INSERT INTO @users ( Id
, Username
, Name
, Email
, [Role]
, Active)
VALUES (1,'enb081','enb081','enb081@mack.com',2,1),
(2,'Mack','Mack','mack@mack.com',1,1),
(3,'Bob','Bobby','bob@mack.com',1,0)
INSERT INTO @DataFields
( Id
, Name
, [Type])
VALUES (1,'DataField1',3),
(2,'DataField2',1),
(3,'DataField3',2),
(4,'DataField4',0)
INSERT INTO @DataFieldsValues
( Id
, UserId
, DataFieldId
, Value)
VALUES (1,1,1,'value11'),
(2,1,2,'value12'),
(3,1,3,'value13'),
(4,1,4,'value14'),
(5,2,1,'value21'),
(6,2,2,'value22'),
(7,2,3,'value23'),
(8,2,4,'value24')
--Query
SELECT *
FROM
( SELECT ut.Username,
df.Name as DataFieldName,
dfv.Value
FROM @Users ut
INNER JOIN @DataFieldsValues dfv
ON ut.Id = dfv.UserId
INNER JOIN @DataFields df
ON dfv.DataFieldId = df.Id) src
PIVOT
( MIN(Value) FOR DataFieldName IN (DataField1, DataField2, DataField3, DataField4)) pvt
--Results
Username DataField1 DataField2 DataField3 DataField4
enb081 value11 value12 value13 value14
Mack value21 value22 value23 value24
The most important thing to remember is to try things out for yourself as whatever we suggest might be altered by factors at your site that we aren't aware of.