views:

95

answers:

2

This scenario comes up often, now that I use LinkToSql and the classes it creates.

Classic scenario - two tables:

Member
ID
Name
...

Membership
ID
MemberID (foreign key)
Start (datetime)
Expiration (datetime)
...

An active membership would be one where now is between start and expiration.

To find out if a user has an active membership, I have created this partial class to extend on the LinkToSql-generated Member-class:

Partial Public Class Member
    Public ReadOnly Property HasActiveMembership() As Boolean
        Get
            Dim activeMembershipCount As Integer = (From m In Me.Memberships _
                                Where m.MemberId = Me.MemberId _
                                And m.StartDate < Now And m.ExpirationDate > Now() _
                                Select m).Count

            If activeMembershipCount > 0 Then Return True Else Return False

        End Get
    End Property
End Class

This works great for one member, but when I want to list 700 members and if they have active membership or not, it makes 700+ calls to the database.

How should I change my model/classes?

I could create a query just for lists, where make the entire thing into one query, which is pretty straightforward, but I would like to avoid that, if I could.

+2  A: 

You have a few options; for example, you could use DataLoadOptions.LoadWith or DataLoadOptions.AssociateWith to fetch the data eagerly - however, the optimal approach depends on how you are using it.

For example, you could write a "users with active membership" query (either in LINQ using Any, or with a SPROC/UDF). That would then be 1 round trip, but is arguably less object-centric (and more query centric). Which might be fine...

Marc Gravell
A: 

It looks like you already have an association defined, perhaps due to a FK relationship between the tables. That being the case, you can simply extend your membership query using a where clause using the entity reference already defined on the class.

C# example:

var query = db.Members.Where( m => m.Memberships
                                    .Any( ms => ms.StartDate > now
                                                && ms.ExpirationDate < now ));
tvanfosson
What I am looking for is a way to show all members - and a true/false if they actually have an active membership. Not just the members with an actual active membership. :)
Kjensen
Look at @Marc's answer with respect to the DataLoadOptions. If the data isn't lazily loaded, your query against Memberships won't go back against the database -- it will be LinqToObjects. You should probably use Any() instead of calculating the count, too. I would expect in LinqToObjects it will short circuit after the first one is found. Count would need to examine all of them. Probably a small performance benefit, but its the right method to use.
tvanfosson
Better than short circuit; in many cases it'll use EXISTS / COUNT / TOP 1 etc (I can't remember which), rather than actually fetch all of them
Marc Gravell
@Marc -- that would be true if they hadn't been loaded yet. Once they're in memory, I would think that it would actually have to look at each one or at least until it found one.
tvanfosson