I've been trying to come up with an extensible search pattern for an mvc site I'm working on and I wanted some base functionality to minimize what I need to do to extend the search by adding another controller option.
I'm having a problem determining where I would go from having a base abstract searchable controller that presumes a particular entity and model type. The Mapper reference refers to AutoMapper and just maps the entity to the model. Everything seems to work ok but I'm not sure what the view would look like in terms of the model definition because the type is variable for the base model SearchModel. I could implement my own view in each controller with the #model SearchModel but I'm just wondering how much "free" plumbing I can get away with.
public abstract class SearchableController<TModel, TEntity, TRepository> : Controller where TRepository : ISearchableRepository<TEntity> where TEntity : class where TModel : class
{
protected TRepository _repository;
[HttpGet]
public ActionResult Search(string q, int? page)
{
int pageNumber = page.HasValue ? page.Value : 1;
PagedList<TEntity> entities = _repository.Search(q, 10, page);
Mapper.CreateMap<TEntity, TModel>();
var results = new List<TModel>();
foreach (TEntity entity in entities)
{
var entityModel = Mapper.Map<TEntity, TModel>(entity);
results.Add(entityModel);
}
var model = new SearchModel<TModel>();
model.searchPattern = q;
model.pageNumber = pageNumber;
model.Results = new StaticPagedList<TModel>(results, entities.PageNumber, entities.PageSize, entities.TotalItemCount);
return View(model);
}
}
I guess I don't really understand your problem.
Your model would be an #model SearchModel<WhateverYourTModelIs>. What "free plumbing" are you referring to?
Maybe i'm confused, but you have to define the static types for your derived controllers, so what is your problem with defining static type views for them?
Yeah I think that is the route I ended up with. Then I just use a master view that handles the common view portion of the search model like the search pattern rebind to a search input, pagination, current page number etc...
Related
I have read that in an MVC application one should keep controllers "thin". But when I put code to fetch data in the ViewModel I feel it's less intuitive to locate, meaning when I am troubleshooting I generally tend to look in my controller first (or maybe that's my real problem). Also I find one can reuse the same VM for many different things if you pass data through the controller.
Am I violating some big principle or causing performance issues?
For example, compare these dummy snippets with two approaches, both seem to work just fine:
Assume a Repository called repositoryy with a method GetCourses() that fetches a list of Courses.
1) ViewModel fetches the data and controller directs traffic:
public CourseViewModel
{
private MyProjectEntities db = new MyProjectEntities();
Repository repository = new Repository();
{
public CourseViewModel()
{
Courses = db.Course.ToList();
}
public List<Course> Courses {get; set;}
}
}
public class CourseController : Controller
{
public ActionResult Index()
{
var courseviewmodel = new CourseViewModel();
return View(courseviewmodel);
}
}
2) Controller fetches data, passes to ViewModel and then to the View:
public CourseViewModel
{
public List<Course> Courses {get; set;}
}
public class CourseController : Controller
{
public ActionResult Index()
{
var courseviewmodel = new CourseViewModel();
courseviewmodel.Courses = repository.GetCourses.ToList();
return View(courseviewmodel);
}
}
The second option. Btw there's no 'more correct MVc approach'. There is the MVC separation and that's that. Your first option breaks that separation, because the view model does the controller's work instead of being the 'dumb' dto holding the view's data. You don't want to couple the view model to the model.
In MVVM though, the view model acts a bit like a controller, however that approach is best suited for desktop aps, not web apps.
I want to create a repository class to separate out my data logic from my controllers. I am using a ViewModel to represent some data that will be filled with data from different tables.
Here are some questions I have:
For a method like GetAll(), do I return an IQueryable<MyViewModel> or IQueryable<Entity>? If I return viewmodels, how do I cope with a GetAll() that pulls thousands of records?
Do I create a constructor for my custom ViewModel class that takes the Entity as a parameter to do the mapping? (I'm still unfamiliar with automapper so just need an understanding on how to do this from a design point of view)
Again, my main concern is a method like GetAll() which would pull many records. If I did a foreach loop to translate each Entity into a ViewModel seems like a lot of overhead. My thought was to put a reference inside the custom ViewModel class to the IQueryable<Entity> to access from the collection, and have the ListViewModel just have indexers or something like that which reference the collection property.
1) For a method like GetAll(), do I return an IQueryable or IQueryable? If I return viewmodels, how do I cope with a GetAll() that pulls thousands of records?
IQueryable<Entity>. The repository doesn't deal with view models. Think of the repository as something that is defined in a separate class library that doesn't reference your ASP.NET MVC application which is where your view models live. It is the ASP.NET MVC application that references this library.
2) Do I create a constructor for my custom ViewModel class that takes the Entity as a parameter to do the mapping? (I'm still unfamiliar with automapper so just need an understanding on how to do this from a design point of view)
No. Don't create constructors in your view models especially if you want your controller actions to take those view models as action parameters (think of a POST action). The reason for this is that the default model binder will no longer know how to instantiate your view model and you will have to write custom model binders.
So AutoMapper or manually map.
Example with manual mapping which is what you could start with:
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);
}
And once you get sick of writing this code move to AutoMapper:
public ActionResult SomeAction()
{
IEnumerable<Entity> entities = Repository.GetAll();
IEnumerable<MyViewModel> model = Mapper.Map<IEnumerable<Entity>, IEnumerable<MyViewModel>>(entities);
return View(model);
}
or if you write a custom action filter that uses the OnActionExecuted event to pull the domain model that was passed to the view, map it to the view model using AutoMapper and substitute the model with the view model for the view, you could further simplify the repetitive code:
[AutoMap(typeof(IEnumerable<Entity>), typeof(IEnumerable<MyViewModel>))]
public ActionResult SomeAction()
{
IEnumerable<Entity> entities = Repository.GetAll();
return View(entities);
}
Again, my main concern is a method like GetAll() which would pull many
records. If I did a foreach loop to translate each Entity into a
ViewModel seems like a lot of overhead.
Don't be concerned about that. Pulling your records will be a magnitude slower than looping and mapping to the view model.
There are many different ways to do this, but to start simply, I would return an IEnumerable<T> for your GetAll() method. However, you'll probably want to implement paging in some fashion. You might want to setup a generic repository that does your basic data access for most scenarios and returns an Enumerable. You could reserve a single method that should be reserved for more complicated queries and returns an IQueryable<T>. The basic stripped down implementation might look like below.
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);
}
}
And the Interface would look like
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);
}
The above can function as the beginning of a simple generic repository. Once you have your entities, you don't need AutoMapper, it just makes life easier as many ViewModels have the same properties as your entities. You can simply define a new ViewModel or List of ViewModels and map the properties on your own.
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);
}
*That was quite a bit to type, sorry about any typos :)
I think you may have a misunderstanding of the view model and it's purpose. You don't need to create a view model for every entity in your database, as it seems you want to do; you just create a view model for each view you want to render. Hence the term "view model"--it organizes the data in the form of a model that your view can be strongly typed to.
You wouldn't, for example, want to create a separate view model for every entity returned by a GetAll(). In a simple scenario of displaying a gridview of all records you would probably just need a single viewmodel with one property:
public class MyViewModel
{
public List<MyRecord> AllRecords {get;set;}
}
You would populate this view model in the controller
public ActionResult SomeAction()
{
var viewmodel = new MyViewModel{AllRecords = GetAll()};
return View(viewModel);
}
Have a look at this blog post by Rachael Appel for a really concise discussion.
I'm starting to implement a service layer to my MVC project to thin down some bloated controllers (it also has repository / unitofwork pattern).
My question is if you have a complicated view model for a page with lots of child objects etc, and quite a lot of logic going on behind the scenes (to give you an idea the controller the original developer wrote had almost 4000 lines of code!!) is it OK to have multiple services going off doing their thing? or should I just have one big ReportService which does everything?
My controller is starting to look like this? and if I carry on I could end up having quite a lot of different services being called to build up the view model.
Does this look OK or is it starting to go in the wrong direction?
public ViewResult Index(int? reportId)
{
// get the base report object
var reportService = new ReportService();
var report = reportService.GetByReportId(reportId);
var model = Mapper.Map<Report, ReportViewModel>(report);
// get the current active user
var userService = new UserService();
var user = userService.GetCurrentUser();
model.User = Mapper.Map<User, ReportViewModel.UserViewModel>(user);
// get the first unread message
var messageService = new MessageService();
var message = messageService.GetFirstUnread(user.Id);
model.Message = Mapper.Map<Message, ReportViewModel.MessageViewModel>(message);
// get the category navigation
var categoryService = new CategoryService();
var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);
model.CategoryNavigation = Mapper.Map<IEnumerable<Category>, IEnumerable<ReportViewModel.CategoryNavigationViewModel>>(categoryNavigation);
return View(model);
}
It's fine to have multiple small services in your controller. However, there is one thing that's wrong here:
You services should be available through the entire controller and injected through the constructor to achieve loose-coupling.
So something like this:
private readonly IReportService _reportService;
private readonly IUserService _userService;
public SomeConstructor(IReportService reportService, IUserService userService, etc.)
{
_reportService = reportService;
_userService = userService;
// etc
}
That does look like a good approach, an alternative approach would to be split some of this up by using Child Actions - the best solution will depend upon your specific use case though.
If, for example, the ViewModel property CategoryNavigation was being used by the view to create a sort of navigation 'widget' that might be useful in several different Views, you might be better spliting this off into a ChildAction e.g.
[ChildActionOnly]
public ActionResult CategoryNavigationWidget(int reportId)
{
// get the category navigation
var categoryService = new CategoryService();
var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);
return PartialView(categoryNavigation);
}
Any View could then render that ChildAction by going:
#{ Html.RenderAction("CategoryNavigationWidget", "Report",
new { reportId = Model.ReportId }); }
Whether or not this is a good idea will probably depend upon whether or not the 'widget' is reusable.
In a heavy enviroment application, we have Users, Locations, bla bla bla... and we use in many situations a call to a service where we retrieve the list of countries.
Where is the 'best practice' or 'proper way' to implement this. This method is called in several places and many objects has a List<CountryVO> property.
Specially considering using Razor views an often having to add this property to ModelViews
The solution is using DAL / BLL / SERVICE / UI[s] architecture.
Real Example:
public class User {
...
...
public List<DeliveryZoneVO> DeliveryZones {get;set;}
public User() {
...
DeliveryZones = service.GetDeliveryZones().ToList();
}
}
The class DeliveryZoneVO comes from a webservice, so one property is
int IdCountry
The class User have a list of DeliveryZoneVO as presented on the class, the 'problem' here, is since it retrieves the data from a web service, I only have the ID of the country.
When I prepare the data in the controller to send to the View:
UserModelView userMV = new UserModelView();
userMV.user = service.GetUserById(1);
ViewData.Model = userMV;
BUT, inside userMV.user, I have DeliveryZones with a list of DeliveryZoneVO objects with IdCountries.
In the view, when I do (for example) :
#DisplayFor(m => m.user.DeliveryZones)
I want to show the Country Name, only have the ID. So i need a reference somewhere.. the question lies in where should that data needs to be placed that is considered BEST PRACTICES.
Is having in all modelview (in the case of the example, the UserModelView() the property Countries with a List ?
A good thing because this kind of issues is to have a BaseController class that derived from controller, and all the other controllers you have derived from it.
in the BaseController put a static List<CountryVO> property with getter only, this way it will be initialized once and will be accessible to all of your's controllers and views(If you pass it with the ViewModel or ViewBag).
Example:
public class BaseController : Controller
{
private static List<CountryVO> _allCountries;
public static List<CountryVO> AllCountries
{
get{ return _allCountries ?? _GetCountriesFromSomeWhere();}
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
ViewBag.AllCountries = this.AllCountries;
return View();
}
}
I would create a partial view that is responsible for just rendering the country list. Then any changes to how the list is rendered is can be made in just one place. I would create a model class that encapsulates calling the service to get the countries. Assuming that the country list is fairly static you could handle caching of the information in the model class for less calls to the service and better performance. Below is an example of a method in the model that gets the country list from the server cache if it is available.
const string cacheId = "deliveryZones";
public List<DeliveryZoneVO> GetDeliveryZones()
{
List<DeliveryZoneVO> deliveryZones = (List<DeliveryZoneVO>)HttpRuntime.Cache.Get(cacheId);
if (deliveryZones == null)
{
deliveryZones = service.GetDeliveryZones().ToList();
System.Web.HttpContext.Current.Cache.Insert(cacheId, deliveryZones);
}
return deliveryZones;
}
My current problem is that I have a partial view that I want to determine what model is being used by it.
I have had to deal with a few strange scenarios for my project so I will try to outline it here, maybe someone can offer a better way to do this.
I am designing something like the Google iGoogle page. A main page with multiple widgets that are able to move around or be configured as needed. The current system loads the actual widget's data asynchronously view a POST to a controller within my application. That controller will either render a partial view to HTML that can be returned (and then loaded into the page view JQUERY) or just straight HTML/JavaScript that is stored in a database.
This was working fine for me, I had a model for the widgets that holds a dictionary of options that are described via the database, and then used by the partial view. The problem came when I wanted to pass data to a partial view. The best solution I could come up with was having the controller determine which model the partial view in question uses, have some function that will fill the model, and then pass it, along with the partial view, to the function that will render it to HTML within the controller.
I realize this is an odd scenario for MVC (the layers are blending...) and any advice on fundamental design, or implementation of this would be greatly appreciated.
I am currently using MVC3/Razor. Feel free to ask any other questions.
I prototyped a possible solution to this, because it seemed like a fun problem. I hope it's useful to you.
Models
First, the models. I decided to create two 'widgets', one for news, and one for a clock.
public class NewsModel
{
public string[] Headlines { get; set; }
public NewsModel(params string[] headlines)
{
Headlines = headlines;
}
}
public class ClockModel
{
public DateTime Now { get; set; }
public ClockModel(DateTime now)
{
Now = now;
}
}
Controller
My controller doesn't know anything about the views. What it does is returns a single model, but that model has the ability to dynamically fetch the right model as required by the view.
public ActionResult Show(string widgetName)
{
var selector = new ModelSelector();
selector.WhenRendering<ClockModel>(() => new ClockModel(DateTime.Now));
selector.WhenRendering<NewsModel>(() => new NewsModel("Headline 1", "Headline 2", "Headline 3"));
return PartialView(widgetName, selector);
}
Delegates are used so that the correct model is only created/fetched if it is actually used.
ModelSelector
The ModelSelector that the controller uses is pretty simple - it just keeps a bag of delegates to create each model type:
public class ModelSelector
{
private readonly Dictionary<Type, Func<object>> modelLookup = new Dictionary<Type, Func<object>>();
public void WhenRendering<T>(Func<object> getter)
{
modelLookup.Add(typeof(T), getter);
}
public object GetModel(Type modelType)
{
if (!modelLookup.ContainsKey(modelType))
{
throw new KeyNotFoundException(string.Format("A provider for the model type '{0}' was not provided", modelType.FullName));
}
return modelLookup[modelType]();
}
}
The Views - Simple solution
Now, the easiest way to implement a view would be:
#model MvcApplication2.ModelSelector
#using MvcApplication2.Models
#{
var clock = (ClockModel) Model.GetModel(typeof (ClockModel));
}
<h2>The time is: #clock.Now</h2>
You could end here and use this approach.
The Views - Better solution
That's pretty ugly. I wanted my views to look like this:
#model MvcApplication2.Models.ClockModel
<h2>Clock</h2>
#Model.Now
And
#model MvcApplication2.Models.NewsModel
<h2>News Widget</h2>
#foreach (var headline in Model.Headlines)
{
<h3>#headline</h3>
}
To make this work, I had to create a custom view engine.
Custom view engine
When a Razor view is compiled, it inherits a ViewPage<T>, where T is the #model. So we can use reflection to figure out what type the view wanted, and select it.
public class ModelSelectorEnabledRazorViewEngine : RazorViewEngine
{
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var result = base.CreateView(controllerContext, viewPath, masterPath);
if (result == null)
return null;
return new CustomRazorView((RazorView) result);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var result = base.CreatePartialView(controllerContext, partialPath);
if (result == null)
return null;
return new CustomRazorView((RazorView)result);
}
public class CustomRazorView : IView
{
private readonly RazorView view;
public CustomRazorView(RazorView view)
{
this.view = view;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
var modelSelector = viewContext.ViewData.Model as ModelSelector;
if (modelSelector == null)
{
// This is not a widget, so fall back to stock-standard MVC/Razor rendering
view.Render(viewContext, writer);
return;
}
// We need to work out what #model is on the view, so that we can pass the correct model to it.
// We can do this by using reflection over the compiled views, since Razor views implement a
// ViewPage<T>, where T is the #model value.
var compiledViewType = BuildManager.GetCompiledType(view.ViewPath);
var baseType = compiledViewType.BaseType;
if (baseType == null || !baseType.IsGenericType)
{
throw new Exception(string.Format("When the view '{0}' was compiled, the resulting type was '{1}', with base type '{2}'. I expected a base type with a single generic argument; I don't know how to handle this type.", view.ViewPath, compiledViewType, baseType));
}
// This will be the value of #model
var modelType = baseType.GetGenericArguments()[0];
if (modelType == typeof(object))
{
// When no #model is set, the result is a ViewPage<object>
throw new Exception(string.Format("The view '{0}' needs to include the #model directive to specify the model type. Did you forget to include an #model line?", view.ViewPath));
}
var model = modelSelector.GetModel(modelType);
// Switch the current model from the ModelSelector to the value of #model
viewContext.ViewData.Model = model;
view.Render(viewContext, writer);
}
}
}
The view engine is registered by putting this in Global.asax.cs:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ModelSelectorEnabledRazorViewEngine());
Rendering
My home view includes the following lines to test it all out:
#Html.Action("Show", "Widget", new { widgetName = "Clock" })
#Html.Action("Show", "Widget", new { widgetName = "News" })
One option would be to extend the idea of partial requests in your application. Steve Sanderson has a fantastic example of this, although the post relates to MVC 1 & 2. I think it would still help in you v3, but I haven't investigated v3 to see if the MVC team implemented their own version. In your asynch scenario, you'll need to toy with the implementation a bit, perhaps change the PartialRequest definition to accept different information as needed, but I think this might be a good start. The net result would be better isolation of concerns, allowing individual controllers to manage a particular type of partial, and in turn be better aware of the model Type you want to work with.
I'm not 100% sure that this is what you'd be looking for, but the [ChildActionOnly] attribute can be added to a method within your controller. That requires that the method can only be called from a partial view. Then you can set up your partial view for that method that basically resembles one of your widgets. Check out the MVC Music Store example here:
http://www.asp.net/mvc/tutorials/mvc-music-store-part-10
What about a dynamic view model? Layouts in MVC3 use them, and maybe you can use something similar for your purposes:
Dynamic in C# 4.0: Introducing the ExpandoObject
Fun With Method Missing and C# 4
Dynamic View Page, MVC without a View Model
I blogged about doing exactly this. Please see http://blogs.planetcloud.co.uk/mygreatdiscovery/?tag=/widget
Essentially I built out a similar widget system. The posts also cover how to handle configuration of those widgets. This makes use of the dynamic support in Mvc3 so that any model object can be passed to the view, from a single controller action.
By default all widgets have a collection of KVP properties (I believe this is what the OP has). So for a simple widget we get access to those properties from within the view. I used for a widget that displayed some html (where the html was stored in one of those properties).
However, for more complex widgets we implement IWidgetWithDisplayModel. This tells us that before we pass the loaded widget back to the view, we need to "build" our display model.
Here's the controller action that does that. Check the posts for full details.
[HttpGet]
public ActionResult Get(string name)
{
var widget = widgetService.GetWidgetBySystemName(name, true);
if (widget == null)
return Content(string.Format("Widget [{0}] not found!", name));
if (!this.ViewExists(widget.WidgetName))
return Content(string.Format("A template for widget [{0}] was not found.", widget.WidgetName));
if (widget is IWidgetWithDisplayModel) {
(widget as IWidgetWithDisplayModel).CreateDisplayModel();
}
return PartialView(widget.WidgetName, widget);
}