简体   繁体   中英

Entity Framework Best Practices In Business Logic?

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?

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. :)

I would have the ctx as a private variable within each class, then create a new instance of this each time and then dispose when finished.

public class ArticleService
{
    private ArticleEntities _ctx;

    public ArticleService()
    {
        _ctx = new ArticleEntities();
    }

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

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

    public void Dispose()
    {
        _ctx.Dispose();
        _ctx = null;
    }

}

Then when calling this.

ArticleService articleService = new ArticleService();
IEnumerable<Article> article = articleService.GetLatestArticles(true);
articleService.Dispose(); // killing the connection

This way you can also add/update other objects within the same context and call a save method which saves any changes to the db through the Entity.

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

What you can also do is store your context at a higher level.

Eg, you can have a static class storing the current context:

class ContextManager
{
    [ThreadStatic]
    public static ArticleEntities CurrentContext;
}

Then, somewhere outside you do something like this:

using (ContextManager.CurrentContext = new ArticleEntities())
{
    IEnumerable<Article> article = articleService.GetLatestArticles(true);
}

Then, inside the GetLastestArticles, you just use the same ContextManager.CurrentContext.

Of course, this is just the basic idea. You can make this a lot more workable by using service providers, IoC and such.

You can start preparing Entity Framework from data access layer by creating a generic repository class for all required Entity Framework functions. Then you can used it in Business layer (Encapsulated)

Here are the best practices that I have used for Entity Framework in data, business, and UI layers

Techniques used for this practice:

  1. Applying SOLID architecture principles
  2. Using Repository design pattern
  3. Only one class to go (and you will find it ready)

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