DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Data
  4. Using RenderAction: Is Breaking the MVC Pattern Worth It?

Using RenderAction: Is Breaking the MVC Pattern Worth It?

Christopher Bennage user avatar by
Christopher Bennage
·
May. 25, 12 · Interview
Like (1)
Save
Tweet
Share
8.37K Views

Join the DZone community and get the full member experience.

Join For Free
project silk . there are four separate items displayed.

the primary concern of the page is displaying a list of vehicles. however it also displays some statistics and a set of reminders. i labeled the stats and reminders as orthogonal because they are (in a sense) independent of the primary concern. finally, there is the ambient data of the currently logged in user. i call this data ambient because we expect it to be present on all the pages in the application.

it’s a common practice in mvc-style applications to map a single controller action to a view. that is, it is the responsibility of a single action to produce everything that is needed to render a particular web page.

the difficulty with this approach is that other pages often need to render the same orthogonal data. let’s examine the code for the action invoked by \vehicle\list.

public actionresult list()
{
    addcountrylisttoviewbag();

    var vehicles = using<getvehiclelistforuser>()
        .execute(currentuserid);

    var imminentreminders = using<getimminentremindersforuser>()
        .execute(currentuserid, datetime.utcnow);

    var statistics = using<getfleetsummarystatistics>()
        .execute(currentuserid);

    var model = new dashboardviewmodel
                    {
                        user = currentuser,
                        vehiclelistviewmodel = new vehiclelistviewmodel(vehicles),
                        imminentreminders = imminentreminders,
                        fleetsummarystatistics = statistics
                    };

    return view(model);
}

disregarding how you might feel about the using<t> method to invoke commands and other such details, i want you to focus on the fact that the controller is composing a model. we generate a number of smaller viewmodels and then compose them into an instance of dashboardviewmodel. the class dashboardviewmodel only exists to tie together the four, otherwise independent data.

personally, i prefer to avoid classes like dashboardviewmodel and simply rely on dynamic typing in the view. however, others feel strongly about having intellisense support in the view.

project silk had separate actions just to serve up json:

public jsonresult jsonlist()
    {
        var list = using<getvehiclelistforuser>()
            .execute(currentuserid)
            .select(x => tojsonvehicleviewmodel(x))
            .tolist();

        return json(list);
    }

you’ll notice that both jsonlist and list use the same getvehiclelistforuser command for retrieving their data. jsonlist also projected the data to a slightly different viewmodel.

reducing the code

while reevaluating this code for project liike , we decided to employ content negotiation . that is, we wanted a single endpoint, such as \vehicle\list, to return different representations of the data based upon a requested format. if the browser requested json, then \vehicle\list should return a list of the vehicles in json. if the browser requested markup, then the same endpoint should return html.

first, we needed to eliminate the differences between the json viewmodel and the html viewmodel. without going deep into details, this wasn’t hard to do. in fact, it revealed that we had some presentation logic in the view that should not have been there. the real problem was that i wanted the action to look more like this:

public actionresult list()
{
    var vehicles = using<getvehiclelistforuser>()
        .execute(currentuserid);

    return new contenttypeawareresult(vehicles);
}

only, the view still needed the additional data of statistics and reminders. how should the view get it?

we decided to use renderaction . renderaction allows a view to invoke another action and render the results into the current view.

we needed to break out the other concerns into their own actions. for the sake of example, we’ll assume they are both on the vehiclecontroller and named reminders and statistics. each of these action would be responsible for getting a focused set of data. then in the (imaginary) view for list we could invoke the actions like so:

// list.cshtml 
<ul>
@foreach (var vehicle in model)
{
    <li>@vehicle.name</li>
}
</ul>

<section role="reminders">
@{ html.renderaction( "reminders", "vehicle") }
</section>

<section role="statistics">
@{ html.renderaction( "statistics", "vehicle") }
</section>

note that each action has it’s on associated view.

the value of using renderaction is that we where able to create very simple actions on our controllers. we were also able to reuse the actions for rendering both markup and json.

a secondary benefit is the separation of concerns. for example, because we moved the responsibility of composition from the controller into the view, a designer could now revise the view for the \vehicle\list without needing to touch the code. they could remove any of the orthogonal concerns or even add new ones without introducing any breaking changes.

the downside

there are a few caveats with this approach.

first, don’t confuse renderaction with renderpartial . renderaction is for invoking a completely independent action, with its own view and model. renderpartial is simply for renders a view based on a model passed to it (generally derived from the main viewmodel).

secondly, avoid using renderaction to render a form. it’s likely won’t work the way you’d expect.this means that any form rendering will need to occur in your primary view.

thirdly, using renderaction breaks the model-view-controller pattern. what i mean is that, in mvc, it’s assumed that the view does nothing more than render a model. controllers invoke a view, and not vice versa. using renderaction breaks this rule. personally, i have no problem breaking the rule when it results in code that is more simple and more easily maintained. isn’t that the whole point of best practices anyway?

Data (computing) IT

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Is JavaScript Slice? Practical Examples and Guide
  • How to Use Buildpacks to Build Java Containers
  • How To Best Use Java Records as DTOs in Spring Boot 3
  • When Should We Move to Microservices?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: