使用ViewModels设计MVC存储库

时间:2012-08-13 22:28:37

标签: asp.net-mvc model-view-controller repository viewmodel

我想创建一个存储库类,将我的数据逻辑与控制器分开。我使用ViewModel来表示一些数据,这些数据将填充来自不同表的数据。

以下是我的一些问题:

  1. 对于像GetAll()这样的方法,我会返回IQueryable<MyViewModel>还是IQueryable<Entity>?如果我返回视图模型,我如何处理可以提取数千条记录的GetAll()
  2. 我是否为自定义ViewModel类创建一个构造函数,该类将实体作为参数来进行映射? (我仍然不熟悉automapper,所以只需要从设计的角度理解如何做到这一点)
  3. 同样,我主要担心的是像GetAll()这样的方法,可以提取很多记录。如果我做了一个foreach循环来将每个实体转换为一个ViewModel似乎需要很多开销。我的想法是将自定义ViewModel类中的引用放到IQueryable<Entity>以从集合中访问,并让ListViewModel只有索引器或类似引用集合属性的引用。

3 个答案:

答案 0 :(得分:5)

  

1)对于像GetAll()这样的方法,我会返回IQueryable还是IQueryable?如果我返回viewmodels,我该如何处理一个可以提取数千条记录的GetAll()?

IQueryable<Entity>。存储库不处理视图模型。可以将存储库视为在单独的类库中定义的内容,该库不引用您的视图模型所在的ASP.NET MVC应用程序。 ASP.NET MVC应用程序引用了这个库。

  

2)我是否为自定义ViewModel类创建了一个构造函数,它将Entity作为参数来进行映射? (我仍然不熟悉automapper,所以只需从设计的角度理解如何做到这一点)

没有。不要在视图模型中创建构造函数,尤其是当您希望控制器操作将这些视图模型作为操作参数时(考虑POST操作)。原因是默认模型绑定器将不再知道如何实例化视图模型,您将不得不编写自定义模型绑定器。

所以AutoMapper或手动映射。

手动映射的示例,您可以从以下开始:

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

一旦你厌倦了将这段代码写入AutoMapper:

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

或者如果您编写了一个自定义操作过滤器,它使用OnActionExecuted事件来提取传递给视图的域模型,使用AutoMapper将其映射到视图模型,并将模型替换为视图的视图模型,您可以进一步简化重复代码:

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

同样,我主要担心的是像GetAll()这样的方法会拉很多   记录。如果我做了一个foreach循环来将每个实体转换成一个   ViewModel似乎有很多开销。

不要担心。拉取记录将比循环和映射到视图模型慢一些。

答案 1 :(得分:1)

有很多不同的方法可以做到这一点,但简单来说,我会为你的GetAll()方法返回一个IEnumerable<T>。但是,您可能希望以某种方式实现分页。您可能希望设置一个通用存储库,为大多数方案执行基本数据访问并返回Enumerable。您可以保留一个应该为更复杂的查询保留的方法,并返回IQueryable<T>。基本的精简实现可能如下所示。

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

}

接口看起来像

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

}

以上内容可以作为简单通用存储库的开头。拥有实体后,您不需要AutoMapper,它只会让生活更轻松,因为许多ViewModel具有与您的实体相同的属性。您只需定义一个新的ViewModel或ViewModel列表,并自行映射属性。

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

*这有点打字,抱歉有任何错别字:)

答案 2 :(得分:1)

我认为您可能对视图模型及其目的有误解。您不需要为数据库中的每个实体创建视图模型,因为您似乎想要这样做;您只需为要渲染的每个视图创建一个视图模型。因此,术语“视图模型” - 它以模型的形式组织数据,您的视图可以强类型化。

例如,您不希望为GetAll()返回的每个实体创建单独的视图模型。在显示所有记录的gridview的简单场景中,您可能只需要一个具有一个属性的viewmodel:

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

您将在控制器中填充此视图模型

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

请参阅Rachael Appel的this blog post进行真正简洁的讨论。