简体   繁体   中英

Linq to Entities - 3-tier Architecture

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"??)

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 eg 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...
    }
}

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

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.

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);
     }
}

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 executionhttp://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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM