我想创建一个存储库类,将我的数据逻辑与控制器分开。我使用ViewModel来表示一些数据,这些数据将填充来自不同表的数据。
以下是我的一些问题:
GetAll()
这样的方法,我会返回IQueryable<MyViewModel>
还是IQueryable<Entity>
?如果我返回视图模型,我如何处理可以提取数千条记录的GetAll()
? 同样,我主要担心的是像GetAll()
这样的方法,可以提取很多记录。如果我做了一个foreach循环来将每个实体转换为一个ViewModel似乎需要很多开销。我的想法是将自定义ViewModel类中的引用放到IQueryable<Entity>
以从集合中访问,并让ListViewModel只有索引器或类似引用集合属性的引用。
答案 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进行真正简洁的讨论。