2012-08-13 20 views
6

Quiero crear una clase de repositorio para separar mi lógica de datos de mis controladores. Estoy usando un ViewModel para representar algunos datos que se llenarán con datos de diferentes tablas.Diseñando un repositorio de MVC usando ViewModels

Aquí hay algunas preguntas que tengo:

  1. Por un método como GetAll(), puedo devolver un IQueryable<MyViewModel> o IQueryable<Entity>? Si devuelvo viewmodels, ¿cómo puedo lidiar con un GetAll() que extrae miles de registros?
  2. ¿Creé un constructor para mi clase personalizada de ViewModel que toma la Entidad como parámetro para hacer la asignación? (Todavía estoy familiarizado con AutoMapper por lo que sólo necesita un entendimiento sobre cómo hacer esto desde un punto de vista del diseño)

Una vez más, mi principal preocupación es un método como GetAll() que tirar de muchos registros. Si hiciera un bucle foreach para traducir cada Entidad a un ViewModel parecería una gran sobrecarga. Mi idea era poner una referencia dentro de la clase personalizada de ViewModel en el IQueryable<Entity> para acceder desde la colección, y hacer que ListViewModel solo tenga indexadores o algo así que haga referencia a la propiedad de la colección.

Respuesta

5

1) Para un método como GetAll(), ¿devuelvo IQueryable o IQueryable? Si devuelvo viewmodels, ¿cómo puedo lidiar con GetAll() que extrae miles de registros?

IQueryable<Entity>. El repositorio no trata con modelos de vista. Piense en el repositorio como algo que se define en una biblioteca de clases separada que no hace referencia a su aplicación ASP.NET MVC, que es donde viven sus modelos de vista. Es la aplicación ASP.NET MVC que hace referencia a esta biblioteca.

2) ¿Creo un constructor para mi clase personalizada de ViewModel que toma la Entidad como un parámetro para hacer la asignación? (Todavía estoy familiarizado con AutoMapper por lo que sólo necesita un entendimiento sobre cómo hacer esto desde un punto de vista del diseño)

No. No crear constructores en sus modelos de vista sobre todo si quieres que las acciones del controlador de tome esos modelos de vista como parámetros de acción (piense en una acción POST). La razón de esto es que la carpeta de modelo predeterminada ya no sabrá cómo crear una instancia de su modelo de vista y tendrá que escribir carpetas de modelo personalizadas.

So AutoMapper o mapa manualmente.

Ejemplo con el mapeo manual, que es lo que se podría comenzar con:

public ActionResult SomeAction() 
{ 
    IEnumerable<Entity> entities = Repository.GetAll(); 
    IEnumerable<MyViewModel> model = entities.Select(x => new MyViewModel 
    { 
     Prop1 = x.Prop1, 
     Prop2 = x.Prop2, 
     ... 
    }); 
    return View(model); 
} 

Y una vez que se enferma de escribir este código de movimiento para AutoMapper:

public ActionResult SomeAction() 
{ 
    IEnumerable<Entity> entities = Repository.GetAll(); 
    IEnumerable<MyViewModel> model = Mapper.Map<IEnumerable<Entity>, IEnumerable<MyViewModel>>(entities); 
    return View(model); 
} 

o si se escribe una acción personalizada filtro que usa el evento OnActionExecuted para extraer el modelo de dominio que se pasó a la vista, asignarlo al modelo de vista utilizando AutoMapper y sustituir el modelo con el modelo de vista para la vista, puede simplificar aún más el código repetitivo:

[AutoMap(typeof(IEnumerable<Entity>), typeof(IEnumerable<MyViewModel>))] 
public ActionResult SomeAction() 
{ 
    IEnumerable<Entity> entities = Repository.GetAll(); 
    return View(entities); 
} 

Una vez más, mi principal preocupación es un método como GetAll() que tirar de muchos registros. Si hice un bucle foreach para traducir cada entidad a un ViewModel parece una gran sobrecarga.

No se preocupe por eso. Tirando de sus registros será una magnitud más lenta que el bucle y la asignación al modelo de vista.

1

Hay muchas maneras diferentes de hacerlo, pero para comenzar simplemente, devolvería IEnumerable<T> para su método GetAll(). Sin embargo, es probable que desee implementar la paginación de alguna manera. Es posible que desee configurar un repositorio genérico que acceda a los datos básicos para la mayoría de los escenarios y devuelva un Enumerable. Puede reservar un único método que debe reservarse para consultas más complicadas y devuelve un IQueryable<T>. La implementación básica simplificada puede verse a continuación.

public class Repository<T> : IRepository<T> where T : class 
{ 
    internal ObjectContext _objectContext; 
    internal ObjectSet<T> _objectSet; 

    public Repository(ObjectContext objectContext) 
    { 
     _objectContext = objectContext; 
     _objectSet = objectContext.CreateObjectSet<T>(); 
    } 

    public IQueryable<T> GetQuery() 
    { 
     return _objectSet; 
    } 

    public IEnumerable<T> GetAll() 
    { 
     return GetQuery().ToList(); 
    } 

    public IEnumerable<T> Find(Func<T, bool> where) 
    { 
     return _objectSet.Where<T>(where); 
    } 

    public T Single(Func<T, bool> where) 
    { 
     return _objectSet.SingleOrDefault<T>(where); 
    } 

    public List<T> Page<TKey>(Expression<Func<T, bool>> where, int page, int pagesize, Expression<Func<T, TKey>> orderBySelector, bool ascending) 
    { 
     return ascending 
      ? GetQuery().Where(where).OrderBy(orderBySelector).Skip((page - 1) * pagesize).Take(pagesize).ToList() 
      : GetQuery().Where(where).OrderByDescending(orderBySelector).Skip((page - 1) * pagesize).Take(pagesize).ToList(); 
    } 


    public void Delete(T entity) 
    { 
     _objectSet.DeleteObject(entity); 
    } 

    public void Add(T entity) 
    { 
     _objectSet.AddObject(entity); 
    } 

} 

y la interfaz se vería

public interface IRepository<T> where T : class 
{ 
    IQueryable<T> GetQuery(); 

    IEnumerable<T> GetAll(); 

    IEnumerable<T> Find(Func<T, bool> where);  

    T Single(Func<T, bool> where); 

    List<T> Page<TKey>(Expression<Func<T, bool>> where, int page, int pagesize, Expression<Func<T, TKey>> orderBySelector, bool ascending); 

    void Delete(T entity); 

    void Add(T entity); 

} 

la función puede anteriormente como el inicio de un simple repositorio genérico. Una vez que tiene sus entidades, no necesita AutoMapper, simplemente hace la vida más fácil ya que muchos Modelos de Vista tienen las mismas propiedades que sus entidades. Simplemente puede definir un nuevo modelo de vista o una lista de modelos de vista y asignar las propiedades por su cuenta.

List<ViewModel> vm = new List<ViewModel>(); 

foreach (var e in entities) 
{ 
    ViewModel v = new ViewModel(); 
    v.something = e.something; 
    // perform the rest 
    vm.Add(v); 
} 

* Eso fue un poco para escribir, lo siento por ninguna errata :)

1

que cree que puede tener una mala interpretación del modelo de vista y su propósito. No es necesario crear un modelo de vista para cada entidad en su base de datos, como parece que quiere hacer; usted acaba de crear un modelo de vista para cada vista que desea representar. De ahí el término "modelo de vista": organiza los datos en la forma de un modelo al que su opinión puede ser fuertemente tipada.

No desea, por ejemplo, querer crear un modelo de vista separado para cada entidad devuelta por GetAll(). En un escenario simple de mostrar un gridview de todos los registros que probablemente sólo necesita un único modelo de vista con una propiedad:

public class MyViewModel 
    {  
     public List<MyRecord> AllRecords {get;set;} 
    } 

Se podría llenar esta vista del modelo en el controlador

public ActionResult SomeAction() 
{ 
    var viewmodel = new MyViewModel{AllRecords = GetAll()}; 
    return View(viewModel); 
} 

un vistazo a this blog post por Rachael Appel para una discusión muy concisa.

Cuestiones relacionadas