views:

526

answers:

5

I am currently playing around with the Asp.Net mvc framework and loving it compared to the classic asp.net way. One thing I am mooting is whether or not it is acceptable for a View to cause (indirectly) access to the database?

For example, I am using the controller to populate a custom data class with all the information I think the View needs to go about doing its job, however as I am passing objects to the view it also can cause database reads.

A quick pseudo example.

public interface IProduct
{
    /* Some Members */

    /* Some Methods */
    decimal GetDiscount();
}

public class Product : IProduct
{
    public decimal GetDiscount(){ ... /* causes database access */ }
}

If the View has access to the Product class (it gets passed an IProduct object), it can call GetDiscount() and cause database access.

I am thinking of ways to prevent this. Currently I am only coming up with multiple interface inheritance for the Product class. Instead of implementing just IProduct it would now implement IProduct and IProductView. IProductView would list the members of the class, IProduct would contain the method calls which could cause database access.

The 'View' will only know about the IProductView interface onto the class and be unable to call methods which cause data access.

I have other vague thoughts about 'locking' an object before it is passed to the view, but I can foresee huge scope for side effects with such a method.

So, My questions:

  • Are there any best practices regarding this issue?
  • How do other people using MVC stop the View being naughty and doing more to objects than they should?
+1  A: 

One thing I am mooting is whether or not it is acceptable for a View to cause (indirectly) access to the database?

I've often asked the same question. So many things we access on the Model in Stack Overflow Views can cause implicit database access. It's almost unavoidable. Would love to hear others' thoughts on this.

Jeff Atwood
The model in MVC is not the powerful domain model. Or at least it shouldn't be. It's a lightweight view model - a hierarchy of null-safe, flattened DTOs and other presentation objects. Your controllers and other services build the view model; the view simply renders it.
Matt Hinze
A: 

The model should not have methods ("actions") that consist of data access. That's the DAL's concern. YOu could have a discount percent property stored in the product class and have the GetDiscount method return a simple calculation such as Price * (100 - discountPercent) or something like this.

I disconnect my business entities (Product in your example) from data access. That's the repository (in my case) 's concern.

Andrei Rinea
I have methods in my Model to prevent the situation whereby you needlessly do expensive database actions simply to 'populate' your object before you allow any other code to touch it. What if in this particular circumstance you never use the discount value but still retrieved it?
Ash
I should probably also add that GetDiscount() is probably a bad choice of method name, maybe GetCategories() might have been more suitable. I.e. something directly related to the object the class represents but not physically part of the database table for it.
Ash
@Ash: For the GetCategories() example, I would have populated my ViewData with a MenuDocument object that contained all of the information necessary to render a menu of categories. The MenuDocument itself could be populated by the ActiveRecord-style domain classes.
Nicholas Piasecki
+3  A: 

Your view isn't really causing data access. The view is simply calling the GetDiscount() method in a model interface. It's the model which is causing data access. Indeed, you could create another implementation of IProduct which wouldn't cause data access, yet there would be no change to the view.

Model objects that do lazy loading invariably cause data access when the view tries to extract data for display.

Whether it's OK is down to personal taste and preference.

However, unless you've got a good reason for lazy loading, I'd prefer to load the data into the model object and then pass that "ready-baked" for the view to display.

Mike Scott
I want to provide ready baked data to the view, thats what I'm doing in the controller (I use builders to create my view model classes), I (perhaps prematurely) want to cater for situations in the future where I may want all db access in a transaction for example - not easy to control in a view!
Ash
There's no catering required for this ready-baked data (sorry, for the terrible pun ;-)) ). Your view need not be aware of anything-it just consumes the model object. It's up to the controller and model to negotiate transactions etc. later if/when required.
Mike Scott
+1  A: 

If you keep your domain objects "persistent ignorant", then you don't have this problem. That is, instead of having getDiscount inside your Product class, why not just have a simple property called Discount? This would then be set by your ORM when loading the instance of the Product class from the database.

Kevin Pang
Exactly what I proposed a few minutes earlier...
Andrei Rinea
Even with POCO, PI model objects, iteration and other operations on them can cause side effects. Beware using the domain model as a view model.
Matt Hinze
A: 

I've built a site in MonoRail before that sometimes has methods that trigger data access from the view. I try to avoid it because when it fails, it can fail in unusual and unfixable ways (I can't really try/catch in an NVelocity template, for example). It's totally not the end of the world--I wrote well-abstracted PHP sites for years that accessed the database from the view and they still work well enough because most of the time if something blows up, you're just redirecting to a "Something didn't work"-type error page anyway.

But yeah, I try to avoid it. In a larger sense, my domain model usually doesn't trickle all the way down into the view. Instead, the view is rendering Document objects that are unashamedly just strongly-typed data dumps, with everything pre-formatted, whipped, crushed, and puree'd to the point where the view just has to spit out some strings with some loops and if/else's, transform the number "4" into 4 star images, etc. This document is usually returned by a Web service that sits in front of the beautiful domain model, or it's just a simple struct that is constructed in the controller and passed along as part of the ViewData. If a domain object is used directly, then it usually doesn't do anything to explicitly trigger data access; that's handled by a collection-like repository that the view doesn't have access to and the domain objects usually don't have access to, either.

But you don't have to do it that way. You could just be discplined enough to just not call those methods that touch the database from the view.

Nicholas Piasecki
I am doing a hybrid on this, but I have a number of view helper methods which themselves may format data based on the contents of the objects supplied, so I really don't want to flatten/format my data in the controller - I feel that is the job of the view!
Ash
@Ash: Fair enough assessment; again, a lot of this is personal taste. The Document object usually *builds* itself based on the domain model, but that object is instanced/constructed in the controller. For a Web architecture, I find that controllers inevitably do some such interpreting for the view.
Nicholas Piasecki
This is getting closer to a better view model. As long as this approach is easily testable it seems fine.
Matt Hinze