Ayende @ Rahien

Refunds available at head office

My baseline ASP.Net MVC modifications

We start by killing ViewData. I don't want it, and I want to fail hard if someone is trying to use it:

image

Likewise in the views, I hate ViewData.Model, so I remove it from there as well:

image

For Javascript, I don't want to use <script/> tags, they are prone to path problems, and I might need to go back and change them (using compression, combining scripts, etc). I use this approach:

image

The page title is set by the view, the way it should:

image

Or, I just set it up in the master page, if I don't care that much about changing it.

Comments

Javier Lozano
12/29/2008 05:59 PM by
Javier Lozano

Why don't you want/need them?

Travis
12/29/2008 06:10 PM by
Travis

Ok, so how should the Title be set in the MVC world? I see your view markup, but that does not tell much.

El Guapo
12/29/2008 06:34 PM by
El Guapo

Hmm it doesn't like my attempt at posting angle brackets.

<script type="text/javascript" src="<%= Url.Content("~/Content/MicrosoftAjax.js") %>

Ayende Rahien
12/29/2008 07:18 PM by
Ayende Rahien

El Guapo,

You do notice how much MORE you have to write, right?

And that this doesn't solve the problem of allow us to replace the implementation with contactenated script (reducing network round trips).

Steve
12/29/2008 07:54 PM by
Steve

I prefer the MvcContrib's 'ScriptInclude'

Also, there is a 'Stylesheet'

RafalG
12/29/2008 08:00 PM by
RafalG

Since we're both digging in asp.net mvc, I've got a question. Suppose you have a view and a model. The model contains some data retrieved from database (let's use NHibernate). In the controller you retrieve a data record (let it be Order), put it into ViewData (or View.Model in your case) and want to display it. But you need to display more than just the record - you need to include also some information from objects referenced by Order, for example Order's items and ordering user's address. So you need to load the related objects from database. NHibernate would do that silently, but you are in a view - so the data session used in controller is already closed.

How should such situation be handled?

option 1 - pre-load everything in controller and use detached data only

option 2 - open another data session in view

option 3 - somehow share the session between controller & view

I know option 1 is the preferred way, but it incurs a significant overhead (you need to manually do what OR-mappers can do automatically). Is (3) acceptable?

Aaron Jensen
12/29/2008 10:38 PM by
Aaron Jensen

RafalG,

You should not give entities to your view. They do not belong there. Your view is a reporting concern and should be treated as such. Get the data from the database however you need to (projection queries from NHibernate, mapping from NHib entities, in memory cache, etc) and pass DTOs to your view.

This is a common misconception about MVC, that your business model is the same model that you give the view. This is not the case and will lead to disaster and questions such as this.

configurator
12/29/2008 10:45 PM by
configurator

Your Model property is available in MS MVC vNext, isn't it?

Paul Blamire
12/29/2008 11:14 PM by
Paul Blamire

@Aaron - It sounds like you are saying you should always use DTOs to pass data to the view (I may have misunderstood you, maybe it's just because your comment is strongly worded).

Whilst I agree with the theoretical argument for using DTOs to represent messages to and from the view, how do you feel about only creating DTOs for views where the business model cannot be used directly due to say transformations or aggregations etc.

It just seems to me unless the project demands complete architectural purity (e.g. it's a huge enterprise app) then a DTO explosion might not be the right thing for a typical small project.

Rafal G
12/29/2008 11:16 PM by
Rafal G

Aaron,

I don't agree with you. I'm using OR mapper to build my data objects - in my application this is the model - the 'M' in 'MVC'. So views should be able to use it to do their work.

I really don't see why you call this a 'misconception'. Are you trying to tell me that DAOs are not a proper 'Model' and I should build some other 'Model' suitable for views? Why? Because one model is too simple? Can you really tell my 'model' is bad without even seeing it?

My hypothesis is that asp.net mvc is just 'incompatible' with OR mappers and that's why you and many others suggest that we can't use OR-mapped entities for model and must create some detached data structures instead. Why detached? Because data sessions are not allowed in views. Andy why aren't they allowed? I don't know - probably because of some religious approach to 'design patterns' forbidding use of any logic in views. This is very unnatural and counter-effective IMHO.

Will Shaver
12/29/2008 11:51 PM by
Will Shaver

This also helps catch really cool code errors like this one:

if (String.IsNullOrEmpty(password))

{

ModelState.AddModelError("password", "You must specify a password.");

}

if (ViewData.ModelState.IsValid)

{

...

The above code is lifted unchanged from the AccountController from the default project. You'll note that one is using ModelState directly and the other is using ViewData.ModelState. Now you might think that these would be linked to two different variables. They aren't.

Controller.cs in asp.net mvc bits:

    public ModelStateDictionary ModelState {

        get {

            return ViewData.ModelState;

        }

    }

So we're just typing out ViewData the second time for fun.

Aaron Jensen
12/29/2008 11:57 PM by
Aaron Jensen

Rafal G,

You don't have to agree with me. I'm just giving you my point of view based on the 3+ years of experience I have in working with a web MVC framework on a very large application.

If you are using an ORM to map to a simple reporting database that is already formatted for display on views then no, I don't see a problem with this, and the answer to your original question would be to make sure that you join fetch the associations you need (assuming you're doing a master detail or tree view of some sort because otherwise you shouldn't be loading collections). I'd have to know the specifics of the view you're actually trying to create to tell you whether or not you're doing it wrong.

I say it's a misconception because it is a misconception. If you are using an ORM to map your domain--your business domain you should not be giving them to your view. Business entities are for behaviors. Being displayed is not a behavior, it is a reporting concern.

As for your hypothesis, there is nothing preventing you from keeping your session open during the view rendering. It's just a horrendously awful thing to do for multiple reasons. I know this because we used to do it.

Your view should not be lazy loading anything, that's just leaky, hard to test and bad. Your view should also not do things like this: Order.OrderInfo.Customer.Name. If you want the customer's name, query for it. Ideally you give your view exactly what it needs to render without it having to do any deep digging. If you want to display customer name and order total, you don't give it Order. You give it a DTO or DataSet or whatever that has just those two things. Anything else is wasteful, leaky, hard to test, ugly, difficult for a designer working on your view to understand and just bad.

Again, feel free to disagree/not listen to me/whatever

Paul,

I hear you. We started out with a small app and we passed entities to the views. We're now a big app and it's killing us. You can use something simple like a DataSet instead of a DTO if you're worried about DTO explosion. I personally wouldn't worry about this though. I've found that there really aren't any reasons to avoid having many DTOs--they don't need to be tested and they're easy to maintain.

Ayende Rahien
12/30/2008 01:48 AM by
Ayende Rahien

RafalG,

I usually go for (3), with the caveat that I watch for SELECT N +1 and optimize accordingly

Ayende Rahien
12/30/2008 01:53 AM by
Ayende Rahien

Aaron,

Trying to query for everything the view need usually means that you need to do a lot more work to give it exactly what it needs.

I tend to pass view model objects that contains references to the entities. This allow me to chose how I want to deal with them, and yes, the views would use the model directly.

That said, there are times when presentation concerns dictate that I wouldn't use the entities directly, because it is easier to get the data in another format, in which case I am all for it.

Andreas &#214;hlund
12/30/2008 08:18 AM by
Andreas Öhlund

Rafael

I agree with Aaron that the M in MVC is usually not a suitable domain model for medium to large size projects.

Check out this great thread on the DDD-group for an in-depth discussion of this:

tech.groups.yahoo.com/.../9176

ripper
12/31/2008 11:34 AM by
ripper

I have seen DTO and Entities referenced before as the same thing,

DTO just has gets/sets?

Entity has adidtional logic? Can also be ActiveRecord (load/save methods)?

Can someone here explain the difference?

Araron: "We started out with a small app and we passed entities to the views. We're now a big app and it's killing us".

So what kind iof stuff is in your Entitiies that killed your app?!

buggs
12/31/2008 02:32 PM by
buggs

Interesting discussion here guys. I'd love to see a simple example project of these concepts.

So if you were building this blog page, there is a number of things to display, the blog entry, coments, blog stats, categories, archives etc. You'd probably end up with domain models like BlogEntry, Comment etc etc. Then you'd need a DTO say BlogShowDTO which would have a list of BlogEntryDTOs, list of CommentDTO etc?

Does a service layer or repository pattern do the conversion between the domain model and DTOs, or does the controller do that?

cheers

Ayende Rahien
12/31/2008 02:45 PM by
Ayende Rahien

Buggs,

I would say it is the responsibility of the controller to do this conversion

andy
12/31/2008 05:49 PM by
andy

do you use for this conversion a object to object mapper or do you do it manually? Which mapper would you suggest?

Aaron Jensen
01/02/2009 09:53 AM by
Aaron Jensen

ripper,

When I say entities I'm referring to DDD Entities. If you haven't read Eric Evan's DDD book, I'd highly suggest it. I'm not a fan of ActiveRecord--I use repositories, so no, I generally don't have load/save on my entities. Your head is in the right place though, the things persisted to the database are the things that should not be given to the view. DTOs are simply dumb objects. No real behavior, just getters/setters.

It's not killing our app, it's just messy. It couples your domain to your views which isn't a good thing, especially when you're using brail and you don't have any compile time checking. The story is a bit better in Spark, but on the project i'm using Spark on there is a Application service/messaging boundary between the controller and the domain, so the controller won't ever even see an entity.

andy,

We used to use mapping classes, but they just seemed like a lot of overhead (interface/class/injection/etc) for what little they did. We now use extension methods (.ToDto() and such). We find that cleans up the syntax quite a bit and is lighter.

ripper
01/02/2009 10:40 AM by
ripper

Aaron,

Thanks for the response. I think we're on the same wavelength.

We use DTO's everywhere:

  1. Business Logic deals in DTO's.

  2. Data Persistence Layer deals in DTO's and collections of.

  3. NHibernate just maps DTO's to tables, etc.

  4. Web Services "talk DTO's".

  5. "Views" just render DTO's/collect information to fill DTO's.

ActiveRecord is a no-go for us: our DTO's are consumed via web servcies from Java/PHP. Hence no way we will expose such crap like .Load/.Save!

Nicomn
01/06/2009 02:53 PM by
Nicomn

Aaron, thanks for these nice explanations about DTOs usage.

Just a question : in wich layer do you put your DTOs ? The domain layer, or the layer which contains the controllers ?

I tend to put the DTOs in the domain layer, because it enables me to instantiate the DTOs directly from the DB, using NHibernate projections.

ripper
01/06/2009 05:59 PM by
ripper

Nicomm,

We place ours in "Commons" assembly that can then be referenced by whoever (biz logic, data providers, UI views, web services proxies, rich clients that send/receive such DTO's via web services, etc.).

Hence they remain "neutral" in an assembly that does not reference any System webform/winform/webservice/data namespaces.

Christiaan VdP
01/08/2009 01:18 PM by
Christiaan VdP

@Ayende,

Can you provide then an example on how you retrieve the necessary data (model?) in the view?

TIA

Christiaan VdP
01/08/2009 01:21 PM by
Christiaan VdP

Ok,

forget my comment, I've looked at the example code better

note to myself: hit myself!

[ICR]
01/12/2009 09:35 PM by
[ICR]

You might want to also add the Obsolete attribute so that compiler warnings are issued when ViewData is used.

Comments have been closed on this topic.