2010-10-18 12 views
30

Estoy utilizando el marco Entity por primera vez, y me gustaría saber si estoy usando las mejores prácticas.Mejores prácticas de Entity Framework en lógica empresarial?

He creado una clase separada en mi lógica de negocio que manejará el contexto de la entidad. el problema que tengo, es que en todos los videos que he visto, generalmente envuelven el contexto en una declaración de uso para asegurarse de que esté cerrado, pero obviamente no puedo hacer esto en mi lógica empresarial ya que el contexto se cerrará antes de que pueda usarlo?

¿Así que está bien qué estoy haciendo? Un par de ejemplos:

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

sólo quiero asegurarme de que no estoy construyendo algo que va a morir cuando una gran cantidad de personas lo utilizan?

Respuesta

62

Realmente depende de cómo desea exponer su repositorio/almacén de datos.

No estoy seguro de lo que quiere decir con "el contexto se cerrará, por lo tanto, no puedo hacer la lógica de negocios". Haga su lógica de negocio dentro de la declaración using. O si su lógica comercial está en una clase diferente, entonces continuemos. :)

Algunas personas vuelven colecciones concretas de su depósito, en cuyo caso se puede envolver el contexto de la instrucción using:

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

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

ventaja de que está satisfaciendo la buena práctica con conexiones - abrir tan tarde como puedes, y cierra tan pronto como puedas.

Puede encapsular toda la lógica de su negocio dentro de la instrucción using.

Las desventajas: su Repositorio se da cuenta de la lógica de negocios, que personalmente no me gusta, y termina con un método diferente para cada escenario en particular.

segunda opción - vuelva a crear un contexto como parte del repositorio y haga que implemente 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(); 
    } 

} 

Y luego:

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

O el -tercera opción de (mi favorito), el uso de inyecciones dependencia.Desacoplar todo el contexto del trabajo desde su repositorio, y dejar que el DI contenedor de eliminación de la manija de los recursos:

public class ArticleRepository 
{ 
    private IObjectContext _ctx; 

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

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

Su elegido contenedor DI inyectará ObjectContext concreto en la creación de instancias del repositorio, con una duración configurada (Singleton, HttpContext, ThreadLocal, etc.) y deshacerse de él en función de esa configuración.

Lo tengo configurado para que cada solicitud HTTP reciba un nuevo contexto. Cuando la Solicitud finalice, mi contenedor DI eliminará automáticamente el contexto.

También utilizo el patrón Unidad de trabajo aquí para permitir que varios repositorios funcionen con un contexto de objeto.

Es posible que también haya notado que prefiero devolver IQueryable de mi Repositorio (a diferencia de una Lista concreta). Mucho más poderoso (aunque arriesgado, si no comprende las implicaciones). Mi capa de servicio realiza la lógica comercial en IQueryable y luego devuelve la colección concreta a la IU.

Esa es mi opción más poderosa, ya que permite un Repositorio simple como el de Heck, la Unidad de Trabajo administra el contexto, la Capa de Servicio maneja la Lógica de Negocios, y el Contenedor DI maneja la vida/eliminación de recursos/objetos.

Avíseme si desea obtener más información al respecto, ya que hay mucho que ofrecer, incluso más que esta respuesta sorprendentemente larga. :)

+0

Sin preocupaciones. :) Mira algunas de mis otras preguntas/respuestas: he estado lidiando con esto recientemente. No tenía la intención de que esta respuesta fuera tan larga, pero creo que me dejé llevar, después de todo, es un tema bastante complicado. :) – RPM1984

+2

¿Podría explicarme más sobre cómo usa el patrón de unidad de trabajo? Pensé que EF ObjectContext es realmente una UoW. – Santhos

+4

Lo que me gustaría saber es qué agrega esta versión final? Simplemente parece una abstracción adicional sobre el marco de la entidad, que ya es una abstracción, ya que el marco de la entidad ya implementa la Unidad de trabajo y el Repositorio en sí mismo. ¿Por qué usar esta clase de repositorio en lugar de la entidad datacontext en sí? – Tobberoth

3

Tendría la ctx como una variable privada dentro de cada clase, luego creé una nueva instancia de esto cada vez y luego deseché cuando terminé.

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

} 

Luego al llamar esto.

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

De esta manera también se puede añadir/actualizar otros objetos dentro del mismo contexto y llamar a un método que ahorra guardar cualquier cambio en el PP a través de la Entidad.

+1

Gracias por el ejemplo :) Apreciado – leen3o

+0

¡Gracias por esta respuesta! – killexe

+0

@killexe más bienvenidos. –

1

Lo que también puede hacer es almacenar su contexto en un nivel superior.

Por ejemplo, usted puede tener una clase estática que almacena el contexto actual:

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

Entonces, en algún lugar fuera haces algo como esto:

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

Luego, dentro de las GetLastestArticles, sólo tiene que utilizar el mismo ContextManager.CurrentContext.

Por supuesto, esta es solo la idea básica. Puede hacer que esto sea mucho más factible utilizando proveedores de servicios, IoC y demás.

3

En mi experiencia, este código no es bueno, porque se pierde la capacidad de navegar relaciones a través de las propiedades de navegación.

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

uso de este enfoque no se puede utilizar el siguiente código a.Members porque siempre es nula (contexto dB es estrecha y no puede obtener datos de forma automática).

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

Usando sólo un contexto mundial db es una mala idea, ya que debe utilizar una función de eliminación cambios

en un punto de su aplicación yo hacer esto, pero no guarde los cambios y cerrar la ventana

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

luego en otro punto de aplicación que hacen alguna operación y guardar el

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

en este caso, la propiedad aprobada del artículo anterior está configurada como modificada por el marco de la entidad. Cuando guardas, aprobado se establece verdad !!!

mejor enfoque para mí es utilizar 1 contexto por clase Puede pasar contexto a otro método externo si necesita

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

Puede comenzar a preparar Marco de la entidad de capa de acceso de datos mediante la creación de una clase repositorio genérico para todas las funciones requeridas del Marco de la Entidad. A continuación, puede utilizado en la capa de negocios (encapsulado)

Estas son las mejores prácticas que he utilizado para el marco de la entidad en los datos, negocios, y las capas de interfaz de usuario

técnicas utilizadas para esta práctica:

    La aplicación de
  1. SOLID architecture principles
  2. Utilizando el patrón de diseño del repositorio
  3. Sólo una clase a ir (y se encuentra listo)
Cuestiones relacionadas