My query below works fine when there is only category to return but as soon as as there is more than one I get Sequence contains more than one element
error message. I would like to return all the relevant categories. So I changed the PostCategory in the DTO from string to a List and that is when I get the casting error. I also tried changing it to a IList(of String)
and IList(of be_Category)
and adding ToList
to ca.CategoryName
. That didn't work.
My query with the joins:
Public Function SelectByID(id As Integer) As PostDTO Implements IPostRepository.SelectByID
Dim post As New PostDTO
Using db As Ctx = New Ctx
post = From ca In db.be_Categories
Join c In db.be_PostCategory On ca.CategoryID Equals (c.CategoryID)
Join p In db.be_Posts On c.PostID Equals (p.PostID)
Where p.PostRowID = id
Select New PostDTO With {
.PostCategory = ca.CategoryName,
.PostDateCreated = p.DateCreated,
.PostGuid = p.PostID,
.PostId = p.PostRowID,
.PostText = p.PostContent,
.PostTitle = p.Title}).Single
End Using
Return post
End Function
So is it possible to project the sequence of Category Names into a new DTO or something else or is there another way to return all the categories? I guess since CategoryName is a string, L2E cannot project the strings into the list. Do I need a GroupBy
to project category strings into a new form? I also tried AsEnumerable
and I tried String.Join
- neither worked.
The DTO is below - If PostCategory is a string then I can get a single category back to the view. I hope I have explained it clearly.
Public Class PostDTO
Public PostId As Integer
Public PostGuid As Guid
Public PostTitle As String
Public PostSummary As String
Public PostText As String
Public PostDateCreated As DateTime
Public PostIsPublished As Boolean
Public PostCategory As IList(Of be_PostCategory)
End Class
EDIT: Updated SelectById:
Public Function SelectByID(id As Integer) As IEnumerable(Of PostDTO) Implements IPostRepository.SelectByID
Dim post As IEnumerable(Of PostDTO)
Using db As Ctx = New Ctx
post = From ca In db.be_Categories
Join c In db.be_PostCategory On ca.CategoryID Equals (c.CategoryID)
Join p In db.be_Posts On c.PostID Equals (p.PostID)
Where p.PostRowID = id
Select New PostDTO With {
.PostCategory = ca.CategoryName,
.PostDateCreated = p.DateCreated,
.PostGuid = p.PostID,
.PostId = p.PostRowID,
.PostText = p.PostContent,
.PostTitle = p.Title}).ToList
End Using
End Function
The Single method throws an exception when the number of elements returned by the query is not exactly 1. Try removing .Single()
from the end of your query.
Also, we don't see what variable it's being assigned to. An anonymous type works well here, though if you don't use one, make sure it's correct i.e.
Dim result As IEnumerable(Of PostDTO) = From ca In db.be_Categories ...
Edit #1
I should add some clarification. When running a LINQ
query, expect for the query to return any number of results, just like you would expect SQL
or similar to do. In the case where you only expect one result however (i.e. Select Top 1 ...
) then you can use .Single()
. Getting back to your case, your query is against an Entity Framework data source I can only imagine (Ctx
is that, correct?). As indicated by the MSDN documentation, you will return a DbQuery(Of PostDTO)
, which is the data type returned by a LINQ to Entities query against a DbContext
. This type, depending on what you want to do with it, can be cast to several interfaces. See its definition
Public Class DbQuery(Of TResult) _
Implements IOrderedQueryable(Of TResult), IQueryable(Of TResult), _
IEnumerable(Of TResult), IOrderedQueryable, IQueryable, IEnumerable, _
IListSource, IDbAsyncEnumerable(Of TResult), IDbAsyncEnumerable
The IEnumerable(Of TResult)
, with TResult
being a PostDTO in your case, is what you can cast to so you can enumerate the results, providing a lot of functionality like further queries, sorting, getting average, max, min, etc. Hope this clears it up.
Edit #2
Finally getting to the bottom of the problem. The first part gives you a single Post, but with nothing in the PostCategory. The second part puts the list of categories into that single post.
post = From ca In db.be_Categories
Join c In db.be_PostCategory On ca.CategoryID Equals (c.CategoryID)
Join p In db.be_Posts On c.PostID Equals (p.PostID)
Where p.PostRowID = id
Select New PostDTO With {
.PostCategory = Nothing,
.PostDateCreated = p.DateCreated,
.PostGuid = p.PostID,
.PostId = p.PostRowID,
.PostText = p.PostContent,
.PostTitle = p.Title}).FirstOrDefault()
post.PostCategory = (From ca In db.be_Categories
Join c In db.be_PostCategory On ca.CategoryID Equals (c.CategoryID)
Where p.PostRowID = id
Select ca.CategoryName).ToList()