Best Practices In Business Logic With Entity Framework?

asp.net c# entity-framework linq

Question

I am using the Entity framework for the first time, and would like to know if I am using in the best practice.

I have created a separate class in my business logic which will handle the entity context. the problem I have, is in all the videos I have seen they usually wrap the context in a using statement to make sure its closed, but obviously I can't do this in my business logic as the context will be closed before I can actually use it?

So is this ok what I'm doing? A couple of examples:

    public IEnumerable<Article> GetLatestArticles(bool Authorised) 
    {
        var ctx = new ArticleNetEntities();
        return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

    public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
    {
        var ctx = new ArticleNetEntities();
        return ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

I just want to make sure I'm not building something that's going to die when a lot of people use it?

1
31
5/21/2015 11:46:43 AM

Accepted Answer

It really depends on how to want to expose your repository/data store.

Not sure what you mean by "the context will be closed, therefore i cannot do business logic". Do your business logic inside the using statement. Or if your business logic is in a different class, then let's continue. :)

Some people return concrete collections from their Repository, in which case you can wrap the context in the using statement:

public class ArticleRepository
{
   public List<Article> GetArticles()
   {
      List<Article> articles = null;

      using (var db = new ArticleNetEntities())
      {
         articles = db.Articles.Where(something).Take(some).ToList();
      }
   }
}

Advantage of that is satisfying the good practice with connections - open as late as you can, and close as early as you can.

You can encapsulate all your business logic inside the using statement.

The disadvantages - your Repository becomes aware of business-logic, which i personally do not like, and you end up with a different method for each particular scenario.

The second option - new up a context as part of the Repository, and make it implement IDisposable.

public class ArticleRepository : IDisposable
{
   ArticleNetEntities db;

   public ArticleRepository()
   {
      db = new ArticleNetEntities();
   }

   public List<Article> GetArticles()
   {
      List<Article> articles = null;
      db.Articles.Where(something).Take(some).ToList();
   }

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

}

And then:

using (var repository = new ArticleRepository())
{
   var articles = repository.GetArticles();
}

Or the third-option (my favourite), use dependency injection. Decouple all the context-work from your Repository, and let the DI container handle disposal of resources:

public class ArticleRepository
{
   private IObjectContext _ctx;

   public ArticleRepository(IObjectContext ctx)
   {
      _ctx = ctx;
   }

   public IQueryable<Article> Find()
   {
      return _ctx.Articles;
   }
}

Your chosen DI container will inject the concrete ObjectContext into the instantiation of the Repository, with a configured lifetime (Singleton, HttpContext, ThreadLocal, etc), and dispose of it based on that configuration.

I have it setup so each HTTP Request gets given a new Context. When the Request is finished, my DI container will automatically dispose of the context.

I also use the Unit of Work pattern here to allow multiple Repositories to work with one Object Context.

You may have also noticed I prefer to return IQueryable from my Repository (as opposed to a concrete List). Much more powerful (yet risky, if you don't understand the implications). My service layer performs the business logic on the IQueryable and then returns the concrete collection to the UI.

That is my far the most powerful option, as it allows a simple as heck Repository, the Unit Of Work manages the context, the Service Layer manages the Business Logic, and the DI container handles the lifetime/disposal of resources/objects.

Let me know if you want more info on that - as there is quite a lot to it, even more than this surprisingly long answer. :)

64
10/18/2010 9:45:28 AM

Popular Answer

In my experience this code is not good, because you lose the capacity to navigate relationships through navigation properties.

public List<Articles>  getArticles( ){  
    using (var db = new ArticleNetEntities())
    {
        articles = db.Articles.Where(something).ToList();
    }
}

Using this approach you can't use the following code because a.Members is always null( db context is close and cant get data automatically).

var articles = Data.getArticles();
   foreach( var a in articles ) {
       if( a.Members.any(p=>p.Name=="miki") ) {
           ...
       }
       else {
           ...
       }
    }
}

Using only a global db context is a bad idea because you must use a delete changes function

in a point of your application yo do this but don't save changes and close the window

var article= globalcontext.getArticleByID(10);
article.Approved=true;

then in another point of application you make some operation and save

//..... something
globalcontext.saveChanges();

in this case previous article approved property is set to modified by entity framework. When you save, approved is set true!!!

Best approach for me is use 1 context per class You can pass context to another external method if you need

class EditArticle {

    private DbEntities de;
    private currentAricle;

    public EditArticle() {
        de = new DbEntities; //inizialize on new istance
    }

    loadArticleToEdit(Articele a){
        // a is from another context 
        currentArticle= de.Article.Single(p=>p.IdArticle==a.IdArticle){
    }

    private saveChanges(){
        ...
        pe.saveChanges();
    }
}


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow