tags:

views:

292

answers:

3

I have a GridView bound to an ICollection<UserAnswer> that needs to show two columns:

<asp:GridView ID="UserAnswersGridView" runat="server">
    <Columns>
         <asp:BoundField DataField="Question.Name" HeaderText="Question Name" SortExpression="QuestionID" />
         <asp:BoundField DataField="Score" HeaderText="Score" SortExpression="Score" />
    </Columns>
</asp:GridView>

But I get the error:

A field or property with the name 'Question.Name' was not found on the selected data source.

Each UserAnswer has a QuestionId value which is what I would use to query the question name. In code, I would just call userAssessment.Question.Name, but how would I do this with a bound column in a GridView without creating a new type?

For reference this is the method that returns the data:

public static ICollection<UserAnswer> GetUserAnswers(Int32 userAssessmentId)
{
    DataContext database = new DataContext(GetConnectionString());
    return database.UserAnswers.Where(u => u.UserAssessmentId == userAssessmentId).ToList();
}

Sorry if this explanation isn't very clear!

+2  A: 

I believe that GridView only supports properties of the immediate type. Is repeater or anything similar an option? That gives you more flexibility.

Alternatively, you can add shim properties to the type via a partial class:

namespace YourLinqNamespace {
    partial class UserAnswer {
        public string QuestionName {get {return Question.Name;}}
    }
}

You can't use this in filters (Where etc), but you should be able to use it on returned objects without issue. Note that you might want to use LoadWith to save the round-trips:

DataLoadOptions options = new DataLoadOptions();
options.LoadWith<UserAnswer>(x=>x.Question);
database.LoadOptions = options;
Marc Gravell
I had never heard "shim property" before - very nice
Andrew Hare
shim, pass-thru, pick your own terminology - I roll dice ;-p
Marc Gravell
Cheers, didn't realise I could do this :).
weiran
+1  A: 

You could also use the ItemDataBoundEvent to set the field in code behind. Or you could use an anonymous type this is sort of like Marc's shim property concept, but you wouldn't be modifying your domain model.

 from u in database.UserAnswers
 Where u.UserAssessmentId == userAssessmentId
 select new { QuestionName=u.Question.Name,
              Answer = u.Answer
              //etc etc
            };

As Marc pointed out you shouldn't return an Anonymous type from a method, I've used trick when manually binding to the data source so:

myGrid.DataSource= from u etc....

In my opinion using my first suggestion is better and handle it in the item data bound event.

JoshBerke
Except you can't return an anon-type (cleanly) from GetUserAnswers. You can do it via object, but that isn't pretty.
Marc Gravell
Ohh yea good point...I guess I do this right when I would bind to the data source of the grid...
JoshBerke
+1  A: 

Like Marc Gravell, I'll almost always create shim properties for these cases. Another option would be to used a template column instead of a bound column, but it really depends on your situation:

<asp:TemplateField HeaderText="Question Name">
  <ItemTemplate>
    <%# Eval("Question.Name") %>
  </ItemTemplate>
</asp:TemplateField>
DavGarcia