Este artículo proviene de un motor de traducción automática.

Patrones en la práctica

Modelos de persistencia

Jeremy Miller

Contenido

Asignar objetos a las bases de datos
Registro activo
Asignador de datos
Utilizar un repositorio
Asignación de identidad
Carga diferida Y inmediata
Modelo de proxy virtual
Realizar el paso siguiente

Acceso a datos es un tema popular entre los desarrolladores.Sin duda ha oído muchas las opiniones de tecnologías de acceso a datos específicos y marcos de persistencia, pero ¿cuál es la mejor forma para consumir estas herramientas en el proyecto?¿Qué criterios debe utilizar para seleccionar la herramienta adecuada para el proyecto?¿Lo necesita saber desde el punto de vista conceptual acerca de estas herramientas antes de utilizar?¿Qué ocurre si se tenemos mucho tiempo en sus manos y desea escribir su propia herramienta de persistencia, ¿qué debe saber?

Unsurprisingly, la respuesta a todas estas preguntas es para examinar los patrones de diseño subyacente de persistencia.

Modelos de dominio

Cuando se piensa en cómo se va a la estructura y representan la lógica empresarial en su sistema, tiene un par opciones principales.En este artículo, en gran medida supongo que ha elegido el enfoque de modelo de dominio para organizar la lógica empresarial en objetos de entidad.

A partir de la descripción formal, un modelo de dominio es un modelo de objetos del dominio que incorpora el comportamiento y datos.

Por ejemplo, el proyecto actual implica administración de relaciones cliente.(CRM) Tenemos objetos de la entidad de mayúsculas y minúsculas, usuario y soluciones que contienen los datos y implementar reglas empresariales relacionadas con los datos.Un modelo de dominio puede abarcar desde un modelo anemic que es simplemente un conjunto de estructuras de datos a un modelo muy enriquecida que jealously protege los datos sin procesar subyacentes de una interfaz estrecha (desarrollo Domain-Driven grandes).Que cae el modelo de dominio en este intervalo es en gran medida una cuestión de cómo complicada la lógica empresarial en el sistema realmente es y cómo frecuente informes o entrada de datos están en los requisitos del sistema.

Una explicación adecuada del patrón de modelo de dominio se sale del ámbito de este artículo.Se recomienda leer el capítulo 2 de libreta de patrones de arquitectura de aplicaciones corporativo Martin Fowler para una buena explicación de los patrones de diseño principal para organizar la lógica empresarial.

Antes de comenzar, vamos a revisar perciben la función del código de acceso base de datos y los datos en el sistema de las dos maneras principales:

  • La base de datos es la keystone de la aplicación y de un activo empresarial.El código de acceso a datos y el incluso el código de aplicación o servicio son simplemente mecanismos para conectar la base de datos con el mundo exterior.
  • Los objetos de negocio en el nivel intermedio y la capa de interfaz o servicio de usuario son la aplicación y la base de datos es un medio para mantener el estado de los objetos de negocio entre sesiones de forma confiable.

Personalmente, me siento como el primero, se entiende bien datacentric punto de vista en la comunidad de .NET, por lo que me gustaría para centrarse en el segundo punto de vista.En el segundo punto de vista, normalmente, está trabajando con objetos de entidad en el nivel intermedio.Una forma u otra, probablemente realiza asignación de objeto/relacional (o/administrador de recursos) para asignar datos de las entidades de negocio a las tablas de la base de datos y viceversa.Es posible que se hacerlo manualmente, pero lo más probable que con las herramientas de algún tipo.Estas herramientas son excelentes y potencialmente pueden ahorrar mucho tiempo de desarrollo, pero hay algunos aspectos que debe tener en cuenta y siempre es bueno comprender cómo funciona una herramienta bajo las portadas.Con suerte, estudiar los modelos de este artículo ayudará en las dos cuentas.

Soy un defensor grande de Agile prácticas como desarrollo controlado por prueba, desarrollo controlado por comportamiento, eficiente de programación y diseño continua.Diga esto sólo sea desactive que hay una diferencia muy específica en respecto a los modelos que se va explicar en este artículo y, probablemente se mostrará.

Asignar objetos a las bases de datos

HE decidido al modelo de que objetos de mi sistema en el nivel medio con la entidad que tiene identidad, datos y comportamiento relacionado.La lógica empresarial se implementarse en estos objetos de entidad o en servicios de dominio que utilizan estos objetos de entidad.¿Genial, pero cómo hacer perfectamente datos de mover y retroceder entre la base de datos y los objetos de entidad?

En el caso de una base de datos relacional, tiene que mover los campos y propiedades de nuestros objetos a las tablas y campos de la base de datos.Se puede escribir este código completamente por disponible, escribir instrucciones INSERT, UPDATE, SELECT y DELETE SQL independientes, pero se le da rápidamente cuenta que se está repetición usted mismo en el código bastante.Es decir, repetidas veces está especificando que los datos en una propiedad de objeto o el campo deben estar almacenados en una columna determinada de una tabla de base de datos.

Esto es que un asignador de objeto/relacional (O/CC) de pasos de.Cuando se utiliza un O o administrador de recursos, simplemente crear una asignación de las propiedades de objeto a la tabla de la base de datos y dejar que la herramienta de O o administrador de recursos utilizar que los metadatos para averiguar cuáles deberían ser las instrucciones SQL y cómo mover datos desde el objeto a las instrucciones SQL.

Pasemos concreta y ver un ejemplo muy básico.El proyecto actual tiene una clase de dirección que tiene este aspecto:

public class Address 
{
  public long Id { get; set; }
  public string Address1 { get; set; }
  public string Address2 { get; set; }
  public string City { get; set; }
  public string StateOrProvince { get; set; }
  public string Country { get; set; }
  public string PostalCode { get; set; }
  public string TimeZone { get; set; }
}

Cuando configura la asignación de la clase de dirección, necesitan especificar que la dirección de clase se asigna a la tabla, cómo los objetos Address se identificarse (clave principal), y las propiedades que se asignan a qué tablas de base de datos.

En este ejemplo, me usar la herramienta NHibernate Fluent para asignar direcciones.

Deliberadamente estoy haciendo la asignación de una manera longhand para mostrar todos los detalles. (En uso real, emplean Convención de configuración Para eliminar gran parte de la repetitiveness.) Este es el código:

public class AddressMap : ClassMap<Address> 
{
  public AddressMap() 
  {
    WithTable("Address");
    UseIdentityForKey(x => x.Id, "id");

    Map(x => x.Address1).TheColumnNameIs("address1");
    Map(x => x.Address2).TheColumnNameIs("address2");
    Map(x => x.City).TheColumnNameIs("city");
    Map(x => x.StateOrProvince).TheColumnNameIs("province_code");
    Map(x => x.Country).TheColumnNameIs("country_name");
    Map(x => x.PostalCode).TheColumnNameIs("postal_code");
  }
}

Ahora que ha realizado una asignación del modelo de objetos en el modelo de base de datos, algo tiene que ejecutar realmente la asignación y aproximadamente tiene dos opciones: Directory Record o asignador de datos.

Registro activo

Al elegir una estrategia de persistencia, la primera decisión que necesita hacer es dónde colocar la responsabilidad para llevar a cabo la asignación. Tiene dos opciones muy diferentes: puede hacer cada clase de entidad propia responsable de la asignación, o puede utilizar una clase totalmente independiente para realizar la asignación a la base de datos.

La primera opción se conoce como el patrón de registro Active: un objeto que contiene una fila en una tabla de base de datos o vista, encapsula el acceso de la base de datos y se agrega la lógica de dominio en que los datos. Un enfoque de registro Active coloca los métodos de persistencia directamente en el objeto de entidad. En este caso, la clase de dirección probablemente podría tener métodos como Guardar, actualizar y eliminar, así como un método de carga estático que consulta la base de datos para un objeto Address.

El uso típico de la trama de registro Active es hacer básicamente un contenedor de clase con establecimiento inflexible de tipos para una sola fila de una tabla de base de datos. Como tales, las clases de registro Active son generalmente un reflejo exacto de la estructura de base de datos (esto se varía entre herramientas). Muchas implementaciones de registro Active generará los objetos de entidades directamente de la estructura de base de datos.

La implementación de registro Active más famosa es la herramienta ActiveRecord que se incluye como parte de la Ruby en marco de desarrollo Web Rails (que es útil en la programación de .NET con IronRuby; consulte" Introducción A IronRuby Y RSpec, parte 1"). Si utilizaba ActiveRecord, debe crear primero una tabla denominada direcciones en la base de datos. A continuación, si todos me interesa está teniendo acceso a los datos de direcciones y los métodos para buscar, guardar e insertar datos de direcciones, toda la clase direcciones tendrá aspecto exactamente esta clase transcripción:

class Address < ActiveRecord::Base
end

La implementación Ruby utiliza metaprogramming para dinámicamente crear campos y métodos que coinciden con la tabla de base de datos denominada direcciones (forma del nombre de clase direcciones pluralized) en tiempo de ejecución de consultas.

Con algunas excepciones, las implementaciones de .NET de la trama de registro Active suelen trabajar mediante generación de código para crear clases de .NET que mapa directamente a las tablas de bases de datos. La ventaja de este enfoque es que es muy fácil conservar el modelo de base de datos y el objeto sincronizado.

Apoyar la programación

Programación eficiente enseña a eliminar esfuerzo desaprovechado en proyectos de desarrollo mediante favorece el diseño de "extracción" sobre el diseño de "inserción". Esto significa cuestiones de infraestructura como persistencia sólo se debe diseñado y creó para satisfacer las necesidades de los requisitos empresariales (extraídos a petición) en lugar de crear el código de capa de acceso a datos que cree que la aplicación necesitará más adelante (inserta).

Según mi experiencia, esto significa que debe desarrollar el sistema incremental mediante el desarrollo verticalmente, creando una característica en uno, en lugar de crear la capa horizontal un sistema a la vez. Al trabajar de este modo puede estar seguro de que desarrollo de infraestructura es la de la no más de lo que sea absolutamente necesita por unión todo código de infraestructura para una característica está integrada en el sistema. Cuando se trabaja horizontal mediante la creación de la capa de acceso de datos o la base de datos antes de escribir la interfaz de usuario, servicio o capa lógica empresarial, se arriesga a continuación:

  • No obtener comentarios adecuados al principio de los participantes en el proyecto porque no hay ninguna funcionalidad de trabajo para mostrar
  • Escribir código de infraestructura innecesarias para las características que es posible que no realmente obtener integrado
  • Crear el código de acceso a datos incorrectos porque no entienden el requisito empresarial desde el principio, o los requisitos de cambian antes de se crean las otras capas

Diseño de extracción también significa que se debe determinar la elección de la estrategia de acceso a datos por las necesidades de la aplicación. Dada una opción, elegirá un administrador de recursos y O para un sistema donde ha modelado la lógica empresarial de un modelo de dominio. Una aplicación de creación de informes, podrían omitir herramientas persistencia totalmente en favor de simplemente utiliza SQL y los conjuntos de datos. Para un sistema que es principalmente la entrada de datos, puede elegir una herramienta Registro de Active. La cuestión es que se forma el acceso a datos y persistencia por las necesidades de sus consumidores.

Asignador de datos

Bueno como el registro Active modelo puede ser, suele valiosa para tener una estructura de objeto que varía desde el modelo de base de datos. Quizás desee asignar varias clases a la misma tabla de base de datos. Es posible que tenga un esquema de base de datos heredada que no cabe en la forma de los objetos que se desea expresar en alguna lógica de negocios (un común caso para mí en Mis últimos varios proyectos).

Esto es donde la podría elegir el modelo de asignador de datos: una capa de asignador de objetos que mover datos entre objetos y una base de datos mientras conserva independiente de entre sí y con el asignador de clases propias. El modelo de asignador de datos elimina la mayor parte de la responsabilidad de persistencia de los objetos de entidad en favor de las clases externas a las entidades. Con un patrón de asignador de datos, obtiene acceso a, consulta y guarda los objetos de entidad con algún tipo de depósito (descrita más adelante en este artículo).

¿Cómo elige entre los dos? La diferencia personal muy establecimiento inflexible de tipos es hacia la solución de asignador de datos. Ese margen, en la que registro Active es mejor para sistemas con lógica de dominio más sencilla, las aplicaciones que hacen un uso intensivo de CRUD (crear, leer, actualizar y eliminar, es decir) y situaciones donde el modelo de dominio no es necesario difieren mucho de la estructura de base de datos. Registro activo puede ser más cómodo para muchos equipos de desarrollo de .NET ya que implica una manera de datacentric de trabajo es más común para los equipos de .NET y, Francamente, mucho mejor admitidos por .NET propia. La mayoría de persistencia de las herramientas en el espacio de .NET podría caracterizar como herramientas de registro Active.

Por otro lado, asignador de datos es más apropiado para los sistemas con dominio compleja lógica donde se divergen considerablemente la forma del modelo de dominio desde el modelo de base de datos. Asignador de datos también decouples las clases de modelo de dominio desde el almacén de persistencia. Eso podría ser importante para los casos en que necesite volver a utilizar el modelo de dominio con los motores de base de datos diferente, los esquemas o los mecanismos de almacenamiento diferente incluso por completo.

Más importante que se me como un profesional de Agile, el enfoque de asignador de datos me permite diseñar el modelo de objetos independientemente de la base de datos con el desarrollo de controlado por pruebas de forma más eficaz que un registro Active solución podría. Esto es importante a los equipos que desea adoptar un enfoque incremental para diseñar ya que permite un equipo para trabajar en el diseño en el modelo de objetos en primer lugar donde la refactorización de las herramientas y técnicas de pruebas de unidad son por lo general más eficaces, crear el modelo de base de datos más adelante cuando el modelo de objetos es estable. El modelo de asignador de datos también es más aplicable al enfoque de arquitectura de diseño de dominios controlado por que está ganando en popularidad de la comunidad de .NET.

Utilizar un repositorio

Cuando ha creciendo hasta como desarrollador en los días de Windows DNA, residían en mortal miedo de olvidarse de cerrar un objeto Connection de ADO en mi código. En mi primer proyecto de empresa grande, codifica directamente en ADO. Cada vez necesario para solicitar o guardar datos en la base de datos, tuve que hacer lo siguiente:

  1. Buscar la cadena de conexión de algún tipo de configuración
  2. Abrir una nueva conexión a la base de datos
  3. Crear un objeto de comando o un objeto de conjunto de registros
  4. Ejecutar la instrucción SQL o procedimiento almacenado
  5. Cerrar la conexión para liberar los recursos de base de datos
  6. Y AH Sí, colocar algún error adecuado controla alrededor del código de acceso a datos

El primer problema era la cantidad absurd de código repetitivo que escribía. El problema segundo era que debía escribir el código correctamente cada vez única, porque olvidarse de cerrar una única conexión podría y Lamentablemente hizo, unidad de un sistema esenciales sin conexión bajo una carga elevada. Nadie desea ser el chico contraído cuyo código de protección de conexión de base de datos bajo, pero aún ocurrió con demasiada frecuencia.

En mi proyecto siguiente, mi compañero de trabajo y tiene más inteligentes. Hemos implementado una única clase que se realizan todos los programa de instalación, destrucción y código del objeto Connection de ADO de tratamiento de errores. Cualquier otra clase en el sistema puede interactuar con la base de datos mediante esta clase central para llamar a procedimientos almacenados. Hemos escrito mucho menos código y, mejor aún, no estaban sufrían por problemas que surgen de olvidarse de cerrar las conexiones de bases de datos porque tenemos que obtener dicho código de administración de conexión derecha sólo una vez.

Mi equipo ha consistía en crear una implementación grosero del patrón de repositorio que Media entre el dominio y los datos de las capas de asignación mediante una interfaz similar de la colección para obtener acceso a objetos de dominio.

Básicamente, el modelo de repositorio simplemente significa poner una fachada a través de su sistema de persistencia de modo que puede proteger el resto del código de la aplicación de tener que saber cómo funciona la persistencia. El proyecto actual utiliza un repositorio con una interfaz pública que presenta el aspecto en la figura 1 .

La figura 1 repositorio de interfaz

public interface IRepository 
{
  // Find an entity by its primary key
  // We assume and enforce that every Entity
  // is identified by an "Id" property of 
  // type long
  T Find<T>(long id) where T : Entity;

  // Query for a specific type of Entity
  // with Linq expressions.  More on this later
  IQueryable<T> Query<T>();
  IQueryable<T> Query<T>(Expression<Func<T, bool>> where);

  // Basic operations on an Entity
  void Delete(object target);
  void Save(object target);
  void Insert(object target);

  T[] GetAll<T>();
}

Cuando alguna clase en el sistema necesita tener acceso a un objeto de entidad, puede utilizar simplemente IRepository para recuperar esa entidad por su IDENTIFICADOR o consulta de una lista de objetos de entidad con una expresión de LINQ.

Cuando se utiliza la clase concreta de IRepository, que utiliza la biblioteca NHibernate O/CC, me extraer una cadena de conexión de la memoria, carga las definiciones de asignación de un ensamblado, crear el SessionFactory NHibernate (una vez y sólo una vez porque es una detección de gran rendimiento) y ajuste la interfaz ISession bajo nivel de NHibernate. Con algunos ayuda de la clase del repositorio, NHibernate administra problemas de ciclo de vida de conexión de base de datos.

Whew. Que es mucho material ocurre entre bastidores. Es algo bueno que ha trazará todos de la interacción directa con NHibernate detrás de la interfaz IRepository. No tengo que saber todo ese material NHibernate inicio sólo para cargar, guardar y consulta objetos. Aún mejor, puesto que cada clase depende de la interfaz de IRepository abstracta para acceso a datos y la persistencia, puede diapositiva en una implementación InMemoryRepository de IRepository que utiliza LINQ a objetos internamente para la base de datos de código auxiliar durante las pruebas.

Asignación de identidad

Veamos un escenario común. Dentro de una sola transacción lógica en algún tipo de sistema de envío, dispone de dos clases completamente diferentes que funcionan de forma independiente, pero ambas clases se necesita recuperar la misma entidad cliente durante la transacción. Lo ideal sería que desees sólo un objeto cliente único dentro de una sola transacción para cada cliente lógico para que cada objeto que está trabajando fuera de la coherencia de los datos.

En la persistencia tooling, evitar duplicadas referencias lógicas es el trabajo de la trama Identity Map. Como se indicó por Martin Fowler, una asignación de identidad garantiza que cada objeto se carga sólo una vez manteniendo todos los objetos cargado en un mapa y busca objetos mediante la asignación cuando se hace referencia a ellos.

Para la clase de cliente, podría crear una implementación naïve de asignación de identidad como la en la figura 2 . Me deliberadamente omitiendo subproceso bloqueo aquí sólo para simplificar el código. Una implementación real requeriría medidas de seguridad del subproceso adecuado.

Clase de Customer de asignación de identidad de la figura 2

public class CustomerRepository 
{
  public IDictionary<long, Customer> _customers = 
    new Dictionary<long, Customer>();

  public Customer FindCustomer(long id) 
  {
    if (_customers.ContainsKey(id)) 
  {
      return _customers[id];
    }

    var customer = findFromDatabase(id);
    _customers.Add(id, customer);

    return customer;
  }

  private Customer findFromDatabase(long id) 
  {
    throw new System.NotImplementedException();
  }
}

En este ejemplo, un objeto de cliente se identifica por su identificador. Cuando solicite una instancia de cliente por IDENTIFICADOR, el CustomerRepository comprueba primero un diccionario interno para ver si tiene que cliente concreto. Si es así, devuelve el objeto cliente existente. De lo contrario, CustomerRepository se recuperar los datos de la base de datos, crear un nuevo cliente, almacenar dicho objeto de cliente en su diccionario para las solicitudes posteriores y devolver el nuevo objeto de cliente.

Afortunadamente, normalmente no escribir este código manualmente porque cualquier herramienta de persistencia para adultos debe incluye esta característica. Tiene que tener en cuenta que esto sucede en segundo plano y ámbito de los objetos de compatibilidad con la persistencia en consecuencia. Muchos equipos utilizarán la característica de administración de ciclo de vida de una herramienta de inversión de control (StructureMap, Windsor Ninject y otros) para asegurarse de que todas las clases de una sola solicitud HTTP o subproceso se utilizan la misma subyacentes de asignación de identidad. El modelo de unidad de trabajo es otra forma para administrar un mapa de identidad única en varias clases en la misma transacción lógica.

Sólo para ilustrar este modelo más, figura 3 muestra un ejemplo de cómo funciona una asignación de identidad. El código se escribe en función de la arquitectura de mi proyecto actual. Las instancias de la interfaz de IRepository que se muestra en el código siguiente ajusta una sola NHibernate ISession, que a su vez se implementa el modelo de asignación de identidad. Cuando se ejecuta esta prueba el resultado es:

1 passed, 0 failed, 0 skipped, took 5.86 seconds.

La figura 3 mediante una asignación de identidad

[Test]
public void try_out_the_identity_map() 
{
  // All I'm doing here is getting a fully formed "Repository"
  // from an IoC container and letting an IoC tool bootstrap 
  // NHibernate offstage.
  IRepository repository = ObjectFactory.GetInstance<IRepository>();

  // Find the Address object where Id == 1
  var address1 = repository.Find<Address>(1);

  // Find the Address object where Id == 1 from the same Repository
  var address2 = repository.Find<Address>(1);

  // Requesting the same identified Address object (Id == 1) inside the 
  // same Repository / Identity Map should return the exact same
  // object
  address1.ShouldBeTheSameAs(address2);

  // Now, let's create a completely new Repository that has a 
  // totally different Identity Map
  IRepository secondRepository = ObjectFactory.GetInstance<IRepository>();

  // Nothing up my sleeve...
  repository.ShouldNotBeTheSameAs(secondRepository);

  var addressFromSecondRepository = secondRepository.Find<Address>(1);

  // and this is a completely different Address object, even though
  // it's loaded from the same database with the same Id
  addressFromSecondRepository.ShouldNotBeTheSameAs(address1);
}

Carga diferida Y inmediata

Una de las mejores cosas acerca del uso de una herramienta de persistencia es la capacidad para cargar un objeto de raíz (factura, tal vez), a continuación, navegar directamente a sus elementos secundarios (InvoiceLineItem) y objetos relacionados sólo mediante las propiedades de la clase principal. Sin embargo, o temprano vamos a tiene que interesa el rendimiento de la aplicación. Obtener un gráfico de objetos completo cuando es posible que necesite sólo el nivel superior objeto la mayor parte del tiempo o puede renunciar partes del objeto gráfico no es eficaz.

Eso es ACEPTAR. En ese caso, puede utilizar el modelo de carga diferida en el que aplazar inicializar el objeto hasta justo antes de que es necesario.

Vamos a poner esto en términos más concretos. Supongamos que tiene una clase denominada cliente que hace referencia a un objeto Address:

public class Customer : DomainEntity 
{
  // The "virtual" modifier is important.  Without it,
  // Lazy Loading can't work
  public virtual Address HomeAddress { get; set; }
}

En la mayoría Utilice que implican el objeto de cliente de los casos, el código no necesita la propiedad Customer.HomeAddress. En ese caso, puede configurar la asignación de base de datos para hacer la Customer.HomeAddress propiedad diferida cargada al igual que en esta asignación NHibernate Fluent:

public class CustomerMap : DomainMap<Customer> 
{
  public CustomerMap() 
  {
    // "References" sets up a Many to One
    // relationship from Customer to Address
    References(x => x.HomeAddress)
      .LazyLoad() // This marks the property as "Lazy Loaded"
      .Cascade.All();
  }
}

Con diferida de carga activado, se recupera el objeto de cliente sin los datos de direcciones. Sin embargo, tan pronto como cualquier llamador intenta tener acceso a la propiedad Customer.HomeAddress por primera vez, los datos se transparente cargará.

Tenga en cuenta el modificador virtual en la propiedad Customer.HomeAddress. No todas las herramientas de persistencia para ello, pero NHibernate implementa propiedades de cargadas diferidas mediante la creación de una subclase de cliente que reemplaza la propiedad HomeAddress para que sea lenta carga dinámica. La propiedad HomeAddress debe marcarse como virtual para permitir que una subclase se reemplaza la propiedad.

Por supuesto, existen otras veces cuando solicita un objeto y sabe que se necesita lo más probable es que sus elementos secundarios al mismo tiempo. En este caso, probablemente se opta por carga inmediata y dispone de los datos de elementos secundarios cargado al mismo tiempo que el elemento primario. Muchas herramientas de persistencia tendrá algún tipo de capacidad para optimizar la carga inmediata escenarios para recuperar una jerarquía de datos en un viaje de ida y vuelta en el base de datos única momento. Si necesita los datos Customer.HomeAddress la mayor parte del tiempo que utilizar un objeto de cliente, a continuación, sería mejor realizar inmediata de carga para obtener los datos al cliente y la dirección al mismo tiempo.

En este momento, debe repetir el maxim antigua que la única manera confiable ajustar una aplicación de rendimiento está usando una medida empírico de rendimiento con un generador de perfiles.

Modelo de proxy virtual

Carga diferida a menudo se implementa mediante un objeto de proxy virtual similar del objeto real que se va cargar más tarde. Suponga que el modelo de dominio incluye una clase denominada CustomerRepresentative que hace referencia a una lista de objetos de cliente. Parte de esa clase se muestra en Figura 4 .

La figura 4 CustomerRepresentative

public class CustomerRepresentative 
{
  // I can create a CustomerRepresentative directly
  // and use it with a normal List
  public CustomerRepresentative() 
  {
    Customers = new List<Customer>();
  }

  // Or I can pass an IList into it.
  public CustomerRepresentative(IList<Customer> customers) 
  {
    Customers = customers;
  }

  // It's not best practice to expose a collection
  // like I'm doing here, but it makes the sample
  // code simpler ;-)
  public IList<Customer> Customers { get; set; }
}

Hay muchas veces el sistema utiliza una instancia de CustomerRepresentative sin necesidad de la lista de clientes. En ese caso, puede crear simplemente una CustomerRepresentative con un objeto proxy virtual que busca como un IList <customer> objeto y utiliza esa clase de proxy virtual sin realizar ningún cambio en CustomerRepresentative absoluto. Esa clase de proxy virtual puede parecerse a figura 5 . A continuación, se pudo crear un objeto CustomerRepresentative con carga diferida tal como se muestra en la figura 6 .

La figura 5 virtual proxy CustomerRepresentative

public class VirtualProxyList<T> : IList<T> 
{
  private readonly Func<IList<T>> _fetcher;
  private IList<T> _innerList;
  private readonly object _locker = new object();

  // One way or another, VirtualProxyList needs to 
  // find the real list.  Let's just cheat and say
  // that something else will pass it a closure
  // that can find the real List
  public VirtualProxyList(Func<IList<T>> fetcher) 
  {
    _fetcher = fetcher;
  }

  // The first call to 
  private IList<T> inner 
  {
    get 
    {
      if (_innerList == null) 
      {
        lock (_locker) 
        {
          if (_innerList == null) 
          {
            _innerList = _fetcher();
          }
        }
      }

      return _innerList;
    }
  }


  IEnumerator IEnumerable.GetEnumerator() 
  {
    return inner.GetEnumerator();
  }

  public IEnumerator<T> GetEnumerator() 
  {
    return inner.GetEnumerator();
  }

  public void Add(T item) 
  {
    inner.Add(item);
  }

  // and the rest of the IList<T> implementation

} 

Figura 6 CustomerRepresentative de carga diferida

public class CustomerRepresentativeRepository 
{
  private readonly ICustomerRepository _customers;

  public CustomerRepresentativeRepository(
      ICustomerRepository customers) 
  {
    _customers = customers;
  }

  // This method will "find" a CustomerRepresentative, and
  // set up the Virtual Proxy for the Customers
  public CustomerRepresentative Find(long id) 
  {
    var representative = findRepresentative(id);
    representative.Customers = 
      new VirtualProxyList<Customer>(() => 
      _customers.GetCustomersForRepresentative(id));

    return representative;
  }
}

Como la mayoría de los patrones en este artículo, el proxy virtual no es algo que probablemente escribir a mano, pero tenga en cuenta que es no existe en el fondo de la herramienta de persistencia.

Realizar el paso siguiente

Cuando comenzó a programación con las tecnologías de Windows DNA, probablemente Dediqué una mitad de mi tiempo trabajando con código de ADO sin formato.Hoy en día, codificación de persistencia y la infraestructura es un porcentaje muy pequeño de tiempo de mi equipo.¿Por lo que lo cambiado en los años?Utilizamos las herramientas de persistencia y estos patrones de diseño para eliminar tanto de la codificación repetitivas que se utilizar para realizar.

La mayoría de estos patrones se toma de libreta de Martin Fowler, patrones de arquitectura de aplicaciones empresa.Recomienda leer este libro si tiene nada que ver con escribir aplicaciones de empresa.Debido a las limitaciones de longitud, no cubren algunos otros modelos importantes como la unidad de trabajo, las especificaciones y persistencia Ignorance.Además, hay un número de formas de utilizar las repositorio de modelo de diseño consideraciones y (un único genérico repositorio frente a específico, "restringir" clases de repositorio, si aún debe exponer un repositorio de "guardar" métodos, etc..) Podría incitar a investigar estos temas también y puede escribir un artículo de seguimiento para continuar con esta explicación.

Por último, me gustaría decir que el ecosistema de .NET es más rico que simplemente Entity Framework y LINQ para SQL.Estoy satisfecho con NHibernate como un asignador de datos que sabe relativamente poco acerca de persistencia.SubSonic es una implementación de registro Active popular para la programación de .NET.iBatis.Net es fenomenal para las bases de datos existentes o ocasiones en las que desea completo controlan sobre la creación de las instrucciones SQL.LLBLGen Pro es una herramienta muy madura con capacidades de consultas únicas.Muchas de las otras herramientas también tienen la posibilidad de utilizar consultas LINQ.

Envíe sus preguntas y comentarios ammpatt@Microsoft.com.

Jeremy Miller,MVP de Microsoft para C#, también es autor de la (StructureMap de código abiertostructuremap.SourceForge.NET) herramienta para inyección de dependencia con .NET y el próximo (StoryTellerstoryteller.tigris.org) herramienta para probar FIT supercharged en. NET.Visite su blog "Los desarrolladores de árbol de sombra" forma parte del sitio CodeBetter.