5

In the past few months I've learned alot about Linq-To-Entities and the 3-tier architecture with a DAO/DAL/Repository. Now I've a few things in my mind that keeps bugging me. I've those three questions that you'll see below.

There are alot of ways to make the repository work but what way is "the" way of making the repository work in ways of performance.

1) Initialize a datacontext in the constructor

public class Repository : IRepository
{

    private Datacontext context;

    public Repository()
    {
        context = new Datacontext();
    }

    public IList<Entity> GetEntities()
    {
        return (from e in context.Entity
                select e).ToList();
    }
}

2) Use "Using"

public class Repository : IRepository
{
    public IList<Entity> GetEntities()
    {
        using (Datacontext context = new Datacontext())
        {
            return (from e in context.Entity
                    select e).ToList();
        }

    }
}

3) In another way (please comment)

I'll put your suggestion here for others to comment


Also it seems some people say the repository should return an IQueryable to the businesslayer while others say it's better to return an IList. What is your oppinion on this?


The above code samples in the first question are pointing to the Repository, but what is the bestway to implement the repository in the businesslayer (Initialize in the constructor, use "Using"??)

Julian
  • 1,105
  • 2
  • 26
  • 57

5 Answers5

2

Either works I think. The main thing is that you should be making your object contexts fairly short lived (IMHO). Therefore I think you have two choices: -

  1. Create / destroy the context within a single method call e.g. the using statement as per your second example.

  2. Create / destroy the context when you create / destroy the repository - in which case your repository should both implement IDisposable and itself be wrapped in a using statement, and should be short lived. The benefit of this approach is that your repository methods simply do the query, there is no using (new ObjectContext()) polluting the method; the flip-side is that the reposibility passes onto the client to dispose of the Repository. Using this mechanism means that you can also compose queries within IQueryable<> (provided that you execute the query before disposing of the repository). For example:

public class Repository : IDisposable { DataHubContext context = new DataHubContext();

public IQueryable<Payment> GetPayments()
{
    return context.Payments;
}

public void Dispose()
{
    context.Dispose();
}

}

Formatting has gone a bit funny in SO - sorry....And then in your calling code: -

public class ClientCode
{
    public void DisplayPaymentsOnScreen()
    {
        Payment[] payments;

        using (var repository = new Repository())
        {
            payments = repository.GetPayments().Where(p => p.Amount > 100).ToArray();
        }

        // Do stuff with the data here...
    }
}
Isaac Abraham
  • 3,422
  • 2
  • 23
  • 26
  • And how about the normal garbage collector that disposes the repository after the webclient has left the site? (after a while) – Julian Jul 11 '11 at 13:21
  • if you put your repository in a using statement then once it leaves scope of the using statement it'll be marked for GC. This is unrelated to the web client leaving your site - more to do with scope of the repository. Just ensure that in your dispose method of the repository you dispose the ObjectContext :-) – Isaac Abraham Jul 11 '11 at 13:37
  • Yes when you use the using statement it'll. But most of the people here are saying I shouldn't use the using statement but create the context in the constructor of the repository. What are your thoughts about that? – Julian Jul 11 '11 at 13:45
  • my second suggestion above is to indeed create the object context in the constructor. Make the repository class itself disposable and in your client classes wrap it a using statement. then when you dispose of the repository the context gets disposed as well. i will update my answer with a code sample. – Isaac Abraham Jul 11 '11 at 13:47
  • Hmm, this looks really nice Isaac, would the performance suffer because everytime you ask something from the businesslayer it'll create and afterwards dispose a new Repository? – Julian Jul 12 '11 at 09:27
  • Hmmm - I don't think so. The main cost is creating and disposing of the object context itself; the actual call to Dispose isn't a big thing as far as I know - it's what happens in the dispose. And somewhere you'll need to be creating and disposing of your object context. – Isaac Abraham Jul 12 '11 at 09:28
  • I think this is a really nice way of using the repository pattern. I'll accept this answer till I find the perfect way. – Julian Jul 12 '11 at 14:03
1

I think it depends on your needs...

If your repository is only for getting data then I would probably go with the Using technique, but to be honest, when will you want a repository for just getting data. You will also want to add and update so I would definitely go for a global Data Context. This has the benefit that you can update an entity model and then save the changes back. If you use a different data context for each call you cannot persist the changes.

A sample repository would be something like...

public class Repository : IRepository
{

private Datacontext context;

public Repository()
{
    context = new Datacontext();
}

public IList<Entity> GetEntities()
{
    return (from e in context.Entity
            select e).ToList();
}

public void Save()
{
    context.SubmitChanges();
}
}

...then you can make many data changes and submit to database in one go. Another point to think about is that in your GetEntities you have a call of ToList(). When you call this you are in fact executing the database query there and then. If you plan to do further logic processing on the results you may want to return an IQueryable instead and only call ToList when you really need to use a list

musefan
  • 47,875
  • 21
  • 135
  • 185
  • This was the first thought I had about the repository. Return lists and initialize the datacontext in the constructor. How would you dispose the repository after the webclient has left the website? – Julian Jul 11 '11 at 13:24
  • I haven't been, AFAIK you don't have to do that. I thought it would clear up after it went out of scope, C# handles these kind of things. I have read a few other comments thou and I am definitely going to have double check this for myself now... you give some knowledge, you get some knowledge, and then the cycle repeats :D – musefan Jul 11 '11 at 13:37
  • Jup, nice I'll look into it some more also. And whenever I know the best way to handle these kind of stuff I'll edit my post. Thanks for helping! – Julian Jul 11 '11 at 13:42
0

I would prefer the using pattern as that allows the code to be more clean and simple as we know the scope of the db context and clearly can say when this get disposed, which is difficult to say in case of a constructor case.

Also, I don't think you can return IQueryable in case of "using" as the db context will be disposed as soon as the using block exit and then you cannot use the returned IQueryable in your business tier.

Ankur
  • 33,367
  • 2
  • 46
  • 72
  • But isn't this a problem whenever my Client/GUI-Layer makes an edit. Like adding a Book-entity to the BookReader-entity? (Because the context is disposed so whenever I call the Bookreader.Books.Add(...) it will not update my Bookreader Entity) – Julian Jul 11 '11 at 11:40
  • Adding can be done simply through repository by passing the new Book object into add method and this method created a context, add the book, call saved, and dispose the context. Where as in case of edit, you will open the context, find the book by book id, make property changes to the book and then call save method on context, then dispose – Ankur Jul 11 '11 at 11:59
0

If you want ot be able to chain your methods in order to make the most accurate query, use the first case. Eg:

public class Repository : IRepository
{

    private Datacontext context;

    public Repository()
    {
        context = new Datacontext();
    }

    public IQueryabale<Entity> SelectAllEntities()
    {
         return context.Entity.Where(e=>! e.IsObsolote);
    }

    public IQueryable<Entity> SelectAllEntityRelatedToAnotherEntity(Entity otherEntity)
    {
         return this.SelectAllEntities().Where(e=>e.RelatedEntityId == otherEntity.Id);
    }
}

EDIT

You can use it in collaboration with your business layer like this:

public class EntityManager()
{
     public IQueryable<Entities> FindAllApprovedEntities(Entity other)
     {
          return new Repository().SelectAllEntityRelatedToAnotherEntity(other).Where(e=>e.Approved);
     }
}
Gregoire
  • 24,219
  • 6
  • 46
  • 73
  • This seems fair, but also here you got the problem of disposing the context after it has done his job. Also, that last method "SelectAllEntityRelatedToAnotherEntity" isn't that the job of the business layer? – Julian Jul 11 '11 at 13:22
  • @Julian the job of the business layer is to add the conditions to the Iqueryable. See my edit for example – Gregoire Jul 11 '11 at 14:07
0

For me myself I would always return IQueryable<T>

public IQueryable<Entity> GetEntities()
{
    return from e in context.Entity select e;
}

You have to read about deferred execution http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx I use it so I can in the business logic or the UI I query the exact parts of the entity and not the whole entity (it like select *)

I prefer to initialize the context in the constructor

public class Repository : IRepository
{

 private Datacontext context;

 public Repository()
 {
     context = new Datacontext();
 }

 public IQueryable<Entity> GetEntities()
 {
     return from e in context.Entity select e;
 }  

 public int Save()
 {
     // Return the number of the affected rows to determine in your code whether your query executed or not 
     return context.SubmitChanges();
 }


}

Notice: Also when you designing your EF repository make sure that you have one instance of the context across all repositories to avoid errors during the update and delete.

I have a generic repository I made in my company and I plan to blog it soon, it allows you to just easily make CRUD operation and you can extend it as you want with a very few line of code. After I finish it i will update the answer with the URL

Ahmed Magdy
  • 5,956
  • 8
  • 43
  • 75
  • Nice arguments for your statement here Professor. I'll look into your link later but how do you dispose your repository when the user (webclient) exists? Except for waiting for the garbage collector I saw Isaac mention the IDisposable in his post. Do you use that or? – Julian Jul 11 '11 at 13:21
  • I Keep it here for GC because the code I gave u is just an example but in my real code because I mentioned one context for all repository so I cannot dispose it from a repository, I have a context (my context) for all repositories in the system, here I can think of dispose the context but I prefer to keep it to GC. I saw once this must be done in Linq to SQL but it's not necessary for EF – Ahmed Magdy Jul 11 '11 at 13:33
  • So your saying that we should just let the GC do his job. Ok, and what about the methods for the repository and businesslayer. Should the repository only have the save/update,delete,find(id),findall methods. Or is it fine to use like "findAllReadBooks()" kind of methods in the repository? – Julian Jul 11 '11 at 13:49
  • There will be an inheritance using generics and that base repository have the main CRUD then in the entity repository class you can add more as you like based on your business logic. About GC I'm not 100% sure of that we should keep it to GC and not to dispose it ourselves, I have to investigate about it. – Ahmed Magdy Jul 11 '11 at 14:26