Pregunta

Estoy luchando para definir un método de clase que puebla y devuelve una colección de instancias. La cuestión no sé cómo moverse es que tengo atributos particulares para poblar.

Vamos a usar el ejemplo de una clase de libro. No quiero que el código para establecer directamente (por ejemplo) la disponibilidad de un libro. Quiero que el código tenga que utilizar un método de verificación en una instancia de libro. Así que tenemos algo como:

public class Book
{
  private int ID;
  private bool pAvailableForCheckout;

  public string Title { get; set; }
  public bool AvailableForCheckout { get { return pAvailableForCheckout } }

  // instance methods

  public Book(int BookID)
  {
     // Load book from DB by ID
  }
  public CheckOut()
  {
     // perform everything involved with checking a book out
  }
  // .. other methods like saving a book, checking out books etc.

  // class method

  public static List<Book> FindAll()
  {
     // load Dataset of books
     // foreach record in DB, use the Book(int BookID) constructor and add to List
     // return list of books
  }
}

Por lo tanto, no puedo hacer uso de esto en mi código:

foreach(Book curBook in Book.FindAll())
  { /* do something with book */ }

El problema con la aplicación anterior es que tengo que usar N + 1 resultados de la base de datos para cargar todos los libros en lugar de sólo 1 consulta. ¿Cómo consigo alrededor de esto?

Estoy seguro de que esta es la programación de 101, pero necesitaba preguntar.

¿Fue útil?

Solución

Se puede crear un constructor protegido que rellena las propiedades privadas directamente.

Otros consejos

El foreach debe iterar sobre una lista de objetos que ya están instanciados, que no tendrán que conectarse a la base de datos.

Es necesario crear un constructor que acepte las propiedades de su libro objeto para que pueda crear una instancia de un libro de un conjunto existente de datos en lugar de un nuevo golpe a la base de datos.

así:

Constructor:

public book (String title, String avail) {Title=title...}

Y en el método

public static void FindAll()
{
List<Books> books = new List<books>();
using (Sqlconnection conn = new sqlconnection(connstring))
using (sqlcommand cmd = new SqlCommand("select title, available from book ", conn)
{
  SqlDatareader dr = cmd.executereader()
  while (dr.read())
  {
    books.add(new Book(dr["title"], dr["avail"])
  }

}

foreach(Book curBook in Book.FindAll())
  { /* do something with book */ }

}

Para un ejemplo algo extremo en su pureza ideológico:

En primer lugar, una interfaz para clases que se pueden recuperar los objetos de tipo T de la base de datos dada su identificación:

interface IAdapter<T>
{
   T Retrieve(int id);
}

Ahora, la clase Book, que ya no expone un constructor público, pero en lugar de un método estático que utiliza un IAdapter<Book> para recuperar el libro de la base de datos:

public class Book
{
    public static IAdapter<Book> Adapter { get; set; }

    public static Book Create(int id)
    {
       return Adapter.Retrieve(id);
    }

    // constructor is internal so that the Adapter can create Book objects
    internal Book() { }

    public int ID { get; internal set; }
    public string Title { get; internal set; }
    public bool AvailableForCheckout { get; internal set; }

}

Tienes que escribir la clase que implementa IAdapter<Book> a sí mismo, y asignar Book.Adapter a una instancia del mismo modo que Book.Create() será capaz de tirar las cosas desde la base de datos.

Yo digo "pureza ideológica" porque este diseño hace cumplir una separación bastante rígida de preocupaciones: no hay nada en la clase Book que sabe cómo hablar con la base de datos - o incluso que hay es una base de datos.

Por ejemplo, aquí está una posible implementación de IAdapter<Book>:

public class DataTableBookAdapter : IAdapter<Book>
{
   public DataTable Table { get; set; }
   private List<Book> Books = new List<Book>();

   Book Retrieve(int id)
   {
      Book b = Books.Where(x => x.ID = id).FirstOrDefault();
      if (b != null)
      {
         return b;
      }

      BookRow r = Table.Find(id);
      b = new Book();

      b.ID = r.Field<int>("ID");
      b.Title = r.Field<string>("Title");
      b.AvailableForCheckout = r.Field<bool>("AvailableForCheckout");

      return b;
   }
}

Algunos otra clase es responsable de crear y llenar el DataTable que esta clase utiliza. Se podría escribir una aplicación diferente que utiliza un SqlConnection hablar con la base de datos directamente.

Puede incluso escribir esto:

public IAdapter<Book> TestBookAdapter : IAdapter<Book>
{
   private List<Book> Books = new List<Book>();

   public TestBookAdapter()
   {
      Books.Add(new Book { ID=1, Title="Test data", AvailableForCheckout=false };
      Books.Add(new Book { ID=2, Title="Test data", AvailableForCheckout=true };
   }

   Book Retrieve(int id)
   {
      return Books.Where(x => x.ID == id);
   }
}

Esta aplicación no utiliza una base de datos en absoluto - que haría uso de este al escribir pruebas unitarias para la clase Book

.

Tenga en cuenta que estas dos clases de mantener una propiedad privada List<Book>. Esto garantiza que cada vez que llame Book.Create() con un determinado ID, volver la misma instancia Book. Hay un argumento que se hizo para hacer de esto una característica de la clase Book lugar -. Que serías una propiedad privada List<Book> estática en Book y escribir la lógica para hacer que el método Create mantenerlo

Se utiliza el mismo enfoque para empujar datos de vuelta a la base de datos -. Añadir Update, Delete y métodos Insert a IAdapter<T> y ponerlas en práctica en sus clases de adaptador, y tienen Book llamar a esos métodos en el momento apropiado

¿Por qué no se comprueba la disponibilidad del libro en el lado de la base de datos utilizando un SQL en la declaración?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top