ECO, LINQ, Anonymous types, and Web Extensions
I’ve been finding LINQ + Anonymous types really compliment ECO and the new ASP web extensions approach to writing websites. I may have mentioned recently that I don’t like the idea of passing instances of my business objects to the presentation layer. The reason is that someone else will be writing the views for this site and I want to be able to control what they are capable of displaying. It’s not just that though, the fact is that your view might need to look completely different to how your business classes are structured, one layer should not dictate the structure of another.
The example I am about to show does in fact have similar structures for the view and model. Having said that there is a slight difference in that the MinorVersion class has its own "int VersionNumber" property, and gets the major part of the version number from self.MajorVersion.VersionNumber. Anyway, now to get on with it.
My requirement was to show all major versions, within each major version show each minor version, and within each minor version show a list of what’s new. In addition, a minor version should only be displayed if its status is #Released, and a major version should not be displayed if it has no minor versions which meet this criteria.
The following code generates a structure like so
and stores the resulting anonymous type into the ViewData for my view to render.
The code behind of my view reads like this:
And finally I use nested ASP:Repeater tags to render the nested HTML.
I think the point is that there is just no need to pass your business class instances through to the UI layer. In fact if you later changed the structure of your business classes this LINQ would no longer compile, whereas the markup in the view is evaluated at runtime so you wouldn’t spot an error here until you tried to view the page.
The example I am about to show does in fact have similar structures for the view and model. Having said that there is a slight difference in that the MinorVersion class has its own "int VersionNumber" property, and gets the major part of the version number from self.MajorVersion.VersionNumber. Anyway, now to get on with it.
My requirement was to show all major versions, within each major version show each minor version, and within each minor version show a list of what’s new. In addition, a minor version should only be displayed if its status is #Released, and a major version should not be displayed if it has no minor versions which meet this criteria.
The following code generates a structure like so
MajorVersion (VersionNumber)
1..* MinorVersion (MajorVersionNumber, MinorVersionNumber)
1..* WhatsNew (ID, Headline)
and stores the resulting anonymous type into the ViewData for my view to render.
ViewData[GlobalViewDataKeys.WhatsNewKeys.WhatsNewList] =
from majorVersion in software.MajorVersions
where
(from minorVersionCheck in majorVersion.MinorVersions
where minorVersionCheck.Status == MinorVersionStatus.Released select minorVersionCheck ).Count() > 0
select
new
{
VersionNumber = majorVersion.VersionNumber,
MinorVersions =
from minorVersion in majorVersion.MinorVersions
select
new
{
MajorVersionNumber = majorVersion.VersionNumber,
VersionNumber = minorVersion.VersionNumber,
WhatsNew =
from whatsNew in minorVersion.WhatsNew
select
new
{
ID = whatsNew.ID,
Headline = whatsNew.Headline
}
}
};
RenderView("AllHistory");
The code behind of my view reads like this:
protected void Page_Load(object sender, EventArgs e)
{
MajorVersionRepeater.DataSource = ViewData[GlobalViewDataKeys.WhatsNewKeys.WhatsNewList];
MajorVersionRepeater.DataBind();
}
And finally I use nested ASP:Repeater tags to render the nested HTML.
<ul class="AllHistoryMajorVersionList">
<asp:Repeater id="MajorVersionRepeater" runat="server">
<ItemTemplate>
<li>
Major version
<%# DataBinder.Eval(Container.DataItem, "VersionNumber") %>
<ul class="AllHistoryMinorVersionList">
<asp:Repeater
id="MinorVersionRepeater"
DataSource=’<%# DataBinder.Eval(Container.DataItem, "MinorVersions") %>’
runat="server">
<ItemTemplate>
<li>
Minor version
<%# DataBinder.Eval(Container.DataItem, "MajorVersionNumber") %>.
<%# DataBinder.Eval(Container.DataItem, "VersionNumber") %>
<ul class="AllWhatsNewList">
<asp:Repeater
id="WhatsNewRepeater"
DataSource=’<%# DataBinder.Eval(Container.DataItem, "WhatsNew") %>’
runat="server">
<ItemTemplate>
<li>
<a href="/WhatsNew/View/<%# DataBinder.Eval(Container.DataItem, "ID") %>">
<%# DataBinder.Eval(Container.DataItem, "Headline") %>
</a>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
I think the point is that there is just no need to pass your business class instances through to the UI layer. In fact if you later changed the structure of your business classes this LINQ would no longer compile, whereas the markup in the view is evaluated at runtime so you wouldn’t spot an error here until you tried to view the page.
Comments