5

I think I'm going in circles.

I'm working on an MVC 3 solution using EF4 & POCOs (database-first) and IoC. My repository and UoW patterns were mostly adopted from this article and this article.

My solution is made up of the following projects:

Implementations:

  • Presentation (MVC site)
  • Domain Services (business layer)
  • Domain Repository (data access)
  • Domain Context (my EF4 edmx and generated context)
  • Domain Models (my EF4 generated POCOs)

Interfaces:

  • Domain Services Interfaces (business layer interfaces)
  • Domain Repository Interfaces (data access interfaces)
  • Domain Context Interfaces (interfaces for generated EF4 context)

And lastly, the IoC project that ties everything together.

If you notice in that first article, the author mentions removing the dependency on ObjectSet from the domain services. This, I'm assuming, is for testability. Problem with this, though, is that it hinders the ability to do complex queries from the domain services, because IObjectSet and IEnumerable (returned by most methods on repository) don't stub out methods for complex querying.

Does this imply that I should be doing my complex querying in my repository? Should I move away from methods like public T Single(Expression<Func<T, bool>> where) and stick to methods like public T GetUserById(int id)?

If this is not the case, then how do I do complex queries such as this in my service layer?

Looking at my solution outline above and the questions I have, am I moving in the right direction, or am I creating problems for myself?

Thanks in advance.

Community
  • 1
  • 1
Jerad Rose
  • 15,235
  • 18
  • 82
  • 153
  • Also take a look at this article http://bit.ly/bF7jL3. It takes an approach like the article you referenced. Working with `IQueryable` is the key here. – Steven Mar 09 '11 at 07:16

2 Answers2

8

This is subjective/matter of opinion, but you could make your Repository return IQueryable<T>, then you can do your "complex queries" in your service like this:

return _repository // IRepository<T>
          .Find() // IQueryable<T>
          .Where(someComplexPredicate) // IQueryable<T>
          .SingleOrDefault(); // T

ObjectSet<T> : IQueryable<T>, which makes this possible.

If you want to start doing ObjectSet<T>-specific things in your service, you've got two options:

  1. Expose the ObjectSet<T>-specific method as a method on your Repository interface
  2. Use an IQueryable<T> extension method to do a "soft cast" to ObjectSet<T> (e.g var objSet = source as ObjectSet<T>).

Always try and go with option 1.

Perfect example is eager loading. There is a method called Include on the ObjectContext<T>, so if you use IQuerayable<T> in your Repository, how do you eager load?

Since Include takes a "magic string", you could accept this in your Find method on your Repository, e.g:

return _repository // IRepository<T>
          .Find("Product.Orders") // IQueryable<T>
          .Where(someComplexPredicate) // IQueryable<T>
          .SingleOrDefault(); // T

Fortunately in EF CTP5 they have introduced a strongly-typed Include which works off IQueryable<T>, so i didn't have to do the above when i cutover.

So as i said, best thing to do is expose methods on your Repository interface. But there needs to be a tradeoff - an interface should be a "contract" or "definition" of a service, not about the implmentation. So EF-specific things should be done via extension methods. General things can be done via the repository interface.

RPM1984
  • 72,246
  • 58
  • 225
  • 350
  • Thanks for this. I actually can do this, as my repository returns the IObjectContext as IQueryable. I overlooked this because of a common silly mistake I seem to keep making -- forgetting to include a `using System.Linq`. Thanks again for taking the time to respond. – Jerad Rose Mar 09 '11 at 06:04
  • @Jerad Rose - haha, no problems. Get yourself a copy of resharper, it's a must have! – RPM1984 Mar 09 '11 at 07:41
  • I actually have ReSharper, I think that was my problem. I wasn't using the LINQ extension methods on any of these interfaces, and when I did auto-clean, it removed `using System.Linq` since it wasn't being used. :( – Jerad Rose Mar 09 '11 at 14:30
  • @Jerad - ahh! yeah i've got caught by that heaps of times, which is why i prefer to manually clean up each "yellow section", instead of a full cleanup. Glad you got it sorted anyway. – RPM1984 Mar 09 '11 at 22:32
1

Entity framework is a repository by itself. Why add an extra layer of complexity (repository). It just makes querying more difficult and you have to write all the repetitive code again and again (FindById(), FindAll(), FindAllByName() ....)

zszep
  • 4,450
  • 4
  • 38
  • 58
  • I guess because you'd always be able to keep your repository code and service layer, even though you change whatever is communicating with you database. The idea is to be able to switch out the EF for something else, and minimizing the amount of code you'd have to change. – Yngve B-Nilsen Mar 30 '11 at 06:50