Inyección de dependencia con la solución Entity Framework de n niveles

12

Actualmente estoy diseñando una solución de n niveles que utiliza Entity Framework 5 (.net 4) como estrategia de acceso a datos, pero me preocupa cómo incorporar la inyección de dependencia para que sea comprobable / flexible.

El diseño de mi solución actual es el siguiente (mi solución se llama Alcatraz):

Alcatraz.WebUI : un proyecto de formulario web asp.net, la interfaz de usuario front-end, hace referencia a proyectos Alcatraz.Business y Alcatraz.Data.Models .

Alcatraz.Business : un proyecto de biblioteca de clases, contiene la lógica empresarial, hace referencia a proyectos Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : un proyecto de biblioteca de clase, alberga AlcatrazModel.edmx y AlcatrazEntitiesDbContext, hace referencia a proyectos Alcatraz.Data.Models .

Alcatraz.Data.Models : un proyecto de biblioteca de clases, contiene POCO para el modelo Alcatraz, sin referencias.

Mi visión de cómo funcionaría esta solución es que la web-ui instanciaría un repositorio dentro de la biblioteca de negocios, este repositorio tendría una dependencia (a través del constructor) de una cadena de conexión (no una AlcatrazEntitiesinstancia). La interfaz de usuario web conocería las cadenas de conexión de la base de datos, pero no es que fuera una cadena de conexión del marco de la entidad.

En el proyecto empresarial:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

En la interfaz de usuario web:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

Tengo algunas preguntas relacionadas sobre este diseño.

  1. ¿Este diseño tiene sentido incluso en términos de capacidades de Entity Frameworks? Escuché que Entity Framework ya usa el patrón de Unidad de trabajo, ¿estoy agregando otra capa de resumen innecesariamente?

  2. No quiero que mi web-ui se comunique directamente con Entity Framework (o incluso hacer referencia a él), quiero que todo el acceso a la base de datos pase por la capa empresarial, ya que en el futuro tendré varios proyectos utilizando la misma capa empresarial. (servicio web, aplicación de Windows, etc.) y quiero que sea fácil de mantener / actualizar teniendo la lógica de negocios en un área central. ¿Es esta una forma apropiada de lograr esto?

  3. ¿Debería la capa Business incluso contener repositorios, o debería estar dentro de la capa Access? Si dónde están está bien, ¿pasar una cadena de conexión es una buena dependencia para asumir?

¡Gracias por tomarse el tiempo de leer!

Mateo
fuente

Respuestas:

11

La forma en que está haciendo DI está mal.

Primero, la cadena de conexión pertenece a la capa de datos. O en el archivo web.config.

La siguiente abstracción con la que tratará es el DbContext, no una cadena de conexión. Sus repositorios no deben saber sobre cadenas de conexión. Su lógica de negocios no sabrá sobre DbContext, etc.

Su interfaz de usuario no tendrá idea y no creará ninguna instancia relacionada con EF.

Respuestas concretas a sus puntos:

  1. No agregue abstracciones, hasta que esté muy familiarizado con EF. Ya agrega buenas abstracciones como UoW, consultas, uso de POCO, etc.

  2. Para que DI funcione, tiene una raíz de composición que hace referencia a todos los componentes necesarios. Esto podría o no estar en el proyecto WebUI. Si no es así, debe esperar que no haga referencia a EF ni a ninguna otra tecnología relacionada con los datos.

  3. Detente aquí mismo. Deja de agregar abstracciones sobre abstracciones. Comience con una arquitectura directa e 'ingenua' y desarrolle con el tiempo.

Las abstracciones son una herramienta para lidiar con la complejidad. La ausencia de complejidad significa que no se necesitan abstracciones (todavía).

Boris Yankov
fuente
Para que quede claro que entiendo lo que estás diciendo: el repositorio (que existe en la interfaz de negocios, y el concreto existe en Alcatraz.Data.Access?) Acepta a DbContextcomo su dependencia. Las clases de negocios tienen repositorios como dependencia. Para la inyección de dependencia, estoy haciendo esto manualmente (así que entiendo lo que está pasando). La razón por la que quiero poder establecer la cadena de conexión en el DbContextes que uso el particionamiento de la base de datos, por lo que en ciertos casos necesito tener un marco de entidad para conectarme a diferentes bases de datos (de la misma estructura). ¿Te entiendo correctamente?
Mateo
Según el código que proporcionó, parece que no está haciendo DI en absoluto. El objetivo principal de DI es liberarte a ti y a tu código de la administración de las dependencias. No puedo imaginarlo haciéndolo efectivamente de forma manual sin un contenedor DI.
Boris Yankov
También mantenga su mente abierta con DI. Ya, por diversión, hice exactamente la misma pregunta aquí que en otro foro para obtener respuestas opuestas. DI es un patrón, no una arquitectura. Dependiendo de su objetivo, puede decidir usarlo o no. Lo uso, pero no por las razones por las que la mayoría de la gente me dice que lo use.
Bastien Vandamme
4

Algunos comentarios rápidos Yo personalmente probablemente no pasaría una cadena de conexión. En todo caso, ¿intentaría crear interfaces para los repositorios y simplemente pasar las interfaces? Haga que los repositorios implementen o expongan una interfaz IOW.

De esta manera, no es necesario que sea una base de datos que implemente sus repositorios. podrían ser una memoria caché, o cualquier cosa. ¿Quizás entonces podría usar algún tipo de marco de inyección de dependencia para crear una instancia de estos incluso?

Entonces, en respuesta a algunas de sus preguntas:

  1. Si creo que esta bien
  2. Todavía tendría la interfaz de usuario que hace referencia al proyecto EF y las interfaces de referencia de la capa de negocio que implementa la capa de repositorio de EF. De esa manera, otros proyectos aún podrían usar los mismos ensamblajes, pero tienen la flexibilidad de intercambiarlos si lo desean.
  3. hmmm, ¿Probablemente los repositorios en la capa de acceso pero implementando una definición de interfaz expuesta en la capa empresarial?

Estos son solo algunos pensamientos para reflexionar.

dreza
fuente
Sobre el punto 2, un objetivo que estaba tratando de lograr es no tener CRUD directamente dentro de la capa ui. Lo que quiero decir es que quiero asegurarme de que solo CRUD pueda suceder al pasar por la capa empresarial, de esa manera se gestiona.
Mateo