1

I have a domain model that includes something like this:

public class Customer : EntityBase<Customer>, IAggregateRoot
{
    public IList<Comment> Comments { get; set; }
}

public class Comment : EntityBase<Comment>
{
    public User CreatedBy { get; set; }
    public bool Private { get; set; }
}

I have a service layer through which I retrieve these entities, and among the arguments passed to that service layer is who the requesting user is.

What I'd like to do is be able to construct a DetachedCriteria in the service layer that would limit the Comment items returned for a given customer so the user isn't shown any comments that don't belong to them and are marked private.

I tried doing something like this:

criteria.CreateCriteria("Comments")
    .Add(Restrictions.Or(Restrictions.Eq("Private", false),
                         Restrictions.And(Restrictions.Eq("Private", true),
                                          Restrictions.Eq("CreatedBy.Id", requestingUser.Id))));

But this doesn't flow through to the lazy-loaded comments.

I'd prefer not to use a filter because that would require either interacting with the session (which isn't currently exposed to the service layer) or forcing my repository to know about user context (which seems like too much logic in what should be a dumb layer). The filter is a dirty solution for other reasons, too -- the logic that determines what is visible and what isn't is more detailed than just a private flag.

I don't want to use LINQ in the service layer to filter the collection because doing so would blow the whole lazy loading benefit in a really bad way. Lists of customers where the comments aren't relevant would cause a storm of database calls that would be very slow. I'd rather not use LINQ in my presentation layer (an MVC app) because it seems like the wrong place for it.

Any ideas whether this is possible using the DetachedCriteria? Any other ways to accomplish this?

Josh Anderson
  • 5,975
  • 2
  • 35
  • 48

1 Answers1

1

Having the entity itself expose a different set of values for a collection property based on some external value does not seem correct to me.

This would be better handled, either as a call to your repository service directly, or via the entity itself, by creating a method to do this specifically.

To fit in best with your current model though, I would have the call that you currently make to get the the entities return a viewmodel rather than just the entities;

public class PostForUser
{
    public Post Post {get; set;}
    public User User {get; set;}
    public IList<Comment> Comments}
}

And then in your service method (I am making some guesses here)

public PostForUser GetPost(int postId, User requestingUser){

   ...
}

You would then create and populate the PostForUser view model in the most efficient way, perhaps by the detached criteria, or by a single query and a DistinctRootEntity Transformer (you can leave the actual comments property to lazy load, as you probably won't use it)

Matt Tew
  • 1,581
  • 1
  • 9
  • 15
  • I actually construct ViewModels in the MVC app, but your answer made me consider something I hadn't before — adding a `CommentsForUser(User user)` method on the entity itself, which would return a LINQ-filtered collection of comments. Being a domain entity, and with this filtering concept being a big part of the domain, I don't think it's an overlap of concerns. – Josh Anderson Oct 16 '11 at 22:03
  • Yep, this is the way that I would actually do it. However, I would not filter the comments property using LINQ, I would call the repository to get only the comments for the user (so that you don't have to load up all the comments from the DB to select only a few) – Matt Tew Oct 16 '11 at 22:45
  • But granted, to do this, you will need to have a repository interface set up with DI, so that your domain can be kept separate from the data layer, but this is a different story... – Matt Tew Oct 16 '11 at 22:47
  • Yeah, I'd rather keep the repository out of the domain objects if possible (even though with NHibernate there's no such thing as a simple POCO). I'll keep that in mind if performance gets out of hand down the road. – Josh Anderson Oct 16 '11 at 22:59