I don’t know if I am doing something wrong “I think I am” or I have hit a subsonic limitation.
I have 3 tables Articles, ArticleCategories and ArticleComments with a one to many relationship between Articles the other tables.
I have created the following class
public partial class Article
{
private string _CategoryName;
public string CategoryName
{
get { return _CategoryName; }
set
{
_CategoryName = value;
}
}
public ArticleCategory Category { get;set;}
private List<System.Linq.IQueryable<ArticleComment>> _Comments;
public List<System.Linq.IQueryable<ArticleComment>> Comments
{
get
{
if (_Comments == null)
_Comments = new List<IQueryable<ArticleComment>>();
return _Comments;
}
set
{
_Comments = value;
}
}
}
I get a collection of articles using this snippet
var list = new IMBDB().Select.From<Article>()
.InnerJoin<ArticleCategory>(ArticlesTable.CategoryIDColumn, ArticleCategoriesTable.IDColumn)
.InnerJoin<ArticleComment>(ArticlesTable.ArticleIDColumn,ArticleCommentsTable.ArticleIDColumn)
.Where(ArticleCategoriesTable.DescriptionColumn).IsEqualTo(category).ExecuteTypedList<Article>();
list.ForEach(x=>x.CategoryName=category);
list.ForEach(y => y.Comments.AddRange(list.Select(z => z.ArticleComments)));
I get the collection Ok but when I try to use the comments collection using
foreach (IMB.Data.Article item in Model)
{
%>
<%
foreach (IMB.Data.ArticleComment comment in item.Comments)
{
%>
***<%=comment.Comment %>***
<%}
} %>
the comment.Comment line throws this exception
Unable to cast object of type ‘SubSonic.Linq.Structure.Query`1[IMB.Data.ArticleComment]’ to type ‘IMB.Data.ArticleComment’.
I am basically trying to avoid hitting the database each time the comment is needed.
is there another to achieve this?
Thanks
OK – I’m not doing exactly what you’re attempting but hopefully this example will bring some clarity to the situation. This is more along the lines of how I would do a join using SubSonic if I absolutely had to do it this way. The only way I would consider this approach is if I were confined by some 3rd party implementation of the object and/or database schema…
The following is my personal opinion and conjecture and can be tossed/used as you see fit…
If you look at the “Better way” example this is more along the lines of how I actually use SubSonic.
SubSonic is based on some simple principles such as
Now if you write your data entities (your classes that are representations of your tables) in a manner that makes sense when you use SubSonic you’re going to work well together as a team. I don’t really do any joins when I work with SubSonic because I typically don’t need to and I don’t want the overhead. You start to show a good practice of “lazy-loading” the comment list property on your article object. This is good, this means if we need the comments in the consumer code, go get-em. If we don’t need the comments, don’t spend the time & money to go fetch them from the database. I restructured your ArticleCategory to Article relationship in a way that makes sense to me but may not suit your needs. It seemed like you agreed with Rob conceptually (and I agree with him again).
Now, there are 1000 other improvements to be made to this architecture. The first that comes to mind is to implement a decent caching pattern. For example, you may not want to fetch the comments from the database on each article, every time the article is loaded. So you might want to cache the article and comments and if a comment is added, in your “add comment” code, wipe the article from the cache so it gets rebuild next load. Categories is a perfect example of this… I would typically load something like categories (something that isn’t likely to change every 5 minutes) into a master Dictionary (int being the category id) and just reference that in-memory list from my Article code. These are just basic ideas and the concept of caching, relational mapping database or otherwise can get as complicated as you like. I just personally try to adhere to the SubSonic mentality that makes database generation and manipulation so much easier.
Note: If you take a look at the way Linq2SQL does things this approach is very similar at the most basic layer. Linq2SQL typically loads your dependent relationships every time whether you wanted that or knew it was doing it or not. I much prefer the SubSonic “obviousness”, if you will, of what is actually happening.
Sorry for the rant but I really hope you can run the above code in a little Console application and get a feel for what I’m getting at.