0

I have implemented Unit of Work and Repository pattern with Entity Framework. A sample operation is shown below:

public T GetById(int id)
{
     return _context.Set<T>().Find(id);
}

So if I wanted to bring a record with id 12 and only StudentName column. I cannot do that using the above method as all columns will be pulled which I could have done using Linq like below:

Student get = context.Students
                    .Where(s => s.Id = 12)
                    .Select(s => new { StudentName = Name })
                    .SingleOrDefault();

Currently, I am returning an IQueryable from the repository like below to make above aforementioned scenario work:

public IQueryable<T> Query() 
{
    IQueryable<T> query = dbset;
    return query;
}

which makes the point of having a Repository to null anyway because I cannot restrict operations on database now. I have to query like below:

Student get = _uow.Students
                 .Query()
                 .Where(s => s.Id = 12)
                 .Select(s => new { StudentName = Name })
                 .SingleOrDefault();

Any suggestions on how to improve this situation or other opinions are required please.

lbrahim
  • 3,710
  • 12
  • 57
  • 95
  • My repository's method has generic return type. So, returning type is not the issue. Please read the post again. – lbrahim May 13 '14 at 16:45
  • i used to pass a property name array and return it from the query using reflection. its not certainly something pretty. but you always dont have to use generic repository.i think its completely valid to use a entity specific repository when there is a special case – qamar May 13 '14 at 16:48
  • Selecting columns when pulling results is very common and not at all specific. 10 out of 11 entities will require it. If I create specific repos for each and everyone of them then there goes DRY. – lbrahim May 13 '14 at 16:51
  • 1
    well I don't agree on that. You are no way repeating yourself. You are not rewriting the whole repository may be a method or two. Anyway if you send a list of property name perhaps you can use reflection to search for those and construct an object and send back to the caller. – qamar May 13 '14 at 16:53
  • Thanks. I suppose that is an option. – lbrahim May 13 '14 at 16:59
  • Do you know all your classes have an integer key and no composite keys? – CharlesNRice May 13 '14 at 17:06
  • What you are implementing there is a generic repository. You have discovered that generic repositories are, in most cases, useless and a bad idea. – usr May 13 '14 at 17:51
  • We have had enough discussion around generic repositories. Please do some research in that direction. http://stackoverflow.com/questions/1230571/advantage-of-creating-a-generic-repository-vs-specific-repository-for-each-obje is quite useful to read. I'm not closing this because it is not an exact duplicate. – usr May 13 '14 at 17:54
  • @CharlesNRice That is hardly the concern here. – lbrahim May 13 '14 at 17:55
  • @Md.lbrahim I was going to show how to return an IQueryable with passing in just the int key with Expression Trees so the Where clause would auto be set. – CharlesNRice May 13 '14 at 18:25
  • @CharlesNRice Yes they will always be `int`. – lbrahim May 13 '14 at 19:55
  • @usr I can see generic repositories are becoming a pain but what would you recommend as an alternative? – lbrahim May 14 '14 at 06:32
  • @Md.lbrahim EF already is a repository. In many cases that is enough. – usr May 14 '14 at 09:02

1 Answers1

2

I would argue that you are worried about restricting operations at the wrong layer; the Repository. Assuming your Repositories are supposed to abstract away the details of your DB and Entity Framework, I think this is too low of a level to do what you want.

If you have a Repo per DB table, and a class per query result type (direct SQL/EF or DB View), it doesn't make sense to introduce another layer of abstraction here. It would be better to do this at the next layer up, or whatever is handling your transactional boundaries.

To demonstrate, here is a more concrete example:

Given a Student DB table:

TABLE Student
    PK Id int
    COLUMN Name string
    COLUMN SecretData string

Your StudentRepo should always return instance(s) of a Student class:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string SecretData { get; set; }
}

Then the layer that utilizes your repo(s) should handle your transactions (potentially across multiple repos/operations) and map its results to a Domain entity. Your domain entity can include only the fields which you want to surface. You can create specialized domain entites for each purpose you need.

public class DomainStudent
{
    public int Id { get; private set; } // prevent attempts to change Ids on domain entities
    public string Name { get; set; }
}

public class DomainStudentWithSecret
{
    public int Id { get; private set; }
    public string Name { get; set; }
    public string SecretData { get; set; }
}

And to expand on why you would want to handle this kind of mapping and transactional boundaries outside of your Repository code: these things are best left to code which can operate across many DB tables. Often you need to take the result of two separate SQL queries and map the result to a single domain entity. Or sometimes, you want to roll back a transaction (or not execute subsequent SQL) if an initial query fails. I find it best to keep the Repos/DAOs working on a single table/view/sproc (DB entity) to abstract away the details of the Db engine and have domain-layer classes handle the heavy lifting of how to make sense of the data. If you need complex SQL queries with many JOINs, consider creating a view so you can work with the data like any other table.

Jesse Webb
  • 43,135
  • 27
  • 106
  • 143
  • So if I understand correctly you are suggesting to do all the Db plumbing in my "Business Layer" ? – lbrahim May 13 '14 at 18:05
  • 1
    No, I am suggesting that you do all of your DB plumbing (SQL and EF) in your Repo/DAO layer. Then have your business layer handle making useful information out of your data. – Jesse Webb May 13 '14 at 19:06
  • Have not seen this way before. Any books or article that could elaborate on your way? – lbrahim May 14 '14 at 04:03
  • 1
    Not sure if this pattern has an official name. In the past, in a tiered architecture, we built everything on top of our 'data' layer and the classes it used for it's DB tables. But now, with the growing popularity of the [Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) and the similar [Onion Architecture](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/), it is crucial that your domain entities be pure of deps. The best blog post I have read on the subject is ["Is Layering Worth the Mapping?"](http://blog.ploeh.dk/2012/02/09/IsLayeringWorththeMapping/) – Jesse Webb May 14 '14 at 14:45
  • 1
    Also, this is a good SO Q&A about duplicate/similar classes in data/domain/view layers: ["How do I reduce duplication of domain/entity/DTO objects?"](http://stackoverflow.com/q/18727676/346561) – Jesse Webb May 14 '14 at 14:49