[英]Designing an MVC repository using ViewModels
我想創建一個存儲庫類來將我的數據邏輯與控制器分開。 我使用ViewModel來表示將填充來自不同表的數據的一些數據。
以下是我的一些問題:
GetAll()
這樣的方法,我是否返回IQueryable<MyViewModel>
或IQueryable<Entity>
? 如果我返回viewmodels,我該如何處理一個可以提取數千條記錄的GetAll()
? 同樣,我主要擔心的是像GetAll()
這樣的方法會拉出很多記錄。 如果我做了一個foreach循環來將每個實體轉換為一個ViewModel似乎需要很多開銷。 我的想法是將自定義ViewModel類中的引用放入IQueryable<Entity>
以從集合中訪問,並使ListViewModel只具有索引器或類似引用集合屬性的索引器。
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似乎需要很多開銷。
不要擔心。 拉取記錄將比循環和映射到視圖模型慢一些。
有很多不同的方法可以做到這一點,但簡單來說,我會為你的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);
}
*這是相當多的打字,抱歉任何錯別字:)
我認為你可能對視圖模型及其目的有誤解。 您不需要為數據庫中的每個實體創建視圖模型,因為您似乎想要這樣做; 您只需為要渲染的每個視圖創建一個視圖模型。 因此,術語“視圖模型” - 它以模型的形式組織數據,您的視圖可以強類型化。
例如,您不希望為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的這篇博客文章 ,進行一次非常簡潔的討論。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.