asp.net mvc 5 高级编程第5版


Pro ASP.NET MVC 5 Adam Freeman 2 Pro ASP.NET MVC 5 Copyright © 2013 by Adam Freeman This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. Exempted from this legal reservation are brief excerpts in connection with reviews or scholarly analysis or material supplied specifically for the purpose of being entered and executed on a computer system, for exclusive use by the purchaser of the work. Duplication of this publication or parts thereof is permitted only under the provisions of the Copyright Law of the Publisher’s location, in its current version, and permission for use must always be obtained from Springer. Permissions for use may be obtained through RightsLink at the Copyright Clearance Center. Violations are liable to prosecution under the respective Copyright Law. ISBN-13 (pbk): 978-1-4302-6529-0 ISBN-13 (electronic): 978-1-4302-6530-6 Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made. The publisher makes no warranty, express or implied, with respect to the material contained herein. President and Publisher: Paul Manning Lead Editor: Ewan Buckingham Technical Reviewer: Fabio Claudio Ferracchiati Editorial Board: Steve Anglin, Mark Beckner, Ewan Buckingham, Gary Cornell, Louise Corrigan, Jim DeWolf, Jonathan Gennick, Jonathan Hassell, Robert Hutchinson, Michelle Lowman, James Markham, Matthew Moodie, Jeff Olson, Jeffrey Pepper, Douglas Pundick, Ben Renow-Clarke, Dominic Shakeshaft, Gwenan Spearing, Matt Wade, Steve Weiss Coordinating Editor: Kevin Shea Copy Editor: Larissa Shmailo Compositor: SPi Global Indexer: SPi Global Artist: SPi Global Cover Designer: Anna Ishchenko Distributed to the book trade worldwide by Springer Science+Business Media New York, 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800- SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com, or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation. For information on translations, please e-mail rights@apress.com, or visit www.apress.com. Apress and friends of ED books may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also available for most titles. For more information, reference our Special Bulk Sales–eBook Licensing web page at www.apress.com/bulk-sales. Any source code or other supplementary material referenced by the author in this text is available to readers at www.apress.com/9781430257882. For detailed information about how to locate your book’s source code, go to www.apress.com/source- code/. 3 Dedicated to my lovely wife, Jacqui Griffyth. 4 Contents at a Glance About the Author About the Technical Reviewer Chapter 1: Putting ASP.NET MVC in Context Chapter 2: Your First MVC Application Chapter 3: The MVC Pattern Chapter 4: Essential Language Features Chapter 5: Working with Razor Chapter 6: Essential Tools for MVC Chapter 7: SportsStore: A Real Application Chapter 8: SportsStore: Navigation Chapter 9: SportsStore: Completing the Cart Chapter 10: SportsStore: Mobile Chapter 11: SportsStore: Administration Chapter 12: SportsStore: Security & Finishing Touches Chapter 13: Deployment Chapter 14: Overview of MVC Projects Chapter 15: URL Routing Chapter 16: Advanced Routing Features Chapter 17: Controllers and Actions Chapter 18: Filters Chapter 19: Controller Extensibility Chapter 20: Views Chapter 21: Helper Methods Chapter 22: Templated Helper Methods Chapter 23: URL and Ajax Helper Methods Chapter 24: Model Binding Chapter 25: Model Validation Chapter 26: Bundles Chapter 27: Web API and Single-page Applications Index 5 Contents About the Author About the Technical Reviewer Chapter 1: Putting ASP.NET MVC in Context Understanding the History of ASP.NET What Is Wrong with ASP.NET Web Forms? Web Development Today Web Standards and REST Agile and Test-Driven Development Ruby on Rails Node.js Key Benefits of ASP.NET MVC MVC Architecture Extensibility Tight Control over HTML and HTTP Testability Powerful Routing System Built on the Best Parts of the ASP.NET Platform Modern API ASP.NET MVC Is Open Source What Do I Need to Know? What Is the Structure of This Book? Part 1: Introducing ASP.NET MVC 5 Part 2: ASP.NET MVC in Detail What’s New in this Edition? Where Can I Get the Example Code? What Software Do I Need for This Book? Credits Summary Chapter 2: Your First MVC Application Preparing Visual Studio Creating a New ASP.NET MVC Project Adding the First Controller Understanding Routes Rendering Web Pages Creating and Rendering a View Adding Dynamic Output Creating a Simple Data-Entry Application 6 Setting the Scene Designing a Data Model Linking Action Methods Building the Form Setting the Start URL Handling Forms Adding Validation Styling the Content Completing the Example Summary Chapter 3: The MVC Pattern The History of MVC Understanding the MVC Pattern Understanding the Domain Model The ASP.NET Implementation of MVC Comparing MVC to Other Patterns Building Loosely Coupled Components Using Dependency Injection Using a Dependency Injection Container Getting Started with Automated Testing Understanding Unit Testing Understanding Integration Testing Summary Chapter 4: Essential Language Features Preparing the Example Project Adding the System.Net.Http Assembly Using Automatically Implemented Properties Using Object and Collection Initializers Using Extension Methods Applying Extension Methods to an Interface Creating Filtering Extension Methods Using Lambda Expressions Using Automatic Type Inference Using Anonymous Types Performing Language Integrated Queries Understanding Deferred LINQ Queries Using Async Methods Applying the async and await Keywords Summary Chapter 5: Working with Razor 7 Preparing the Example Project Defining the Model Defining the Controller Creating the View Working with the Model Object Working with Layouts Creating the Layout Applying a Layout Using a View Start File Demonstrating Shared Layouts Using Razor Expressions Inserting Data Values Setting Attribute Values Using Conditional Statements Enumerating Arrays and Collections Dealing with Namespaces Summary Chapter 6: Essential Tools for MVC Preparing the Example Project Creating the Model Classes Adding the Controller Adding the View Using Ninject Understanding the Problem Adding Ninject to the Visual Studio Project Getting Started with Ninject Setting up MVC Dependency Injection Creating Chains of Dependency Specifying Property and Constructor Parameter Values Using Conditional Binding Setting the Object Scope Unit Testing with Visual Studio Creating the Unit Test Project Creating the Unit Tests Running the Unit Tests (and Failing) Implementing the Feature Testing and Fixing the Code Using Moq Understanding the Problem Adding Moq to the Visual Studio Project Adding a Mock Object to a Unit Test Creating a More Complex Mock Object 8 Summary Chapter 7: SportsStore: A Real Application Getting Started Creating the Visual Studio Solution and Projects Installing the Tool Packages Adding References Between Projects Setting Up the DI Container Running the Application Starting the Domain Model Creating an Abstract Repository Making a Mock Repository Displaying a List of Products Adding a Controller Adding the Layout, View Start File and View Setting the Default Route Running the Application Preparing a Database Creating the Database Defining the Database Schema Adding Data to the Database Creating the Entity Framework Context Creating the Product Repository Adding Pagination Displaying Page Links Improving the URLs Styling the Content Installing the Bootstrap Package Applying Bootstrap Styles to the Layout Creating a Partial View Summary Chapter 8: SportsStore: Navigation Adding Navigation Controls Filtering the Product List Refining the URL Scheme Building a Category Navigation Menu Correcting the Page Count Building the Shopping Cart Defining the Cart Entity Adding the Add to Cart Buttons Implementing the Cart Controller Displaying the Contents of the Cart Summary 9 Chapter 9: SportsStore: Completing the Cart Using Model Binding Creating a Custom Model Binder Completing the Cart Removing Items from the Cart Adding the Cart Summary Submitting Orders Extending the Domain Model Adding the Checkout Process Implementing the Order Processor Registering the Implementation Completing the Cart Controller Displaying Validation Errors Displaying a Summary Page Summary Chapter 10: SportsStore: Mobile Putting Mobile Web Development in Context Doing Nothing (Or As Little As Possible) Using Responsive Design Creating a Responsive Header Creating a Responsive Product List Creating Mobile Specific Content Creating a Mobile Layout Creating the Mobile Views Summary Chapter 11: SportsStore: Administration Adding Catalog Management Creating a CRUD Controller Creating a New Layout Implementing the List View Editing Products Creating New Products Deleting Products Summary Chapter 12: SportsStore: Security & Finishing Touches Securing the Administration Controller Creating a Basic Security Policy Applying Authorization with Filters Creating the Authentication Provider Creating the Account Controller Creating the View 10 Image Uploads Extending the Database Enhancing the Domain Model Creating the Upload User Interface Elements Saving Images to the Database Implementing the GetImage Action Method Displaying Product Images Summary Chapter 13: Deployment Preparing Windows Azure Creating the Web Site and Database Preparing the Database for Remote Administration Creating the Schema Deploying the Application Summary Chapter 14: Overview of MVC Projects Working with Visual Studio MVC Projects Creating the Project Understanding MVC Conventions Debugging MVC Applications Preparing the Example Project Launching the Visual Studio Debugger Causing the Visual Studio Debugger to Break Using Edit and Continue Using Browser Link Summary Chapter 15: URL Routing Preparing the Example Project Creating the Example Controllers Creating the View Setting the Start URL and Testing the Application Introducing URL Patterns Creating and Registering a Simple Route Using the Simple Route Defining Default Values Using Static URL Segments Defining Custom Segment Variables Using Custom Variables as Action Method Parameters Defining Optional URL Segments Defining Variable-Length Routes Prioritizing Controllers by Namespaces 11 Constraining Routes Constraining a Route Using a Regular Expression Constraining a Route to a Set of Specific Values Constraining a Route Using HTTP Methods Using Type and Value Constraints Defining a Custom Constraint Using Attribute Routing Enabling and Applying Attribute Routing Creating Routes with Segment Variables Applying Route Constraints Using a Route Prefix Summary Chapter 16: Advanced Routing Features Preparing the Example Project Simplifying the Routes Adding the Optimization Package Updating the Unit Test Project Generating Outgoing URLs in Views Using the Routing System to Generate an Outgoing URL Targeting Other Controllers Passing Extra Values Specifying HTML Attributes Generating Fully Qualified URLs in Links Generating URLs (and Not Links) Generating Outgoing URLs in Action Methods Generating a URL from a Specific Route Customizing the Routing System Creating a Custom RouteBase Implementation Creating a Custom Route Handler Working with Areas Creating an Area Populating an Area Resolving the Ambiguous Controller Issue Creating Areas with Attributes Generating Links to Actions in Areas Routing Requests for Disk Files Configuring the Application Server Defining Routes for Disk Files Bypassing the Routing System URL Schema Best Practices Make Your URLs Clean and Human-Friendly GET and POST: Pick the Right One 12 Summary Chapter 17: Controllers and Actions Preparing the Example Project Setting the Start URL Introducing the Controller Creating a Controller with IController Creating a Controller by Deriving from the Controller Class Receiving Request Data Getting Data from Context Objects Using Action Method Parameters Producing Output Understanding Action Results Returning HTML by Rendering a View Passing Data from an Action Method to a View Performing Redirections Returning Errors and HTTP Codes Summary Chapter 18: Filters Preparing the Example Project Setting the Start URL and Testing the Application Using Filters Introducing the Filter Types Applying Filters to Controllers and Action Methods Using Authorization Filters Applying the Custom Authorization Filter Using the Built-in Authorization Filter Using Authentication Filters Understanding the IAuthenticationFilter Interface Implementing the Authentication Check Combining Authentication and Authorization Filters Handling the Final Challenge Request Using Exception Filters Creating an Exception Filter Applying the Exception Filter Using a View to Respond to an Exception Using the Built-in Exception Filter Using Action Filters Implementing the OnActionExecuting Method Implementing the OnActionExecuted Method Using Result Filters Using the Built-in Action and Result Filter Class 13 Using Other Filter Features Filtering Without Attributes Using Global Filters Ordering Filter Execution Overriding Filters Summary Chapter 19: Controller Extensibility Preparing the Example Project Setting the Start URL Creating a Custom Controller Factory Dealing with the Fallback Controller Instantiating Controller Classes Implementing the Other Interface Methods Registering a Custom Controller Factory Working with the Built-in Controller Factory Prioritizing Namespaces Customizing DefaultControllerFactory Controller Instantiation Creating a Custom Action Invoker Using the Built-in Action Invoker Using a Custom Action Name Using Action Method Selection Improving Performance with Specialized Controllers Using Sessionless Controllers Using Asynchronous Controllers Summary Chapter 20: Views Creating a Custom View Engine Preparing the Example Project Creating a Custom IView Creating an IViewEngine Implementation Registering a Custom View Engine Testing the View Engine Working with the Razor Engine Preparing the Example Project Understanding Razor View Rendering Configuring the View Search Locations Adding Dynamic Content to a Razor View Using Layout Sections Using Partial Views Using Child Actions Summary 14 Chapter 21: Helper Methods Preparing the Example Project Setting the Start URL Testing the Example Application Creating Custom Helper Methods Creating an Inline Helper Method Creating an External Helper Method Managing String Encoding in a Helper Method Using the Built-In Form Helper Methods Creating Form Elements Specifying the Route Used by a Form Using Input Helpers Creating Select Elements Summary Chapter 22: Templated Helper Methods Preparing the Example Project Using Templated Helper Methods Generating Label and Display Elements Using Whole-Model Templated Helpers Using Model Metadata Using Metadata to Control Editing and Visibility Using Metadata for Labels Using Metadata for Data Values Using Metadata to Select a Display Template Applying Metadata to a Buddy Class Working with Complex Type Properties Customizing the Templated View Helper System Creating a Custom Editor Template Creating a Generic Template Replacing the Built-in Templates Summary Chapter 23: URL and Ajax Helper Methods Preparing the Example Project Defining Additional CSS Styles Installing the NuGet Packages Creating Basic Links and URLs Using MVC Unobtrusive Ajax Creating the Synchronous Form View Preparing the Project for Unobtrusive Ajax Creating an Unobtrusive Ajax Form Preparing the Controller 15 Creating the Ajax Form Understanding How Unobtrusive Ajax Works Setting Ajax Options Ensuring Graceful Degradation Providing the User with Feedback While Making an Ajax Request Prompting the User Before Making a Request Creating Ajax Links Ensuring Graceful Degradation for Links Working with Ajax Callbacks Working with JSON Adding JSON Support to the Controller Processing JSON in the Browser Preparing Data for Encoding Detecting Ajax Requests in the Action Method Summary Chapter 24: Model Binding Preparing the Example Project Understanding Model Binding Using the Default Model Binder Binding to Simple Types Binding to Complex Types Binding to Arrays and Collections Manually Invoking Model Binding Dealing with Binding Errors Customizing the Model Binding System Creating a Custom Value Provider Creating a Custom Model Binder Registering the Custom Model Binder Summary Chapter 25: Model Validation Preparing the Example Project Creating the Layout Creating the Views Explicitly Validating a Model Displaying Validation Errors to the User Displaying Validation Messages Displaying Property-Level Validation Messages Using Alternative Validation Techniques Performing Validation in the Model Binder Specifying Validation Rules Using Metadata Defining Self-Validating Models 16 Performing Client-Side Validation Enabling Client-Side Validation Using Client-Side Validation Understanding How Client-Side Validation Works Performing Remote Validation Summary Chapter 26: Bundles Preparing the Example Application Adding the NuGet Packages Creating the Model and Controller Creating the Layout and View Profiling Script and Style Sheet Loading Using Script and Style Bundles Adding the NuGet Package Defining the Bundles Applying Bundles Optimizing the JavaScript and CSS Files Summary Chapter 27: Web API and Single-page Applications Understanding Single-page Applications Preparing the Example Application Creating the Model Adding the NuGet Packages Adding the Controller Adding the Layout and Views Setting the Start Location and Testing the Example Application Using Web API Creating the Web API Controller Testing the API Controller Understanding How the API Controller Works Understanding API Controller Action Selection Mapping HTTP Methods to Action Methods Using Knockout for Single-page Applications Adding the JavaScript Libraries to the Layout Implementing the Summary Implementing the Create Feature Completing the Application Simplify the Home Controller Manage Content Visibility Summary 17 Index 18 About the Author Adam Freeman is an experienced IT professional who has held senior positions in a range of companies, most recently serving as chief technology officer and chief operating officer of a global bank. Now retired, he spends his time writing and running. 19 About the Technical Reviewer Fabio Claudio Ferracchiati is a senior consultant and a senior analyst/developer using Microsoft technologies. He works for Brain Force (http://www.brainforce.com) in its Italian branch (http://www.brainforce.it). He is a Microsoft Certified Solution Developer for .NET, a Microsoft Certified Application Developer for .NET, a Microsoft Certified Professional, and a prolific author and technical reviewer. Over the past 10 years, he’s written articles for Italian and international magazines and coauthored more than 10 books on a variety of computer topics. 20 CHAPTER 1 Putting ASP.NET MVC in Context ASP.NET MVC is a Web development framework from Microsoft that combines the effectiveness and tidiness of model-view- controller (MVC) architecture, the most up-to-date ideas and techniques from agile development, and the best parts of the existing ASP.NET platform. It is a complete alternative to traditional ASP.NET Web Forms, delivering advantages for all but the most trivial of Web development projects. In this chapter, you’ll learn why Microsoft created ASP.NET MVC, how it compares to its predecessors and alternatives, and, finally, what’s new in ASP.NET MVC 5 and what’s covered in this book. Understanding the History of ASP.NET ASP.NET was a huge shift when it first arrived in 2002. Figure 1-1 illustrates Microsoft’s technology stack as it appeared then. Figure 1-1. The ASP.NET Web Forms technology stack With Web Forms, Microsoft attempted to hide both HTTP (with its intrinsic statelessness) and HTML (which at the time was unfamiliar to many developers) by modeling the user interface (UI) as a hierarchy of server-side control objects. Each control kept track of its own state across requests (using the View State facility), rendering itself as HTML when needed and automatically connecting client-side events (for example, a button click) with the corresponding server-side event handler code. In effect, Web Forms is a giant abstraction layer designed to deliver a classic event-driven graphical user interface (GUI) over the Web. The idea was to make Web development feel just the same as Windows Forms development. Developers didn’t need to work with a series of independent HTTP requests and responses. They could think in terms of a stateful UI, and Microsoft could seamlessly transition the army of Windows desktop developers into the new world of web applications. 21 What Is Wrong with ASP.NET Web Forms? Traditional ASP.NET Web Forms development was great in principle, but reality proved more complicated: View State weight: The actual mechanism for maintaining state across requests (known as View State) results in large blocks of data being transferred between the client and server. This data can reach hundreds of kilobytes in even modest Web applications, and it goes back and forth with every request, leading to slower response times and increasing the bandwidth demands of the server. Page life cycle: The mechanism for connecting client-side events with server-side event handler code, part of the page life cycle, can be extraordinarily complicated and delicate. Few developers have success manipulating the control hierarchy at runtime without getting View State errors or finding that some event handlers mysteriously fail to execute. False sense of separation of concerns: ASP.NET Web Forms’ code-behind model provides a means to take application code out of its HTML markup and into a separate code-behind class. This has been widely applauded for separating logic and presentation, but, in reality, developers are encouraged to mix presentation code (for example, manipulating the server-side control tree) with their application logic (for example, manipulating database data) in these same monstrous code-behind classes. The end result can be fragile and unintelligible. Limited control over HTML: Server controls render themselves as HTML, but not necessarily the HTML you want. In early versions of ASP.NET, the HTML output failed to meet with Web standards or make good use of Cascading Style Sheets (CSS), and server controls generated unpredictable and complex ID attribute values that are hard to access using JavaScript. These problems are much improved in recent Web Forms releases, but it can still be tricky to get the HTML you expect. Leaky abstraction: Web Forms tries to hide HTML and HTTP wherever possible. As you try to implement custom behaviors, you frequently fall out of the abstraction, which forces you to reverse-engineer the postback event mechanism or perform obtuse acts to make it generate the desired HTML. Plus, all this abstraction can act as a frustrating barrier for competent Web developers. Low testability: The designers of Web Forms could not have anticipated that automated testing would become an essential component of software development. Not surprisingly, the tightly coupled architecture they designed is unsuitable for unit testing. Integration testing can be a challenge, too. Web Forms isn’t all bad and Microsoft has put a lot of effort into improving standards compliance, simplifying the development process, and even taking some features from ASP.NET MVC. Web Forms excels when you need quick results, and you can have a reasonably complex web app up and running within a day. But unless you are careful during development, you will find that the application you create is hard to test and hard to maintain. Note For complete details of ASP.NET Web Forms, see my Pro ASP.NET 4.5 in C# book, also published by Apress. I cover the complete framework and provide best-practice guidance for avoiding the most serious pitfalls. Web Development Today Outside Microsoft, Web development technology has been progressing rapidly and in several different directions since Web Forms was first released. Web Standards and REST The drive for Web standards compliance has increased in recent years. Web sites are consumed on a greater variety of devices and browsers than ever before, and Web standards (HTML, CSS, JavaScript, and so forth) remain the great hope for enjoying a consistent browsing experience. Modern web platforms can’t afford to ignore the business case and the weight of developer enthusiasm for Web standards compliance. HTML5 has begun to enter mainstream use and provides the Web developer with rich capabilities that allow the client to perform work that was previously the exclusive responsibility of the server. These new capabilities and the increasing maturity of JavaScript libraries such as AngularJS, jQuery, jQuery UI, and jQuery Mobile means that standards have become ever more important and form the critical foundation for ever richer Web apps. 22 Note I touch on HTML5, jQuery, and its cousins in this book, but I don’t go into depth because these are topics in their own right. If you want more complete coverage, then Apress publishes my books on these subjects: Pro AngularJS, Pro jQuery 2.0, Pro JavaScript for Web Apps, and The Definitive Guide to HTML5. At the same time, Representational State Transfer (REST has become the dominant architecture for application interoperability over HTTP, completely overshadowing SOAP (the technology behind ASP.NET’s original approach to Web services). REST describes an application in terms of resources (URIs) representing real-world entities and standard operations (HTTP methods) representing available operations on those resources. For example, you might PUT a new http://www.example.com/Products/Lawnmower or DELETE http://www.example.com/Customers/Arnold-Smith. Today’s Web applications don’t serve just HTML. Often, they must also serve JSON or XML data to client technologies such as AJAX and native smartphone applications. This happens naturally with REST, which eliminates the distinction between Web services and Web applications, but requires an approach to HTTP and URL handling that has not easily been supported by ASP.NET Web Forms. Agile and Test-Driven Development It is not just Web development that has matured. Software development as a whole has shifted toward agile methodologies. This can mean a lot of different things, but it is largely about running software projects as adaptable processes of discovery and resisting excessive forward planning. Enthusiasm for agile methodologies tends to go hand-in-hand with a set of development practices and tools (usually open source) that promote and assist these practices. Test-driven development (TDD), and its close relative, behavior-driven development (BDD), are two examples. The idea is to design your software by first describing examples of desired behaviors (known as tests or specifications), so at any time you can verify the stability and correctness of your application by executing your suite of tests against the implementation. There’s no shortage of .NET tools to support TDD/BDD, but these tend to not work well with Web Forms: Unit testing tools let you specify the behavior of individual classes or other small code units in isolation. These can be effectively applied only to software that has been designed as a set of independent modules, so that each test can be run in isolation. Unfortunately, few Web Forms applications can be tested this way. UI automation tools let you simulate a series of user interactions against a complete running instance of your application. These can be used with Web Forms, but they can break down whenever you make a slight change to your page layout. Without special attention, Web Forms change the HTML structures and element IDs, breaking test suites. The .NET open source and independent software vendor (ISV community has produced no end of top quality unit testing frameworks (NUnit and xUnit), mocking frameworks (Moq and Rhino Mocks), inversion-of-control containers (Ninject and AutoFac), continuous integration servers (Cruise Control and TeamCity), object-relational mappers (NHibernate and Subsonic), and the like. Traditional ASP.NET Web Forms is not amenable to these tools and techniques because of its monolithic design, and so Web Forms gets little respect from these projects. Ruby on Rails In 2004, Ruby on Rails was a quiet, open source contribution from an unknown player. Suddenly fame hit, transforming the rules of Web development. It’s not that Ruby on Rails contained revolutionary technology but that the concept took existing ingredients and blended them in such a compelling and appealing way as to put existing platforms to shame. Ruby on Rails (or just Rails, as it is commonly called) embraced an MVC architecture, which I describe in Chapter 3. By applying MVC and working in tune with the HTTP protocol, by promoting conventions instead of the need for configuration, and by integrating an object-relational mapping (ORM tool into its core, Rails applications more or less fell into place without much effort. It was as if this was how Web development should have been all along. Rails showed that Web standards compliance and RESTfulness don’t need to be hard. It also showed that agile development and TDD work best when the framework is designed to support them. The rest of the Web development world has been catching up ever since. Node.js Another significant trend is the movement toward using JavaScript as a primary programming language. AJAX first showed that JavaScript is important; jQuery showed us that it could be powerful and elegant; and Google’s open source V8 JavaScript engine showed us that it could be fast. Today, JavaScript is becoming a serious server-side programming language. It serves as the data 23 storage and querying language for several non-relational databases, including CouchDB and Mongo, and it is used as a general- purpose language in server-side platforms such as Node.js. Node.js has been around since 2009 and gained acceptance quickly. Its key innovations are as follows: Using JavaScript: Developers need to work only in a single language, from client-side code, through server-side logic, and even into data-querying logic via CouchDB or the like. Being completely asynchronous: Node.js’s core API doesn’t expose any way of blocking a thread while waiting for input/output (I/O) or any other operation. All I/O is implemented by beginning the operation and then later receiving a callback when the I/O is completed. This means that Node.js makes extremely efficient use of system resources and may handle tens of thousands of concurrent requests per CPU. (Alternative platforms tend to be limited to about one hundred concurrent requests per CPU.) Node.js remains a niche technology. Its biggest contribution to web app development has, rather oddly, been to provide a consistent JavaScript engine on which development tools can be written. Many emerging client-side JavaScript frameworks, such as AngularJS, have good tooling support based on the use of Node.js. Node.js adoption for deploying web apps has been slower. Most businesses building real applications in limited time frames typically need the infrastructure in full-stack frameworks such as Ruby on Rails and ASP.NET MVC. Node.js is mentioned here only to put some of ASP.NET MVC’s design into context against industry trends. For example, ASP.NET MVC includes asynchronous controllers (which I describe in Chapter 19). This is a way to handle HTTP requests with non-blocking I/O and scale up to handle more requests per CPU. Key Benefits of ASP.NET MVC In October 2007, Microsoft announced a new MVC Web development platform, built on the core ASP.NET platform, clearly designed as a direct response to the evolution of technologies such as Rails and as a reaction to the criticisms of Web Forms. The following sections describe how this new platform overcame the Web Forms limitations and brought ASP.NET back to the cutting edge. MVC Architecture It is important to distinguish between the MVC architectural pattern and the ASP.NET MVC Framework. The MVC pattern is not new—it dates back to 1978 and the Smalltalk project at Xerox PARC—but it has gained enormous popularity today as a pattern for Web applications, for the following reasons: User interaction with an MVC application follows a natural cycle: the user takes an action, and in response the application changes its data model and delivers an updated view to the user. And then the cycle repeats. This is a convenient fit for Web applications delivered as a series of HTTP requests and responses. Web applications necessitate combining several technologies (databases, HTML, and executable code, for example), usually split into a set of tiers or layers. The patterns that arise from these combinations map naturally onto the concepts in MVC. The ASP.NET MVC Framework implements the MVC pattern and, in doing so, provides greatly improved separation of concerns. In fact, ASP.NET MVC implements a modern variant of the MVC pattern that is especially suitable for Web applications. You will learn more about the theory and practice of this architecture in Chapter 3. By embracing and adapting the MVC pattern, the ASP.NET MVC Framework provides strong competition to Ruby on Rails and similar platforms, and brings the MVC pattern into the mainstream of the .NET world. By capitalizing on the experience and best practices discovered by developers using other platforms, ASP.NET MVC has, in many ways, pushed forward beyond what even Rails can offer. Extensibility The MVC Framework is built as a series of independent components that satisfy a .NET interface or that are built on an abstract base class. You can easily replace components, such as the routing system, the view engine, and the controller factory, with a different one of your own implementation. In general, the MVC Framework gives you three options for each component: 24 Use the default implementation of the component as it stands (which should be enough for most applications). Derive a subclass of the default implementation to tweak its behavior. Replace the component entirely with a new implementation of the interface or abstract base class. You’ll learn all about the various components, and how and why you might want to tweak or replace each of them, starting in Chapter 14. Tight Control over HTML and HTTP ASP.NET MVC produces clean, standards-compliant markup. Its built-in HTML helper methods produce standards-compliant output, but there is a more significant philosophical change compared with Web Forms. Instead of generating out swathes of HTML over which you have little control, the MVC Framework encourages you to craft simple, elegant markup styled with CSS. Of course, if you do want to throw in some ready-made widgets for complex UI elements such as date pickers or cascading menus, ASP.NET MVC’s “no special requirements” approach to markup makes it easy to use best-of-breed UI libraries such as jQuery UI or the Bootstrap CSS library. ASP.NET MVC meshes so well with jQuery, for example, that Microsoft ships jQuery as a built-in part of the default Visual Studio ASP.NET MVC project template, along with other popular libraries, such as Bootstrap, Knockout and Modernizr. Tip I don’t get into the detail of these “blessed” JavaScript libraries in this book because they are not part of the core MVC Framework and do their work within the browser. Client-side development for MVC Framework applications is an important topic, however, and you can learn more in my book Pro ASP.NET MVC 5 Client, which will be published by Apress in 2014. There are some libraries, however, that provide support for core features such as validation and Ajax requests and I describe these in Part 2 of this book. I describe Knockout in Chapter 27 and I use Bootstrap (albeit without a detailed introduction) throughout the book. ASP.NET MVC–generated pages don’t contain any View State data, so they are smaller than typical pages from ASP.NET Web Forms. Despite today’s fast connections, this economy of bandwidth still gives an enormously improved end-user experience and helps reduce the cost of running a popular web application. ASP.NET MVC works in tune with HTTP. You have control over the requests passing between the browser and server, so you can fine-tune your user experience as much as you like. AJAX is made easy, and there aren’t any automatic postbacks to interfere with client-side state. Testability The MVC architecture gives you a great start in making your application maintainable and testable because you naturally separate different application concerns into independent pieces. Yet the ASP.NET MVC designers didn’t stop there. To support unit testing, they took the framework’s component-oriented design and made sure that each separate piece is structured to meet the requirements of unit testing and mocking tools. They added Visual Studio wizards to create unit test projects on your behalf, which can be integrated with open source unit test tools such as NUnit and xUnit as well as the test tools that are included in Visual Studio, which I introduce in Chapter 6. Even if you have never written a unit test before, you will be off to a great start. In this book, you will see examples of how to write clean, simple unit tests for ASP.NET MVC controllers and actions that supply fake or mock implementations of framework components to simulate any scenario, using a variety of testing and mocking strategies. Testability is not only a matter of unit testing. ASP.NET MVC applications work well with UI automation testing tools, too. You can write test scripts that simulate user interactions without needing to guess which HTML element structures, CSS classes, or IDs the framework will generate, and you do not have to worry about the structure changing unexpectedly. Powerful Routing System The style of URLs has evolved as Web application technology has improved. URLs like this one: /App_v2/User/Page.aspx?action=show%20prop&prop_id=82742 are increasingly rare, replaced with a simpler, cleaner format like this: /to-rent/chicago/2303-silver-street 25 There are some good reasons for caring about the structure of URLs. First, search engines give weight to keywords found in a URL. A search for “rent in Chicago” is much more likely to turn up the simpler URL. Second, many Web users are now savvy enough to understand a URL, and appreciate the option of navigating by typing it into their browser’s address bar. Third, when someone understands the structure of a URL, they are more likely to link to it, share it with a friend, or even read it aloud over the phone. Fourth, it doesn’t expose the technical details, folder, and file name structure of your application to the public Internet, so you are free to change the underlying implementation without breaking all your incoming links. Clean URLs were hard to implement in earlier frameworks, but ASP.NET MVC uses a feature known as URL routing to provide clean URLs by default. This gives you control over your URL schema and its relationship to your application, offering you the freedom to create a pattern of URLs that is meaningful and useful to your users, without the need to conform to a predefined pattern. And, of course, this means you can easily define a modern REST-style URL schema if you wish. You’ll find a thorough description of URL routing in Chapters 15 and 16. Built on the Best Parts of the ASP.NET Platform Microsoft’s existing ASP.NET platform provides a mature, well-proven set of components and facilities for developing effective and efficient Web applications. First and most obviously, as ASP.NET MVC is based on the .NET platform, you have the flexibility to write code in any .NET language and access the same API features—not just in MVC itself but in the extensive .NET class library and the vast ecosystem of third-party .NET libraries. Second, ready-made ASP.NET platform features—such as authentication, membership, roles, profiles, and internationalization —can reduce the amount of code you need to develop and maintain any Web application, and these features are just as effective when used in the MVC Framework as they are in a classic Web Forms project. The underlying ASP.NET platform provides a rich set of tools on which to build web applications with the MVC Framework. Note I describe the most commonly used ASP.NET Platform features as they relate to MVC development in this book, but the platform is a topic in its own right. For complete details of the rich features that the ASP.NET platform provides, see my forthcoming Pro ASP.NET MVC 5 Platform, which will be published by Apress in 2014. Modern API Microsoft’s .NET platform has evolved with each major release, supporting – and even defining – the state-of-the-art aspects of modern programming. ASP.NET MVC 5 is built for .NET 4.5.1, so its API can take full advantage of recent language and runtime innovations, including the await keyword, extension methods, lambda expressions, anonymous and dynamic types, and Language Integrated Query (LINQ). Many of the MVC Framework’s API methods and coding patterns follow a cleaner, more expressive composition than was possible with earlier platforms. Don’t worry if you are not up to speed on the latest C# language features: I provide a summary of the most important C# features for MVC development in Chapter 4. ASP.NET MVC Is Open Source Unlike previous Microsoft Web development platforms, you are free to download the original source code for ASP.NET MVC, and even modify and compile your own version of it. This is invaluable when your debugging trail leads into a system component and you want to step into its code (and even read the original programmers’ comments). It is also useful if you are building an advanced component and want to see what development possibilities exist, or how the built-in components actually work. Additionally, this ability is great if you do not like the way something works, if you find a bug, or if you just want to access something that’s otherwise inaccessible, because you can simply change it yourself. However, you’ll need to keep track of your changes and reapply them if you upgrade to a newer version of the framework. ASP.NET MVC is licensed under the Microsoft Public License (Ms-PL, http://www.opensource.org/licenses/ms-pl.html), an Open Source Initiative (OSI)–approved open source license. This means that you can change the source code, deploy it, and even redistribute your changes publicly as a derivative project. You can download the MVC source code from http://aspnetwebstack.codeplex.com. What Do I Need to Know? To get the most from this book, you should be familiar with the basics of web development, have an understanding of how HTML and CSS work and a working knowledge of C#. Don’t worry if you are a little hazy on the client-side details. My emphasis is on server-side development in this book and you can pick up what you need through the examples. In Chapter 4, I provide a summary of the most useful C# language features for MVC development, which you’ll find useful if you are moving to the latest 26 .NET versions from an earlier release. What Is the Structure of This Book? This book is split into 2 parts, each of which covers a set of related topics. Part 1: Introducing ASP.NET MVC 5 I start this book by putting the ASP.NET MVC Framework in context. I explain the benefits and practical impact of the MVC pattern, the way in which the MVC Framework fits into modern web development and describe the tools and C# language features that every MVC Framework programmer needs. In the next chapter you will dive right in and create a simple web application and get an idea of what the major components and building blocks are and how they fit together. Most of this part of the book, however, is given over to the development of a project called SportsStore, through which I show you a realistic development process from inception to deployment, touching on the major features of the ASP.NET MVC Framework. Part 2: ASP.NET MVC in Detail In Part 2, I explain the inner workings of the MVC Framework features that I used to build the SportsStore application. I show you how each feature works, explain the role it plays in the MVC Framework and show you the configuration and customization options that are available. Having set the broad context in Part 1, I dig right into the details in Part 2. What’s New in this Edition? Version 5 of the MVC Framework is a relatively minor upgrade and a lot of the changes are really to do with the way that ASP.NET projects are created and managed in Visual Studio. Table 1-1 briefly describes the new MVC Framework features and details where you can find more information about them in this book. Table 1-1. The New Features in MVC 5 Feature Description See Chapter Authentication Filters A new kind of filter that can be used to include different types of authentication within the same controller 18 Filter Overrides A new kind of filter that is applied to action methods to prevent filters defined globally or on the controller from taking effect 18 Attribute Routing A set of attributes that allow URL routes to be defined within the controller class 15, 16 ASP.NET version 4.5.1, on which the MVC Framework 5 is built, has been enhanced as well. The most important change is the addition of the ASP.NET Identity API, which replaces the Membership system for managing user credentials. I don’t cover ASP.NET Identity in this book, although I do explain how authentication and authorization are applied to MVC Framework applications through the use of features like filters. Note I will be covering ASP.NET Identity in my Pro ASP.NET MVC 5 Platform book, which will be published in 2014 and cover all of the facilities that the ASP.NET platform provides. That said, I don’t want you to have to buy a second book to learn about something as important as user security, and so Apress has agreed to distribute the security-related chapters from that book from its web site for download without charge when that book is published. Those chapters won’t be available immediately because I have not written the platform book yet, but it is my next major writing project after this book and my hope is that the delay won’t be too long. A new edition is a chance to go beyond writing about new features and I have made some other changes for this book. I have expanded the SportsStore example to show the basics of responsive and mobile web application development, I added quick references to the start of all the in-depth chapters so you can find easily specific examples, and I added a chapter that shows how one of the open source libraries that Microsoft has embraced—Knockout—can be combined with the Web API feature to create Single-Page Applications (SPAs). 27 Where Can I Get the Example Code? You can download all of the examples for all of the chapters in this book from Apress.com. The download is available without charge and includes all of the Visual Studio projects and their contents. You don’t have to download the code, but it is the easiest way of experimenting with the examples and cutting and pasting techniques into your own projects. What Software Do I Need for This Book? The only software you need for MVC development is Visual Studio 2013, which contains everything you need to get started, including a built-in application server for running and debugging MVC applications, an administration-free edition of SQL Server for developing database-driven applications, tools for unit testing and, of course, a code editor compiler and debugger. There are several different editions of Visual Studio, but I will be using the one that Microsoft makes available free of charge, called Visual Studio Express 2013 for Web. Microsoft adds some nice features to the paid-for editions of Visual Studio, but you will not need them for this book and all of the figures that you see throughout this book have been taken using the Express edition, which you can download from http://www.microsoft.com/visualstudio/eng/products/visual-studio-express- products. There are several different versions of Visual Studio 2013 Express, each of which is used for a different kind of development. Make sure that you get the Web version, which supports ASP/NET applications. Once you have installed Visual Studio, you are ready to go. Microsoft has improved the scope of the features in the Visual Studio Express in recent years and there is nothing else you need to follow along with this book. I do rely on additional software packages, but these are installed through Visual Studio and don’t require separate downloads and installations (and are available without cost). Tip I have used Windows 8.1 throughout this book, but you can use Visual Studio 2013 and develop MVC applications quite happily on earlier versions of Windows. See the system requirements for Visual Studio 2013 for details of which versions and patch levels are supported). Credits In Chapter 10, I use a feature of the Bootstrap CSS library called Glyphicons Halflings, which are a set of icons that are not usually available for free, but for which the creator has given an open license for their inclusion in Bootstrap. The only request is that the creator’s URL be quoted when it is possible to do so, which seems like a fair and reasonable thing to do. Here is it: http://glyphicons.com. Summary In this chapter, I explained the context in which the MVC Framework exists and how it compares to Web Forms. I described the benefits of using the MVC framework, the structure of this book and the software that you will require to follow the examples. You saw how the ASP.NET MVC platform addresses the weaknesses of ASP.NET Web Forms, and how its modern design delivers advantages to developers who want to write high-quality, maintainable code. In the next chapter, you’ll see the MVC Framework in action in a simple demonstration of the features that deliver these benefits. 28 CHAPTER 2 Your First MVC Application The best way to appreciate a software development framework is to jump right in and use it. In this chapter, you’ll create a simple data-entry application using the ASP.NET MVC Framework. I take things a step at a time so you can see how an ASP.NET MVC application is constructed. To keep things simple, I will skip over some of the technical details for the moment. But don’t worry. If you are new to MVC, you will find plenty to keep you interested. Where I use something without explaining it, I provide a reference to the chapter in which you can find all the details. Preparing Visual Studio Visual Studio Express contains all of the features you need to create, test and deploy an MVC Framework application, but some of those features are hidden away until you ask for them. To enable all of the features, select Expert Settings from the Visual Studio Tools Settings menu. Tip For some reason, Microsoft has decided that the top-level menus in Visual Studio should be all in uppercase, which means that the menu I referred to is really TOOLS. I think this is rather like shouting and I will capitalize menu names as Tools throughout this book. Creating a New ASP.NET MVC Project I am going to start by creating a new MVC Framework project in Visual Studio. Select New Project from the File menu to open the New Project dialog. If you select the Web templates in the Visual C# section, you will see the ASP.NET Web Application project template. Select this project type, as shown in Figure 2-1. 29 Figure 2-1. The Visual Studio ASP.NET Web Application project template Set the name of the new project to PartyInvites and click the OK button to continue. You will see another dialog box, shown in Figure 2-2, which asks you to set the initial content for the ASP.NET project. This is part of the Microsoft initiative to better integrate the different parts of ASP.NET into a set of consistent tools and templates. 30 Figure 2-2. Selecting the initial project configuration Tip Make sure you select version 4.5.1 of the .NET Framework at the top of the window. This is the latest version of .NET and is required for some of the advanced features that I describe in this book. The templates create projects with different starting points and configurations for features such as authentication, navigation and visual themes. I am going to keep things simple: select the Empty option and check the MVC box in the Add folders and core references section, as shown in the figure. This will create a basic MVC project with minimal predefined content and will be the starting point that I use for all of the examples in this book. Click the OK button to create the new project. Note The other project template options are intended to give you a more complete starting point for your ASP.NET projects. I don’t like these templates because they encourage developers to treat some important features, such as authentication, as black boxes. My goal in this book is to give you the knowledge to understand and manage every aspect of your MVC application and, as a consequence, I use the Empty template for most of the examples in the book – the exception is in Chapter 14, where I show you the content that the MVC template adds to new projects. Once Visual Studio creates the project, you will see a number of files and folders displayed in the Solution Explorer window, as shown in Figure 2-3. This is the default project structure for a new MVC project and you will soon understand the purpose of each of the files and folders that Visual Studio creates. 31 Figure 2-3. The initial file and folder structure of an MVC project You can try to run the application now by selecting Start Debugging from the Debug menu (if it prompts you to enable debugging, just click the OK button). You can see the result in Figure 2-4. Because I started with the empty project template, the application does not contain anything to run, so the server generates a 404 Not Found Error. Figure 2-4. Trying to run an empty project When you are finished, be sure to stop debugging by closing the browser window that shows the error, or by going back to Visual Studio and selecting Stop Debugging from the Debug menu. As you have just seen, Visual Studio opens the browser to display the project. The default browser is, of course, Internet Explorer, but you can select any browser that you have installed by using the toolbar shown in Figure 2-5. As the figure shows, I have a range of browsers installed, which I find useful for testing web apps during development. 32 Figure 2-5. Changing the browser that Visual Studio uses to run the project I will be using Internet Explorer 11 throughout this book, but that’s just because I know that IE is so widely installed. Internet Explorer used to play fast and loose with web standards, but recent versions have been good at implementing the HTML5 standard. Google Chrome is also a good choice for development and I tend to use it for my own projects. Adding the First Controller In MVC architecture, incoming requests are handled by controllers. In ASP.NET MVC, controllers are just C# classes (usually inheriting from System.Web.Mvc.Controller, the framework’s built-in controller base class). Each public method in a controller is known as an action method, meaning you can invoke it from the Web via some URL to perform an action. The MVC convention is to put controllers in the Controllers folder, which Visual Studio created when it set up the project. Tip You do not need to follow this or most other MVC conventions, but I recommend that you do—not least because it will help you make sense of the examples in this book. To add a controller to the project, right-click the Controllers folder in the Visual Studio Solution Explorer window and choose Add and then Controller from the pop-up menus, as shown in Figure 2-6. 33 Figure 2-6. Adding a controller to the MVC project When the Add Scaffold dialog appears, select the MVC 5 Controller – Empty option, as shown in Figure 2-7, and click the Add button. 34 Figure 2-7. Selecting an empty controller from the Add Scaffold dialog The Add Controller dialog will appear. Set the name to HomeController and click the Add button. There are several conventions represented in this name: names given to controllers should indicate their purpose; the default controller is called Home and controller names have the suffix Controller. Tip If you have used earlier versions of Visual Studio to create MVC applications, then you will notice that the process is slightly different. Microsoft has changed the way that Visual Studio can populate a project with preconfigured classes and other items. Visual Studio will create a new C# file in the Controllers folder called HomeController.cs and open it for editing. I have listed the default contents that Visual Studio puts into the class file in Listing 2-1. You can see that the class is called HomeController and it is derived from the Controller class, which is found in the System.Web.Mvc namespace. Listing 2-1. The Default Contents of the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace PartyInvites.Controllers { public class HomeController : Controller { public ActionResult Index() { 35 return View(); } } } A good way of getting started with MVC is to make a couple of simple changes to the controller class. Edit the code in the HomeController.cs file so that it matches Listing 2-2. I have highlighted the statements that have changed so they are easier to see. Listing 2-2. Modifying the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace PartyInvites.Controllers { public class HomeController : Controller { public string Index() { return "Hello World"; } } } These changes don’t have a dramatic effect, but they make for a nice demonstration. I have changed the action method called Index so that it returns the string “Hello World”. Run the project again by selecting Start Debugging from the Visual Studio Debug menu. The browser will display the result of the Index action method, as shown in Figure 2-8. Figure 2-8. The output from the controller action method Tip Notice that Visual Studio has directed the browser to port 37782. You will almost certainly see a different port number in the URL that your browser requests because Visual Studio allocates a random port when the project is created. If you look in the Windows task bar notification area, you will find an icon for IIS Express. This is a cut-down version of the full IIS application server which is included with Visual Studio and is used to deliver ASP.NET content and services during development. I’ll show you how to deploy an MVC project into a production environment in Chapter 13. Understanding Routes As well as models, views, and controllers, MVC applications use the ASP.NET routing system, which decides how URLs map to controllers and actions. When Visual Studio creates the MVC project, it adds some default routes to get us started. You can request any of the following URLs, and they will be directed to the Index action on the HomeController: / 36 /Home /Home/Index So, when a browser requests http://yoursite/ or http://yoursite/Home, it gets back the output from HomeController’s Index method. You can try this yourself by changing the URL in the browser. At the moment, it will be http://localhost:37782/, except that the port part may be different. If you append /Home or /Home/Index to the URL and hit return, you will see the same Hello World result from the MVC application. This is a good example of benefiting from following MVC conventions. In this case, the convention is that I will have a controller called HomeController and that it will be the starting point for my MVC application. The default routes that Visual Studio creates for a new project assume that I will follow this convention. And since I did follow the convention, I automatically got support for the URLs in the preceding list. If I had not followed the convention, I would need to modify the routes to point to whatever controller I had created instead. For this simple example, the default configuration is all I need. Tip You can see and edit your routing configuration by opening the RouteConfig.cs file in the App_Start folder. I explain what the entries in this file do in Chapters 16 and 17. Rendering Web Pages The output from the previous example wasn’t HTML—it was just the string “Hello World”. To produce an HTML response to a browser request, I need a view. Creating and Rendering a View The first thing I need to do is modify my Index action method, as shown in Listing 2-3. Listing 2-3. Modifying the Controller to Render a View in the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace PartyInvites.Controllers { public class HomeController : Controller { public ViewResult Index() { return View(); } } } The changes in Listing 2-3 are shown in bold. When I return a ViewResult object from an action method, I am instructing MVC to render a view. I create the ViewResult by calling the View method with no parameters. This tells MVC to render the default view for the action. If you run the application at this point, you can see the MVC Framework trying to find a default view to use, as shown in the error message displayed in Figure 2-9. 37 Figure 2-9. The MVC Framework trying to find a default view This error message is quite helpful. It explains not only that MVC could not find a view for the action method, but it shows where it looked. This is another nice illustration of an MVC convention: views are associated with action methods by a naming convention. The action method is called Index and the controller is called Home and you can see from Figure 2-9 that MVC is trying to find different files in the Views folder that have that name. The simplest way to create a view is to ask Visual Studio to do it for you. Right-click anywhere in the definition of the Index action method in code editor window for the HomeController.cs file and select Add View from the pop-up menu, as shown in Figure 2-10. 38 Figure 2-10. Asking Visual Studio to create a view for an action method Visual Studio displays the Add View dialog, which allows you to configure the initial contents of the view file that will be created. Set View Name to Index (the name of the action method that the view will be associated with—another convention), set Template to Empty (without model), and leave the Create as a partial view and Use a layout page boxes unchecked, as shown in Figure 2-11. Don’t worry about what all of these options mean at the moment—I’ll explain all of the details in later chapters. Click the Add button to create the new view file. Figure 2-11. Configuring the initial contents of the view file 39 Visual Studio will create a file called Index.cshtml in the Views/Home folder. If this isn’t the effect you achieve, then delete the file you created and try again. This is another MVC Framework convention: views are placed in the Views folder, organized in folders that correspond to the name of the controller they are associated with. Tip The .cshtml file extension denotes a C# view that will be processed by Razor. Early versions of MVC relied on the ASPX view engine, for which view files have the .aspx extension. The effect of the values I told you to enter into the Add View dialog tell Visual Studio to create the most basic view, the contents of which are shown in Listing 2-4. Listing 2-4. The Initial Contents of the Index.cshtml File @{ Layout = null; } Index
Visual Studio opens the Index.cshtml file for editing. You’ll see that this file contains mostly HTML. The exception is the part that looks like this: ... @{ Layout = null; } ... This is an expression that will be interpreted by the Razor view engine, which processes the contents of views and generates HTML that is sent to the browser. This is a simple Razor expression and it tells Razor that I chose not to use a layout, which is like a template for the HTML that will be sent to the browser (and which I describe in Chapter 5). I am going to ignore Razor for the moment and come back to it later. Make the addition to the Index.cshtml file that is shown in bold in Listing 2-5. Listing 2-5. Adding to the View HTML in the Index.cshtml File @{ Layout = null; } Index 40
Hello World (from the view)
The addition displays another simple message. Select Start Debugging from the Debug menu to run the application and test the view. You should see something similar to Figure 2-12. Figure 2-12. Testing the view When I first edited the Index action method, it returned a string value. This meant that MVC did nothing except pass the string value as is to the browser. Now that the Index method returns a ViewResult, the MVC Framework renders a view and returns the HTML it produces. I didn’t tell MVC which view should be used, so it used the naming convention to find one automatically. The convention is that the view has the name of the action method and is contained in a folder named after the controller: /Views/Home/Index.cshtml. I can return other results from action methods besides strings and ViewResult objects. For example, if I return a RedirectResult, the browser will be redirected to another URL. If I return an HttpUnauthorizedResult, I force the user to log in. These objects are collectively known as action results, and they are all derived from the ActionResult class. The action result system lets us encapsulate and reuse common responses in actions. I’ll tell you more about them and show more complex uses in Chapter 17. Adding Dynamic Output The whole point of a web application platform is to construct and display dynamic output. In MVC, it is the controller’s job to construct some data and pass it to the view, which is responsible for rendering it to HTML. One way to pass data from the controller to the view is by using the ViewBag object, which is a member of the Controller base class. ViewBag is a dynamic object to which you can assign arbitrary properties, making those values available in whatever view is subsequently rendered. Listing 2-6 demonstrates passing some simple dynamic data in this way in the HomeController.cs file. Listing 2-6. Setting Some View Data in the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace PartyInvites.Controllers { public class HomeController : Controller { public ViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good Morning" : "Good 41 Afternoon"; return View(); } } } I provide data for the view when I assign a value to the ViewBag.Greeting property. The Greeting property didn’t exist until the moment I assigned the value—this allows me to pass data from the controller to the view in a free and fluid manner, without having to define classes ahead of time. I refer to the ViewBag.Greeting property again in the view to get the data value, as illustrated in Listing 2-7, which shows the corresponding change to the Index.cshtml file. Listing 2-7. Retrieving a ViewBag Data Value in the Index.cshtml File @{ Layout = null; } Index
@ViewBag.Greeting World (from the view)
The addition to Listing 2-7 is a Razor expression. When I call the View method in the controller’s Index method, the MVC framework locates the Index.cshtml view file and asks the Razor view engine to parse the file’s content. Razor looks for expressions like the one I added in the listing and processes them. In this example, processing the expression means inserting the value assigned to the ViewBag.Greeting property in the action method into the view. There’s nothing special about the property name Greeting; you could replace this with any property name and it would work the same, just as long as the name you use in the controller matches the name you use in the view. You can pass multiple data values from your controller to the view by assigning values to more than one property. You can see the effect of these changes by starting the project, as shown in Figure 2-13. Figure 2-13. A dynamic response from MVC Creating a Simple Data-Entry Application In the rest of this chapter, I will explore more of the basic MVC features by building a simple data-entry application. I am going to 42 pick up the pace in this section. My goal is to demonstrate MVC in action, so I will skip over some of the explanations as to how things work behind the scenes. But don’t worry, I’ll revisit these topics in depth in later chapters. Setting the Scene Imagine that a friend has decided to host a New Year’s Eve party and that she has asked me to create a web app that allows her invitees to electronically RSVP. She has asked for four key features: A home page that shows information about the party A form that can be used to RSVP Validation for the RSVP form, which will display a thank-you page RSVPs e-mailed to the party host when complete In the following sections, I will build up the MVC project I created at the start of the chapter and add these features. I can check the first item off the list by applying what I covered earlier and add some HTML to my existing view to give details of the party. Listing 2-8 shows the additions I made to the Views/Home/Index.cshtml file. Listing 2-8. Displaying Details of the Party in the Index.cshtml File @{ Layout = null; } Index
@ViewBag.Greeting World (from the view)

We're going to have an exciting party.
(To do: sell it better. Add pictures or something.)

I am on my way. If you run the application, you’ll see the details of the party—well, the placeholder for the details, but you get the idea—as shown in Figure 2-14. 43 Figure 2-14. Adding to the view HTML Designing a Data Model In MVC, the M stands for model, and it is the most important part of the application. The model is the representation of the real- world objects, processes, and rules that define the subject, known as the domain, of the application. The model, often referred to as a domain model, contains the C# objects (known as domain objects) that make up the universe of the application and the methods that manipulate them. The views and controllers expose the domain to the clients in a consistent manner and a well- designed MVC application starts with a well-designed model, which is then the focal point as controllers and views are added. I don’t need a complex model for the PartyInvites application because it is such a simple application and I need to create just one domain class which I will call GuestResponse. This object will be responsible for storing, validating, and confirming an RSVP. Adding a Model Class The MVC convention is that the classes that make up a model are placed inside the Models folder, which Visual Studio created as part of the initial project setup. Right-click Models in the Solution Explorer window and select Add followed by Class from the pop-up menus. Set the file name to GuestResponse.cs and click the Add button to create the class. Edit the contents of the class to match Listing 2-9. Tip If you don’t have a Class menu item, then you probably left the Visual Studio debugger running. Visual Studio restricts the changes you can make to a project while it is running the application. Listing 2-9. The GuestResponse Domain Class Defined in the GuestResponse.cs File namespace PartyInvites.Models { public class GuestResponse { public string Name { get; set; } public string Email { get; set; } public string Phone { get; set; } public bool? WillAttend { get; set; } } } Tip You may have noticed that the WillAttend property is a nullable bool, which means that it can be true, false, or null. I explain the rationale for this in the Adding Validation section later in the chapter. Linking Action Methods One of my application goals is to include an RSVP form, so I need to add a link to it from my Index.cshtml view, as shown in Listing 2-10. Listing 2-10. Adding a Link to the RSVP Form in the Index.cshtml File @{ Layout = null; } Index 44
@ViewBag.Greeting World (from the view)

We're going to have an exciting party.
(To do: sell it better. Add pictures or something.)

@Html.ActionLink("RSVP Now", "RsvpForm")
Html.ActionLink is an HTML helper method. The MVC Framework comes with a collection of built-in helper methods that are convenient for rendering HTML links, text inputs, checkboxes, selections, and other kinds of content. The ActionLink method takes two parameters: the first is the text to display in the link, and the second is the action to perform when the user clicks the link. I explain the complete set of HTML helper methods in Chapters 21-23. You can see the link that the helper creates by starting the project, as shown in Figure 2-15. Figure 2-15. Adding a link to the view If you roll your mouse over the link in the browser, you will see that the link points to http://yourserver/Home/RsvpForm. The Html.ActionLink method has inspected the application’s URL routing configuration and determined that /Home/RsvpForm is the URL for an action called RsvpForm on a controller called HomeController. Tip Notice that, unlike traditional ASP.NET applications, MVC URLs do not correspond to physical files. Each action method has its own URL, and MVC uses the ASP.NET routing system to translate these URLs into actions. Creating the Action Method You will see a 404 Not Found error if you click the link. That’s because I have not yet created the action method that corresponds to the /Home/RsvpForm URL. I do this by adding a method called RsvpForm to the HomeController class, as shown in Listing 2-11. Listing 2-11. Adding a New Action Method in the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace PartyInvites.Controllers { 45 public class HomeController : Controller { public ViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good Morning" : "Good Afternoon"; return View(); } public ViewResult RsvpForm() { return View(); } } } Adding a Strongly Typed View I am going to add a view for the RsvpForm action method, but in a slightly different way—I am going to create a strongly typed view. A strongly typed view is intended to render a specific domain type, and if I specify the type I want to work with (GuestResponse in this case), MVC can create some helpful shortcuts to make it easier. Caution Make sure your MVC project is compiled before proceeding. If you have created the GuestResponse class but not compiled it, MVC won’t be able to create a strongly typed view for this type. To compile your application, select Build Solution from the Visual Studio Build menu. Right-click the RsvpForm method in the code editor and select Add View from the pop-up menu to open the Add View dialog window. Ensure that the View Name is set as RsvpForm, set Template to Empty and select GuestResponse from the drop-down list for the Model Class field. Leave the View Options boxes unchecked, as shown in Figure 2-16. Figure 2-16. Adding a new view to the project 46 Click the Add button and Visual Studio will create a new file called RvspForm.cshtml in the Views/Home folder and open it for editing. You can see the initial contents in Listing 2-12. This is another skeletal HTML file, but it contains a @model Razor expression. As you will see in a moment, this is the key to a strongly typed view and the convenience it offers. Listing 2-12. The Initial Contents of the RsvpForm.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } RsvpForm
Tip The options that you select and check when you create a view determine the initial content of a view file, but that’s all. You can change from regular to strongly typed views, for example, just by adding or removing the @model directive in the code editor. Building the Form Now that I have created the strongly typed view, I can build out the contents of RsvpForm.cshtml to make it into an HTML form for editing GuestResponse objects, as shown in Listing 2-13. Listing 2-13. Creating a Form View in the RsvpForm.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } RsvpForm @using (Html.BeginForm()) {

Your name: @Html.TextBoxFor(x => x.Name)

Your email: @Html.TextBoxFor(x => x.Email)

Your phone: @Html.TextBoxFor(x => x.Phone)

47 Will you attend? @Html.DropDownListFor(x => x.WillAttend, new[] { new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString}, new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option")

} For each property of the GuestResponse model class, I use an HTML helper method to render a suitable HTML input control. These methods let you select the property that the input element relates to using a lambda expression, like this: ... @Html.TextBoxFor(x => x.Phone) ... The HTML TextBoxFor helper method generates the HTML for an input element, sets the type parameter to text, and sets the id and name attributes to Phone (the name of the selected domain class property) like this: This handy feature works because the RsvpForm view is strongly typed, and I have told MVC that GuestResponse is the type that I want to render with this view. This provides the HTML helper methods with the information they need to understand which data type I want to read properties from via the @model expression. Don’t worry if you aren’t familiar with C# lambda expressions. I provide an overview in Chapter 4, but an alternative to using lambda expressions is to refer to the name of the model type property as a string, like this: ... @Html.TextBox("Email") ... I find that the lambda expression technique prevents me from mistyping the name of the model type property, because Visual Studio IntelliSense pops up and lets me pick the property automatically, as shown in Figure 2-17. 48 Figure 2-17. Visual Studio IntelliSense for lambda expressions in HTML helper methods Another convenient helper method is Html.BeginForm, which generates an HTML form element configured to post back to the action method. Because I have not passed any arguments to the helper method, it assumes I want to post back to the same URL that the HTML document was requested from. A neat trick is to wrap this in a C# using statement, like this: ... @using (Html.BeginForm()) { ... form contents go here ... } ... Normally, when applied like this, the using statement ensures that an object is disposed of when it goes out of scope. It is commonly used for database connections, for example, to make sure that they are closed as soon as a query has completed. (This application of the using keyword is different from the kind that brings classes in a namespace into scope in a class.) Instead of disposing of an object, the HtmlBeginForm helper closes the HTML form element when it goes out of scope. This means that the Html.BeginForm helper method creates both parts of a form element, like this:
... form contents go here ...
Don’t worry if you are not familiar with disposing of C# objects. The point here is to demonstrate how to create a form using the HTML helper method. Setting the Start URL Visual Studio will, in an effort to be helpful, make the browser request a URL based on the view that is currently being edited. This is a hit-and-miss feature because it doesn’t work when you are editing other kinds of file and because you can’t just jump in at any point in most complex web apps. To set a fixed URL for the browser to request, select PartyInvites Properties from the Visual Studio Project menu, select the Web section and check the Specific Page option in the Start Action category, as shown in Figure 2-18. You don’t have to enter a value into the field–Visual Studio will request the default URL for the project, which will be directive to the Index action method on the Home controller. (I show you how to use the URL routing system to change the default mapping in Chapters 15 and 16). 49 Figure 2-18. Setting the default start URL for the project You can see the form in the RsvpForm view when you run the application and click the RSVP Now link. Figure 2-19 shows the result. Figure 2-19. The RspvForm view Handling Forms I have not yet told MVC what I want to do when the form is posted to the server. As things stand, clicking the Submit RSVP button just clears any values you have entered into the form. That is because the form posts back to the RsvpForm action method in the Home controller, which just tells MVC to render the view again. 50 Note You might be surprised that the input data is lost when the view is rendered again. If so, you have probably been developing applications with ASP.NET Web Forms, which automatically preserves data in this situation. I will show you how to achieve the same effect with MVC shortly. To receive and process submitted form data, I am going to use a clever feature. I will add a second RsvpForm action method in order to create the following: A method that responds to HTTP GET requests: A GET request is what a browser issues normally each time someone clicks a link. This version of the action will be responsible for displaying the initial blank form when someone first visits /Home/RsvpForm. A method that responds to HTTP POST requests: By default, forms rendered using Html.BeginForm() are submitted by the browser as a POST request. This version of the action will be responsible for receiving submitted data and deciding what to do with it. Handing GET and POST requests in separate C# methods helps to keep my controller code tidy, since the two methods have different responsibilities. Both action methods are invoked by the same URL, but MVC makes sure that the appropriate method is called, based on whether I am dealing with a GET or POST request. Listing 2-14 shows the changes I applied to the HomeController class. Listing 2-14. Adding an Action Method to Support POST Requests in the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using PartyInvites.Models; namespace PartyInvites.Controllers { public class HomeController : Controller { public ViewResult Index() { int hour = DateTime.Now.Hour; ViewBag.Greeting = hour < 12 ? "Good Morning" : "Good Afternoon"; return View(); } [HttpGet] public ViewResult RsvpForm() { return View(); } [HttpPost] public ViewResult RsvpForm(GuestResponse guestResponse) { // TODO: Email response to the party organizer return View("Thanks", guestResponse); } } } I have added the HttpGet attribute to my existing RsvpForm action method. This tells MVC that this method should be used only for GET requests. I then added an overloaded version of RsvpForm, which takes a GuestResponse parameter and applies the HttpPost attribute. The attribute tells MVC that the new method will deal with POST requests. I also imported the PartyInvites.Models namespace—this is just so I can refer to the GuestResponse model 51 type without needing to qualify the class name. I explain how these additions to the listing work in the following sections. Using Model Binding The first overload of the RsvpForm action method renders the same view as before–the RsvpForm.cshtml file–to generate the form shown in Figure 2-18. The second overload is more interesting because of the parameter, but given that the action method will be invoked in response to an HTTP POST request, and that the GuestResponse type is a C# class, how are the two connected? The answer is model binding, an extremely useful MVC feature whereby incoming data is parsed and the key/value pairs in the HTTP request are used to populate properties of domain model types. This process is the opposite of using the HTML helper methods; that is, when creating the form data to send to the client, I generated HTML input elements where the values for the id and name attributes were derived from the model class property names. In contrast, with model binding, the names of the input elements are used to set the values of the properties in an instance of the model class, which is then passed to the POST-enabled action method. Model binding is a powerful and customizable feature that eliminates the grind and toil of dealing with HTTP requests directly and lets us work with C# objects rather than dealing with Request.Form[] and Request.QueryString[] values. The GuestResponse object that is passed as the parameter to the action method is automatically populated with the data from the form fields. I dive into the detail of model binding, including how it can be customized, in Chapter 24. Rendering Other Views The second overload of the RsvpForm action method also demonstrates how to tell MVC to render a specific view in response to a request, rather than the default view. Here is the relevant statement: ... return View("Thanks", guestResponse); ... This call to the View method tells MVC to find and render a view called Thanks and to pass the GuestResponse object to the view. To create the view I specified, right-click on any of the HomeController methods and select Add View from the pop-up menu and use the Add View dialog to create a strongly typed view called Thanks that uses the GuestResponse model class and that is based on the Empty template. (See the Adding a Strongly Typed View section for step-by-step details if needed). Visual Studio will create the view as Views/Home/Thanks.cshtml. Edit the new view so that it matches Listing 2-15—I have highlighted the markup you need to add. Listing 2-15. The Contents of the Thanks.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } Thanks

Thank you, @Model.Name!

@if (Model.WillAttend == true) { 52 @:It's great that you're coming. The drinks are already in the fridge! } else { @:Sorry to hear that you can't make it, but thanks for letting us know. }
The Thanks view uses Razor to display content based on the value of the GuestResponse properties that I passed to the View method in the RsvpForm action method. The Razor @model expression specifies the domain model type that the view is strongly typed with. To access the value of a property in the domain object, I use Model.PropertyName. For example, to get the value of the Name property, I call Model.Name. Don’t worry if the Razor syntax doesn’t make sense—I explain it in more detail in Chapter 5. Now that I have created the Thanks view, I have a basic working example of handling a form with MVC. Start the application in Visual Studio, click the RSVP Now link, add some data to the form, and click the Submit RSVP button. You will see the result shown in Figure 2-20 (although it will differ if your name is not Joe or you said you could not attend). Figure 2-20. The Thanks view Adding Validation I am now in a position to add validation to my application. Without validation, users could enter nonsense data or even submit an empty form. In an MVC application, validation is typically applied in the domain model, rather than in the user interface. This means that I am able to define validation criteria in one place and have it take effect anywhere in the application that the model class is used. ASP.NET MVC supports declarative validation rules defined with attributes from the System.ComponentModel.DataAnnotations namespace, meaning that validation constraints are expressed using the standard C# attribute features. Listing 2-16 shows how I applied these attributes to the GuestResponse model class. Listing 2-16. Applying Validation in the GuestResponse.cs File using System.ComponentModel.DataAnnotations; namespace PartyInvites.Models { public class GuestResponse { [Required(ErrorMessage = "Please enter your name")] public string Name { get; set; } 53 [Required(ErrorMessage = "Please enter your email address")] [RegularExpression(".+\\@.+\\..+", ErrorMessage = "Please enter a valid email address")] public string Email { get; set; } [Required(ErrorMessage = "Please enter your phone number")] public string Phone { get; set; } [Required(ErrorMessage = "Please specify whether you'll attend")] public bool? WillAttend { get; set; } } } The validations rules are shown in bold. MVC automatically detects the attributes and uses them to validate data during the model-binding process. Notice that I have imported the namespace that contains the validations, so I can refer to them without needing to qualify their names. Tip As noted earlier, I used a nullable bool for the WillAttend property. I did this so that I could apply the Required validation attribute. If I had used a regular bool, the value I received through model binding could be only true or false, and I wouldn’t be able to tell if the user had selected a value. A nullable bool has three possible values: true, false, and null. The null value will be used if the user hasn’t selected a value, and this causes the Required attribute to report a validation error. This is a nice example of how the MVC Framework elegantly blends C# features with HTML and HTTP. I check to see if there has been a validation problem using the ModelState.IsValid property in the controller class. Listing 2-17 shows how I have done this in the POST-enabled RsvpForm action method in the Home controller class. Listing 2-17. Checking for Form Validation Errors in the HomeController.cs File ... [HttpPost] public ViewResult RsvpForm(GuestResponse guestResponse) { if (ModelState.IsValid) { // TODO: Email response to the party organizer return View("Thanks", guestResponse); } else { // there is a validation error return View(); } } ... If there are no validation errors, I tell MVC to render the Thanks view, just as I did previously. If there are validation errors, I re-render the RsvpForm view by calling the View method without any parameters. Just displaying the form when there is an error is not helpful—I also need to provide the user with some indication of what the problem is and why I could not accept their form submission. I do this by using the Html.ValidationSummary helper method in the RsvpForm view, as shown in Listing 2-18. Listing 2-18. Using the Html.ValidationSummary Helper Method in the RsvpForm.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } 54 RsvpForm @using (Html.BeginForm()) { @Html.ValidationSummary()

Your name: @Html.TextBoxFor(x => x.Name)

Your email: @Html.TextBoxFor(x => x.Email)

Your phone: @Html.TextBoxFor(x => x.Phone)

Will you attend? @Html.DropDownListFor(x => x.WillAttend, new[] { new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString}, new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option")

} If there are no errors, the Html.ValidationSummary method creates a hidden list item as a placeholder in the form. MVC makes the placeholder visible and adds the error messages defined by the validation attributes. You can see how this appears in Figure 2-21. 55 Figure 2-21. The validation summary The user won’t be shown the Thanks view until all of the validation constraints applied to the GuestResponse class have been satisfied. Notice that the data entered into the form was preserved and displayed again when the view was rendered with the validation summary. This is another benefit of the model binding feature and it simplifies working with form data. Note If you have worked with ASP.NET Web Forms, you will know that Web Forms has a concept of server controls that retain state by serializing values into a hidden form field called __VIEWSTATE. ASP.NET MVC model binding is not related to the Web Forms concepts of server controls, postbacks, or View State. ASP.NET MVC does not inject a hidden __VIEWSTATE field into your rendered HTML pages. Highlighting Invalid Fields The HTML helper methods that create text boxes, drop-downs, and other elements have a handy feature that can be used in conjunction with model binding. The same mechanism that preserves the data that a user entered in a form can also be used to highlight individual fields that failed the validation checks. When a model class property has failed validation, the HTML helper methods will generate slightly different HTML. As an example, here is the HTML that a call to Html.TextBoxFor(x => x.Name) generates when there is no validation error: And here is the HTML the same call generates when the user doesn’t provide a value (which is a validation error because I applied the Required attribute to the Name property in the GuestResponse model class): I have highlighted the difference in bold: the helper method added a class called input-validation-error to the 56 input element. I can take advantage of this feature by creating a style sheet that contains CSS styles for this class and the others that different HTML helper methods apply. The convention in MVC projects is that static content, such as CSS style sheets, is placed into a folder called Content. Create this folder by right-clicking on the PartyInvites item in the Solution Explorer, selecting Add New Folder from the menu and setting the name to Content. To create the CSS file, right click on the newly created Content folder, select Add New Item from the menu and choose Style Sheet from the set of item templates. Set the name of the new file to Styles.css, as shown in Figure 2-22. Figure 2-22. Creating a new style sheet Click the Add button and Visual Studio will create the Content/Styles.css file. Set the content of the new file to match Listing 2-19. Listing 2-19. The Contents of the Styles.css File .field-validation-error {color: #f00;} .field-validation-valid { display: none;} .input-validation-error { border: 1px solid #f00; background-color: #fee; } .validation-summary-errors { font-weight: bold; color: #f00;} .validation-summary-valid { display: none;} To use this style sheet, I add a new reference to the head section of RsvpForm view, as shown in Listing 2-20. You add link elements to views just as you would to a regular static HTML file, although in Chapter 27, I show you the bundles feature that allows JavaScript and CSS style sheets to be consolidated and delivered to the browsers over a single HTTP request. 57 Tip You can drag JavaScript and CSS files from the Solution Explorer windows and drop them on the code editor. Visual Studio will create script and link elements for the files you have selected. Listing 2-20. Adding the Link Element in the RsvpForm.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } RsvpForm @using (Html.BeginForm()) { @Html.ValidationSummary()

Your name: @Html.TextBoxFor(x => x.Name)

Your email: @Html.TextBoxFor(x => x.Email)

Your phone: @Html.TextBoxFor(x => x.Phone)

Will you attend? @Html.DropDownListFor(x => x.WillAttend, new[] { new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString}, new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option")

} Tip If you have moved to MVC 5 directly from MVC 3, you might have been expecting us to have added the CSS file to the view by specifying the href attribute as @Href("∼/Content/Site.css") or @Url.Content("∼/Content/Site.css"). As of MVC 4, Razor detects attributes that begin with ∼/ and automatically inserts the @Href or @Url call for you. With the application of the style sheet, a more visually obvious validation error will be displayed when data is submitted that causes a validation error, as shown in Figure 2-23. 58 Figure 2-23. Automatically highlighted validation errors Styling the Content The basic functionality of the application is in place – except for sending emails, which I’ll get to shortly–but the overall appearance is pretty poor. Although this is a book focused on server-side development, Microsoft has adopted a number of open source libraries and included them in some of the Visual Studio project templates. I am not a fan of these templates, but I do like some of the libraries they use and one of the new adoptees in MVC 5 is Bootstrap, which is a nice CSS library originally developed by Twitter that has become widely used. You don’t have to use the Visual Studio project templates to use libraries like Bootstrap, of course. You can download files directly from project web sites or use NuGet, which is integrated into Visual Studio and provides access to a catalogue of pre- packaged software that can be downloaded and installed automatically. One of the best NuGet features is that it manages dependencies between packages such that if you install Bootstrap, for example, NuGet will also download and install jQuery which some Bootstrap features depend on. Using NuGet to Install Bootstrap To install the Bootstrap package, select Library Package Manager Package Manager Console from the Visual Studio Tools menu. Visual Studio will open the NuGet command line. Enter the following command and hit return: Install-Package -version 3.0.0 bootstrap The Install-Package command tells NuGet to download a package and its dependencies and add them to the project. The name of the package I want is called bootstrap, and you can search for package names either from the NuGet web site (http://www.nuget.org) or using the Visual Studio NuGet user interface (select Tools Library Package Manager Manage NuGet Packages for Solution). I have used –version to specify that I want Bootstrap version 3, which is the latest stable version available as I write this. Without –version, NuGet would have downloaded the latest version of the package, but I want to make sure that you are able to recreate the examples exactly as I have shown them and so installing a specific version helps me to ensure consistency. NuGet will download all of the files required for Bootstrap and for jQuery, which Bootstrap relies on. CSS files are added to the Content folder and a Scripts folder is created (which is the standard MVC location for JavaScript files) and populated with Bootstrap and jQuery files. (A fonts folder is also created–this is a quirk of the Bootstrap typography features, which 59 expect files to be in certain locations). Note The reason that I am showing you Bootstrap in this chapter is to illustrate how readily the HTML generated by the MVC Framework can be used with popular CSS and JavaScript libraries. I don’t want to lose my focus on server-side development, however, and so if you want complete details of the client-side aspects of working with the MVC Framework, then see my book Pro ASP.NET MVC 5 Client, which will be published by Apress in 2014. Styling the Index View The basic Bootstrap features work by applying classes to elements that correspond to CSS selectors defined in the files added to the Content folder. You can get full details of the classes that Bootstrap defines from http://getbootstrap.com, but you can see how I have applied some basic styling to the Index.cshtml view file in Listing 2-21. Listing 2-21. Adding Bootstrap to the Index.cshtml File @{ Layout = null; } Index

We're going to have an exciting party!

And you are invited

@Html.ActionLink("RSVP Now", "RsvpForm")
I have added link elements for the bootstrap.css and bootstrap-theme.css files in the Content folder. These are the Bootstrap files required for the basic CSS styling that the library provides and there is a corresponding JavaScript file in the Scripts folder, but I won’t need it in this chapter. I have also defined a style element that sets the background color for the body element and styles the text for a elements. Tip You will notice that each of the Bootstrap files in the Content folder has a twin with the prefix min–e.g., bootstrap.css and bootstrap.min.cs. It is common practice to minify JavaScript and CSS files when deploying an application into production, which is a process of removing all of the whitespace and, in the case of JavaScript, replacing the function and variable names with shorter labels. The goal of minification is to reduce the amount of bandwidth required to deliver your content to the browser and in Chapter 27, I describe the ASP.NET features for managing this process automatically. For this chapter–and most of the other chapters in this book–I will use the regular files, which is normal practice 60 during development and testing. Having imported the Bootstrap styles and defined a couple of my own, I need to style my elements. This is a simple example and so I only need to use three Bootstrap CSS classes: text-center, btn and btn-success. The text-center class centers the content of an element and its children. The btn class styles a button, input or a element as a pretty button and the btn-success specifies which of a range of colors I want the button to be. The color of the button depends on the theme that is being used–I have the default theme (as defined by the bootstrap- theme.css file), but there are endless replacements available with a search online. You can see the effect that I have created in Figure 2-24. Figure 2-24. Styling the Index view It will be obvious to you that I am not a web designer. In fact, as a child, I was excused from art lessons on the basis that I had absolutely no talent whatsoever. This had the happy result of making more time for math lessons but meant that my artistic skills have not developed beyond those of the average 10 year old. For a real project, I would seek a professional to help design and style the content, but for this example I am going it alone and that means applying Bootstrap with as much restraint and consistency as I can muster. Styling the RsvpForm View Bootstrap defines classes that can be used to style forms. I am not going to go into detail, but you can see how I have applied these classes in Listing 2-22. Listing 2-22. Adding Bootstrap to the RsvpForm.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } RsvpForm 61

RSVP

@using (Html.BeginForm()) { @Html.ValidationSummary()
@Html.TextBoxFor(x => x.Name, new { @class = "form- control"})
@Html.TextBoxFor(x => x.Email, new { @class = "form- control"})
@Html.TextBoxFor(x => x.Phone, new { @class = "form- control"})
@Html.DropDownListFor(x => x.WillAttend, new[] { new SelectListItem() {Text = "Yes, I'll be there", Value = bool.TrueString}, new SelectListItem() {Text = "No, I can't come", Value = bool.FalseString} }, "Choose an option", new { @class = "form- control" })
}
The Bootstrap classes in this example create a panel with a header, just to give structure to the layout. To style the form, I have used the form-group class, which is used to style the element that contains the label and the associated input or select element. These elements are created using HTML helper methods, which means that there are not statically defined elements available to which I can apply the required form-control class. Fortunately, the helper methods take an optional object argument that lets me specify attributes on the elements that they create, as follows: ... @Html.TextBoxFor(x => x.Name, new { @class = "form-control"}) ... 62 I created the object using the C# anonymous type feature, which I describe in Chapter 4 and specified that the class attribute should be set to form-control on the element that the TextBoxFor helper generates. The properties defined by the object are used for the name of the attribute added to the HTML element and class is a reserved word in the C# language, so I have to prefix it with @. This is a standard C# feature that allows keywords to be used in expressions. You can see the result of my styles in Figure 2-25. Figure 2-25. Styling the RsvpForm view Styling the Thanks View The last view file to style is Thanks.cshtml and you can see how I have done this in Listing 2-23. You will notice that the markup I have added is similar to that in the Index.cshtml view. To make an application easier to manage, it is a good principal to avoid duplicating code and markup wherever possible and in Chapter 5 I will introduce you to Razor layouts and in Chapter 20, I describe partial views, both of which can be used to reduce duplication of markup. Listing 2-23. Applying Bootstrap to the Thanks.cshtml File @model PartyInvites.Models.GuestResponse @{ Layout = null; } 63 Thanks

Thank you, @Model.Name!

@if (Model.WillAttend == true) { @:It's great that you're coming. The drinks are already in the fridge! } else { @:Sorry to hear that you can't make it, but thanks for letting us know. }
The lead class applies one of the Bootstrap typographic styles and you can see the effect in Figure 2-26. Figure 2-26. Styling the Thanks View Completing the Example The last requirement for my example application is to e-mail completed RSVPs to the party organizer. I could do this by adding an action method to create and send an e-mail message using the e-mail classes in the .NET Framework–and that would be the technique which is most consistent with the MVC pattern. Instead, I am going to use the WebMail helper method. This is not part of the MVC framework, but it does let me complete this example without getting mired in the details of setting up other means of sending e-mail. I want the e-mail message to be sent as I render the Thanks view. Listing 2-24 show the changes that I need to apply. Listing 2-24. Using the WebMail Helper in the Thanks.cshtml File 64 ... @{ try { WebMail.SmtpServer = "smtp.example.com"; WebMail.SmtpPort = 587; WebMail.EnableSsl = true; WebMail.UserName = "mySmtpUsername"; WebMail.Password = "mySmtpPassword"; WebMail.From = "rsvps@example.com"; WebMail.Send("party-host@example.com", "RSVP Notification", Model.Name + " is " + ((Model.WillAttend ?? false) ? "" : "not") + "attending"); } catch (Exception) { @:Sorry - we couldn't send the email to confirm your RSVP. } }

Thank you, @Model.Name!

@if (Model.WillAttend == true) { @:It's great that you're coming. The drinks are already in the fridge! } else { @:Sorry to hear that you can't make it, but thanks for letting us know. }
... Note I used the WebMail helper because it lets us demonstrate sending an e-mail message with a minimum of effort. Typically, however, I would prefer to put this functionality in an action method. I will explain why when I describe the MVC architecture pattern in Chapter 3. I have added a Razor expression that uses the WebMail helper to configure the details of my e-mail server, including the server name, whether the server requires SSL connections, and account details. Once I have configured all of the details, I use the WebMail.Send method to send the e-mail. I enclosed all of the e-mail code in a try...catch block so that I can alert the user if the e-mail is not sent. I do this by adding a block of text to the output of the Thanks view. A better approach would be to display a separate error view when the e-mail message cannot be sent, but I wanted to keep things simple for this first MVC application. Summary In this chapter, I created a new MVC project and used it to construct a simple MVC data-entry application, giving you a first glimpse of the MVC Framework architecture and approach. I skipped over some key features (including Razor syntax, routing, and automated testing), but I come back to these topics in depth in later chapters. In the next chapter, I describe the MVC architecture, design patterns, and techniques that I use throughout the rest of this book and which form the foundation for effective development with the MVC Framework. 65 CHAPTER 3 The MVC Pattern Before I start digging into the details of the ASP.NET MVC Framework, I want to make sure you are familiar with the MVC design pattern and the thinking behind it. In this chapter, I describe the following: The MVC architecture pattern Domain models and repositories Creating loosely coupled systems using dependency injection (DI) The basics of automated testing You might be familiar with some of the ideas and conventions I discuss in this chapter, especially if you have done advanced ASP.NET or C# development. If not, I encourage you to read carefully—a good understanding of what lies behind MVC can help put the features of the framework into context as you continue through the book. The History of MVC The term model-view-controller has been in use since the late 1970s and arose from the Smalltalk project at Xerox PARC, where it was conceived as a way to organize some early GUI applications. Some of the fine detail of the original MVC pattern was tied to Smalltalk-specific concepts, such as screens and tools, but the broader concepts are still applicable to applications—and they are especially well suited to Web applications. Interactions with an MVC application follow a natural cycle of user actions and view updates, where the view is assumed to be stateless. This fits nicely with the HTTP requests and responses that underpin a Web application. Further, MVC forces a separation of concerns—the domain model and controller logic are decoupled from the user interface. In a Web application, this means that the HTML is kept apart from the rest of the application, which makes maintenance and testing simpler and easier. It was Ruby on Rails that led to renewed mainstream interest in MVC and it remains the implementation template for the MVC pattern. Many other MVC frameworks have since emerged and demonstrated the benefits of MVC— including, of course, ASP.NET MVC. Understanding the MVC Pattern In high-level terms, the MVC pattern means that an MVC application will be split into at least three pieces: Models, which contain or represent the data that users work with. These can be simple view models, which just represent data being transferred between views and controllers; or they can be domain models, which contain the data in a business domain as well as the operations, transformations, and rules for manipulating that data. Views, which are used to render some part of the model as a user interface. Controllers, which process incoming requests, perform operations on the model, and select views to render to the user. Models are the definition of the universe your application works in. In a banking application, for example, the model represents everything in the bank that the application supports, such as accounts, the general ledger, and credit limits for customers—as well as the operations that can be used to manipulate the data in the model, such as depositing funds and making withdrawals from the accounts. The model is also responsible for preserving the overall state and consistency of the data—for example, making sure 66 that all transactions are added to the ledger, and that a client doesn’t withdraw more money than he is entitled to or more money than the bank has. Models are also defined by what they are not responsible for: models don’t deal with rendering UIs or processing requests— those are the responsibilities of views and controllers. Views contain the logic required to display elements of the model to the user —and nothing more. They have no direct awareness of the model and do not directly communicate with the model in any way. Controllers are the bridge between views and the model—requests come in from the client and are serviced by the controller, which selects an appropriate view to show the user and, if required, an appropriate operation to perform on the model. Each piece of the MVC architecture is well-defined and self-contained—this is referred to as the separation of concerns. The logic that manipulates the data in the model is contained only in the model; the logic that displays data is only in the view, and the code that handles user requests and input is contained only in the controller. With a clear division between each of the pieces, your application will be easier to maintain and extend over its lifetime, no matter how large it becomes. Understanding the Domain Model The most important part of an MVC application is the domain model. We create the model by identifying the real-world entities, operations, and rules that exist in the industry or activity that the application must support, known as the domain. We then create a software representation of the domain: the domain model. For the purposes of the ASP.NET MVC Framework, the domain model is a set of C# types (classes, structs, etc.), collectively known as the domain types. The operations from the domain are represented by the methods defined in the domain types, and the domain rules are expressed in the logic inside of these methods—or, as you saw in the previous chapter, by applying C# attributes. When an instance of a domain type is created to represent a specific piece of data, it is called a domain object. Domain models are usually persistent and long-lived— there are lots of different ways of achieving this, but relational databases remain the most common choice. In short, a domain model is the single, authoritative definition of the business data and processes within your application. A persistent domain model is also the authoritative definition of the state of your domain representation. The domain model approach solves many of the problems that arise when maintaining an application. If you need to manipulate the data in your model or add a new process or rule, the domain model is the only part of your application that has to be changed. Tip A common way of enforcing the separation of the domain model from the rest of an ASP.NET MVC application is to place the model in a separate C# assembly. In this way, you can create references to the domain model from other parts of the application but ensure that there are no references in the other direction. This is particularly useful in large-scale projects. I demonstrate this approach in the example I start building in Chapter 7. The ASP.NET Implementation of MVC In MVC, controllers are C# classes, usually derived from the System.Web.Mvc.Controller class. Each public method in a class derived from Controller is an action method, which is associated with a configurable URL through the ASP.NET routing system. When a request is sent to the URL associated with an action method, the statements in the controller class are executed in order to perform some operation on the domain model and then select a view to display to the client. Figure 3-1 shows the interactions between the controller, model, and view. Figure 3-1. The interactions in an MVC application The ASP.NET MVC Framework uses a view engine, which is the component responsible for processing a view in order to generate a response for the browser. Earlier versions of MVC used the standard ASP.NET view engine, which processed ASPX pages using a streamlined version of the Web Forms markup syntax. MVC 3 introduced the Razor view engine, which was refined in MVC 4 (and unchanged in MVC5) and that uses a different syntax entirely, which I describe in Chapter 5). Tip Visual Studio provides IntelliSense support for Razor, making it a simple matter to inject and respond to view data supplied by the controller. 67 ASP.NET MVC doesn’t apply any constraints on the implementation of your domain model. You can create a model using regular C# objects and implement persistence using any of the databases, object-relational mapping frameworks, or other data tools supported by .NET. Comparing MVC to Other Patterns MVC is not the only software architecture pattern, of course. There are many others and some of them are, or at least have been, extremely popular. We can learn a lot about MVC by looking at the alternatives. In the following sections, I briefly describe different approaches to structuring an application and contrast them with MVC. Some of the patterns are close variations on the MVC theme, whereas others are entirely different. I am not suggesting that MVC is the perfect pattern for all situations. I am a proponent of picking the best approach to solve the problem at hand. As you will see, there are situations where some competing patterns are as useful as or better than MVC. I encourage you to make an informed and deliberate choice when selecting a pattern. The fact that you are reading this book suggests that you already have a certain commitment to the MVC pattern, but it is always helpful to maintain the widest possible perspective. Understanding the Smart UI Pattern One of the most common design patterns is known as the smart user interface (smart UI). Most programmers have created a smart UI application at some point in their careers—I certainly have. If you have used Windows Forms or ASP.NET Web Forms, you have too. To build a smart UI application, developers construct a user interface, often by dragging a set of components or controls onto a design surface or canvas. The controls report interactions with the user by emitting events for button presses, keystrokes, mouse movements, and so on. The developer adds code to respond to these events in a series of event handlers: small blocks of code that are called when a specific event on a specific component is emitted. This creates a monolithic application, as shown in Figure 3-2. The code that handles the user interface and the business is all mixed together with no separation of concerns at all. The code that defines the acceptable values for a data input, that queries for data or modifies a user account, ends up in little pieces, coupled together by the order in which events are expected. Figure 3-2. The Smart UI pattern Smart UIs are ideal for simple projects because you can get some good results fast (by comparison to MVC development which, as you’ll see in Chapter 7, requires some careful preparation and initial investment before getting results). Smart UIs are also suited to user interface prototyping. These design surface tools can be really good, although I always find the Web Forms design surface in Visual Studio to be awkward and unpredictable. If you are sitting with a customer and want to capture the requirements for the look and flow of the interface, a Smart UI tool can be a quick and responsive way to generate and test different ideas. The biggest drawback is that Smart UIs are difficult to maintain and extend. Mixing the domain model and business logic code in with the user interface code leads to duplication, where the same fragment of business logic is copied and pasted to support a newly added component. Finding all of the duplicate parts and applying a fix can be difficult. It can be almost impossible to add a new feature without breaking an existing one. Testing a Smart UI application can also be difficult. The only way is to simulate user interactions, which is far from ideal and a difficult basis from which to provide full test coverage. In the world of MVC, the Smart UI is often referred to as an anti-pattern: something that should be avoided at all costs. This antipathy arises, at least in part, because people come to MVC looking for an alternative after spending part of their careers trying to develop and maintain Smart UI applications. Although it is a common point of view, it is overly simplistic and it is a mistake to reject the Smart UI pattern out of hand. Not everything is rotten in the Smart UI pattern and there are positive aspects to this approach. Smart UI applications are quick and 68 easy to develop. The component and design tool producers have put a lot of effort into making the development experience a pleasant one, and even the most inexperienced programmer can produce something professional-looking and reasonably functional in just a few hours. The biggest weakness of Smart UI applications—maintainability—doesn’t arise in small development efforts. If you are producing a simple tool for a small audience, a Smart UI application can be a perfect solution. The additional complexity of an MVC application simply isn’t warranted. Understanding the Model-View Architecture The area in which maintenance problems tend to arise in a Smart UI application is in the business logic, which ends up so diffused across the application that making changes or adding features becomes a fraught process. An improvement in this area is offered by the model-view architecture, which pulls out the business logic into a separate domain model. In doing this, the data, processes, and rules are all concentrated in one part of the application, as shown in Figure 3-3. Figure 3-3. The model-view pattern The model-view architecture can be an improvement over the monolithic Smart UI pattern—it is much easier to maintain, for example–but two problems arise. The first is that since the UI and the domain model are closely integrated, it can be difficult to perform unit testing on either. The second problem arises from practice, rather than the definition of the pattern. The model typically contains a mass of data access code—this need not be the case, but it usually is—and this means that the data model does not contain just the business data, operations, and rules. Understanding Classic Three-Tier Architectures To address the problems of the model-view architecture, the three-tier or three-layer pattern separates the persistence code from the domain model and places it in a new component called the ddata access layer (DAL). This is shown in Figure 3-4. Figure 3-4. The three-tier pattern The three-tier architecture is the most widely used pattern for business applications. It has no constraints on how the UI is implemented and provides good separation of concerns without being too complicated. And, with some care, the DAL can be created so that unit testing is relatively easy. You can see the obvious similarities between a classic three-tier application and the MVC pattern. The difference is that when the UI layer is directly coupled to a click-and-event GUI framework (such as Windows Forms or ASP.NET Web Forms), it becomes almost impossible to perform automated unit tests. And because the UI part of a three-tier application can be complex, there’s a lot of code that can’t be rigorously tested. In the worst scenario, the three-tier pattern’s lack of enforced discipline in the UI tier means that many such applications end up 69 as thinly disguised Smart UI applications, with no real separation of concerns. This gives the worst possible outcome: an untestable, unmaintainable application that is excessively complex. Understanding Variations on MVC I have already described the core design principles of MVC applications, especially as they apply to the ASP.NET MVC implementation. Others interpret aspects of the pattern differently and have added to, adjusted, or otherwise adapted MVC to suit the scope and subject of their projects. In the following sections, I provide a brief overview of the two most prevalent variations on the MVC theme. Understanding these variations is not essential to working with ASP.NET MVC and I have included this information just for completeness because you will hear the terms used in most discussions of software patterns. Understanding the Model-View-Presenter Pattern Model-view-presenter (MVP) is a variation on MVC that is designed to fit more easily with stateful GUI platforms such as Windows Forms or ASP.NET Web Forms. This is a worthwhile attempt to get the best aspects of the Smart UI pattern without the problems it usually brings. In this pattern, the presenter has the same responsibilities as an MVC controller, but it also takes a more direct relationship to a stateful view, directly managing the values displayed in the UI components according to the user’s inputs and actions. There are two implementations of this pattern: The passive view implementation, in which the view contains no logic—it is a container for UI controls that are directly manipulated by the presenter. The supervising controller implementation, in which the view may be responsible for some elements of presentation logic, such as data binding, and has been given a reference to a data source from the domain models. The difference between these two approaches relates to how intelligent the view is. Either way, the presenter is decoupled from the GUI framework, which makes the presenter logic simpler and suitable for unit testing. Understanding the Model-View-View Model Pattern The model-view-view model (MVVM) pattern is the most recent variation on MVC. It originated from Microsoft and is used in the Windows Presentation Foundation (WPF). In the MVVM pattern, models and views have the same roles as they do in MVC. The difference is the MVVM concept of a view model, which is an abstract representation of a user interface—typically a C# class that exposes both properties for the data to be displayed in the UI and operations on the data that can be invoked from the UI. Unlike an MVC controller, an MVVM view model has no notion that a view (or any specific UI technology) exists. An MVVM view uses the WPF binding feature to bi-directionally associate properties exposed by controls in the view (items in a drop-down menu, or the effect of pressing a button) with the properties exposed by the view model. Tip MVC also uses the term view model but refers to a simple model class that is used only to pass data from a controller to a view, as opposed to domain models, which are sophisticated representations of data, operations, and rules. Building Loosely Coupled Components One of most important features of the MVC pattern is that it enables separation of concerns. I want the components in my applications to be as independent as possible and to have as few interdependencies as I can arrange. In an ideal situation, each component knows nothing about any other component and only deals with other areas of the application through abstract interfaces. This is known as loose coupling, and it makes testing and modifying applications easier. A simple example will help put things in context. If I am writing a component called MyEmailSender that will send e- mails, I would implement an interface that defines all of the public functions required to send an e-mail, which I would call IEmailSender. Any other component of my application that needs to send an e-mail—let’s say a password reset helper called PasswordResetHelper—can then send an e-mail by referring only to the methods in the interface. There is no direct dependency between PasswordResetHelper and MyEmailSender, as shown by Figure 3-5. 70 Figure 3-5. Using interfaces to decouple components By introducing IEmailSender, I ensure that there is no direct dependency between PasswordResetHelper and MyEmailSender. I could replace MyEmailSender with another e-mail provider or even use a mock implementation for testing purposes without needing to make any changes to PasswordResetHelper. (I introduce mock implementations later in this chapter and return to them again in Chapter 6). Using Dependency Injection Interfaces help decouple components, but I still face a problem: C# doesn’t provide a built-in way to easily create objects that implement interfaces, except to create an instance of the concrete component with the new keyword. I end up with code like this: public class PasswordResetHelper { public void ResetPassword() { IEmailSender mySender = new MyEmailSender() ; //...call interface methods to configure e-mail details... mySender.SendEmail(); } } This undermines my goal of being able to replace MyEmailSender without having to change PasswordReset helper and means that I am only part of the way to loosely coupled components. The PasswordResetHelper class is configuring and sending e-mails through the IEmailSender interface, but to create an object that implements that interface, it had to create an instance of MyEmailSender. In fact, I have made things worse for myself because PasswordResetHelper now depends on the MyEmailSender class and the IEmailSender interface, as shown in Figure 3-6. Figure 3-6. Components which are tightly coupled after all What I need is a way to get objects that implement an interface without having to create the object directly. The solution to this problem is called dependency injection (DI), also known as Inversion of Control (IoC). DI is a design pattern that completes the loose coupling process. As I describe DI, you might wonder what the fuss is about, but bear with me—this is an important concept that is central to effective MVC development and it can cause a lot of confusion. Breaking and Declaring Dependencies There are two parts to the DI pattern. The first is that I remove any dependencies on concrete classes from my component—in 71 this case PasswordResetHelper. I do this by creating a class constructor that accepts implementations of the interfaces I need as arguments, like this: public class PasswordResetHelper { private IEmailSender emailSender; public PasswordResetHelper(IEmailSender emailSenderParam) { emailSender = emailSenderParam; } public void ResetPassword() { // ...call interface methods to configure e-mail details... emailSender.SendEmail(); } } The constructor for the PasswordResetHelper class is now said to declare a dependency on the IEmailSender interface, meaning that it can’t be created and used unless it receives an object that implements the IEmailSender interface. In declaring its dependency, the PasswordResetHelper class no longer has any knowledge of MyEmailSender, it only depends on the IEmailSender interface. In short, the PassworsResetHelper no longer knows or cares how the IEmailSender interface is implemented. Injecting Dependencies The second part of the DI pattern is to inject the dependencies declared by the PasswordResetHelper class when I create instances of it, hence the term dependency injection. All this really means is that I need to decide which class that implements the IEmailSender interface I am going to use, create an object from that class and then pass the object as an argument to the PasswordResetHelper constructor. Note The PasswordResetHelper class declares its dependencies through its constructor. This is known as constructor injection. I could also declare dependencies to be injected through a public property, known as setter injection. The dependencies are injected into the PasswordResetHelper at runtime; that is to say, an instance of some class that implements the IEmailSender interface will be created and passed to the PasswordResetHelper constructor during instantiation. There is no compile-time dependency between PasswordResetHelper and any class that implements the interfaces it depends on. Because the dependencies are dealt with at runtime, I can decide which interface implementations are going to be used when I run the application. I can choose between different e-mail providers or inject a special mocked implementation for testing. Dependency injection lets me achieve the relationships I was aiming for in Figure 3-5. Using a Dependency Injection Container I have resolved my dependency issue, but how do I instantiate the concrete implementation of interfaces without creating dependencies somewhere else in the application? As it stands, I still have to have statements somewhere in the application like these: ... IEmailSender sender = new MyEmailSender(); helper = new PasswordResetHelper(sender); ... The answer is to use a dependency injection container, also known as an IoC container. This is a component that acts as a broker between the dependencies that a class like PasswordResetHelper declares and the classes that can be used to resolve those dependencies, such as MyEmailSender. I register the set of interfaces or abstract types that my application uses with the DI container, and specify which implementation classes should be instantiated to satisfy dependencies. So, I would register the IEmailSender interface with 72 the container and specify that an instance of MyEmailSender should be created whenever an implementation of IEmailSender is required. When I want a PasswordResetHelper object in my application, I ask the DI container to create one for me. It knows that the PasswordResetHelper has declared a dependency on the IEmailSender interface and it knows that that I have specified that I want to use the MyEmailSender class as the implementation of that interface. The DI container puts these two pieces of information together, creates the MyEmailSender object and then uses it as an argument to create a PasswordResetHelper object, which I am then able to use in the application. Note It is important to note that I no longer create the objects in my application myself using the new keyword. Instead, I go to the DI container and request the objects I need. It can take a while to get used to this when you are new to DI, but as you’ll see, the MVC Framework provides some features to make the process simpler. I do not need to write my own DI container—there are some great open source and freely licensed implementations available. The one I like and use in my own projects is called Ninject and you can get details at www.ninject.org. I’ll introduce you to using Ninject in Chapter 6 and show you how to install the package using NuGet. Tip Microsoft has created its own DI container, called Unity. I are going to use Ninject, however, because I like it and to demonstrate the ability to mix and match tools when using MVC. If you want more information about Unity, see unity.codeplex.com. The role of a DI container may seem simple and trivial, but that is not the case. A good DI container, such as Ninject, has some clever features: Dependency chain resolution: If you request a component that has its own dependencies (e.g., constructor parameters), the container will satisfy those dependencies, too. So, if the constructor for the MyEmailSender class requires an implementation of the INetworkTransport interface, the DI container will instantiate the default implementation of that interface, pass it to the constructor of MyEmailSender and return the result as the default implementation of IEmailSender. Object lifecycle management: If you request a component more than once, should you get the same instance each time or a fresh new instance? A good DI container will let you configure the lifecycle of a component, allowing you to select from predefined options including singleton (the same instance each time), transient (a new instance each time), instance-per- thread, instance-per-HTTP-request, instance-from-a-pool, and many others. Configuration of constructor parameter values: If the constructor for my implementation of the INetworkTransport interface requires a string called serverName, for example, you should be able to set a value for it in your DI container configuration. It is a crude but simple configuration system that removes any need for your code to pass around connection strings, server addresses, and so forth. Writing your own DI container is an excellent way to understand how C# and .NET handle types and reflection and I recommend it as a good project for a rainy weekend. But don’t be tempted to deploy your code in a real project. Writing a reliable, robust and high-performance DI container is difficult and you should find a proven and tested package to use. I like Ninject, but there are plenty of others available and you are sure to find something that suits your development style. Getting Started with Automated Testing The ASP.NET MVC Framework is designed to make it as easy as possible to set up automated tests and use development methodologies such as test-driven development (TDD), which I explain later in this chapter. ASP.NET MVC provides an ideal platform for automated testing and Visual Studio has some solid testing features. Between them they make designing and running tests simple and easy. In broad terms, Web application developers today focus on two kinds of automated testing. The first is unit testing, which is a way to specify and verify the behavior of individual classes (or other small units of code) in isolation from the rest of the application. The second type is integration testing, which is a way to specify and verify the behavior of multiple components working together, up to and including the entire Web application. Both kinds of testing can be valuable in Web applications. Unit tests, which are simple to create and run, are brilliantly precise when you are working on algorithms, business logic, or other back-end infrastructure. The value of integration testing is that it can model how a user will interact with the UI, and can cover the entire technology stack that your application uses, including the 73 Web server and database. Integration testing tends to be better at detecting new bugs that have arisen in old features; this is known as regression testing. Understanding Unit Testing In the .NET world, you create a separate test project in your Visual Studio solution to hold test fixtures . This project will be created when you first add a unit test, or can be set up automatically when you use an MVC project template. A test fixture is a C# class that defines a set of test methods: one method for each behavior you want to verify. A test project can contain multiple test fixture classes. GETTING THE UNIT TEST FEVER Being able to perform unit testing is one of the benefits of working with the MVC Framework, but it isn’t for everyone and I have no intention of pretending otherwise. If you have not encountered unit testing before, then I encourage you to give it a try and see how it works out. I like unit testing and I use it in my own projects, but not all of them and not as consistently as you might expect. I tend to focus on writing unit tests for features and functions that I know will be hard to write and that are likely to be the source of bugs in deployment. In these situations, unit testing helps me structure my thoughts about how to best implement what I need. I find that just thinking about what I test helps throw up ideas about potential problems–and that’s before I start dealing with actual bugs and defects. That said, unit testing is a tool and not a religion and only you know how much testing–and what kind of testing–you require. If you don’t find unit testing useful or if you have a different methodology that suits you better, then don’t feel you need to unit test just because it is fashionable. (Although if you don’t have a better methodology and you are not testing at all, then you are probably letting users find your bugs and you are officially a bad person. You don’t have to unit test, but you really should do some testing of some kind). Note I show you how to create a test project and populate it with unit tests in Chapter 6. The goal for this chapter is just to introduce the concept of unit testing and give you an idea of what a test fixture looks like and how it is used. To get started, I have created a class from an imaginary application, as shown in Listing 3-1. The class is called AdminController and it defines the ChangeLoginName method, which allows my imaginary users to change their passwords. Listing 3-1. The Definition of the AdminController Class using System.Web.Mvc; namespace TestingDemo { public class AdminController : Controller { private IUserRepository repository; public AdminController(IUserRepository repo) { repository = repo; } public ActionResult ChangeLoginName(string oldName, string newName) { User user = repository.FetchByLoginName(oldName); user.LoginName = newName; repository.SubmitChanges(); // render some view to show the result return View(); } } 74 } Tip I created the classes for this demonstration in a new Visual Studio project called TestingDemo. You don’t need to recreate the examples in this section to follow along but I have included the project in the source code download available from Apress.com. The controller relies on some model classes and an interface, which you can see in Listing 3-2. Once again, these are not from a real project and I have simplified these classes to make demonstrating the test easier. I am not suggesting that you create user classes that have just a single string property, for example. Listing 3-2. The Model Classes and Interface that the AdminController Relies On namespace TestingDemo { public class User { public string LoginName { get; set; } } public interface IUserRepository { void Add(User newUser); User FetchByLoginName(string loginName); void SubmitChanges(); } public class DefaultUserRepository : IUserRepository { public void Add(User newUser) { // implement me } public User FetchByLoginName(string loginName) { // implement me return new User() { LoginName = loginName }; } public void SubmitChanges() { // implement me } } } The User class represents a user in my application. Users are created, managed and stored through a repository whose functionality is defined by the IUserRepository interface and there is a partially complete implementation of this interface in the DefaultUserRepository class. My goal in this section is to write a unit test for the functionality provided by the ChangeLoginName method defined by the AdminController, as shown in Listing 3-3. Listing 3-3. A Test Fixture for the AdminController.ChangeLoginName Method using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TestingDemo.Tests { [TestClass] 75 public class AdminControllerTests { [TestMethod] public void CanChangeLoginName() { // Arrange (set up a scenario) User user = new User() { LoginName = "Bob" }; FakeRepository repositoryParam = new FakeRepository(); repositoryParam.Add(user); AdminController target = new AdminController(repositoryParam); string oldLoginParam = user.LoginName; string newLoginParam = "Joe"; // Act (attempt the operation) target.ChangeLoginName(oldLoginParam, newLoginParam); // Assert (verify the result) Assert.AreEqual(newLoginParam, user.LoginName); Assert.IsTrue(repositoryParam.DidSubmitChanges); } } class FakeRepository : IUserRepository { public List Users = new List(); public bool DidSubmitChanges = false; public void Add(User user) { Users.Add(user); } public User FetchByLoginName(string loginName) { return Users.First(m => m.LoginName == loginName); } public void SubmitChanges() { DidSubmitChanges = true; } } } The test fixture is the CanChangeLoginName method. Notice that the method is decorated with the TestMethod attribute and that the class it belongs to—called AdminControllerTests—is decorated with the TestClass attribute. This is how Visual Studio finds the test fixture. The CanChangeLoginName method follows a pattern known as arrange/act/assert (A/A/A). Arrange refers to setting up the conditions for the test, act refers to performing the test, and assert refers to verifying that the result was the one that was required. Being consistent about the structure of your unit test methods makes them easier to read, something you’ll appreciate when your project contains hundreds of unit tests. The test fixture uses a test-specific fake implementation of the IUserRepository interface to simulate a specific condition—in this case, when there is a single member, Bob, in the repository. Creating the fake repository and the User are done in the arrange section of the test. Next, the method being tested—AdminController.ChangeLoginName—is called. This is the act section of the test. Finally, I check the results using a pair of Assert calls (this is the assert part of the test). The Assert method is provided by the Visual Studio test suite and lets me check for specific outcomes. I run the test from the Visual Studio Test menu and receive visual feedback about the tests as they are performed, as shown in Figure 3-7. 76 Figure 3-7. Visual feedback on the progress of unit tests If the test runs without throwing any unhandled exceptions and all of the Assert statements pass without problems, the Test Explorer window shows a green light. If not, you get a red light and details of what went wrong. Note You can see how my use of DI has helped with unit testing. I was able to create a fake implementation of the repository and inject it into the controller to create a specific scenario. I am a big fan of DI and this is one of the reasons. It might seem like I have gone to a lot of effort to test a simple method, but it wouldn’t require much more code to test something far more complex. If you find yourself considering skipping small tests like this one, remember that test fixtures help to uncover bugs that can sometimes be hidden in more complex tests. One improvement I could have made to my test is to eliminate test-specific fake classes like FakeMembersRepository by using a mocking tool—I show you how to do this in Chapter 6. Using TDD and the Red-Green-Refactor Workflow With test-driven development (TDD), you use unit tests to help design your code. This can be an odd concept if you are used to testing after you have finished coding, but there is a lot of sense in this approach. The key concept is a development workflow called red-green-refactor. It works like this: Determine that you need to add a new feature or method to your application. Write the test that will validate the behavior of the new feature when it is written. Run the test and get a red light Write the code that implements the new feature. Run the test again and correct the code until you get a green light. Refactor the code if required. For example, reorganize the statements, rename the variables, and so on. Run the test to confirm that your changes have not changed the behavior of your additions. This workflow is repeated for every feature you add. TDD inverts the traditional development process: you start by writing tests for the perfect implementation of a feature, knowing that the tests will fail. You then implement the feature, creating each aspect of its behavior to pass one or more tests. This cycle is the essence of TDD. There is a lot to recommend it as a development style, not least because it makes a programmer think about how a change or enhancement should behave before the coding starts. You always have a clear end-point in view and a way to check that you are there. And if you have unit tests that cover the rest of your application, you can be sure that your additions have not changed the behavior elsewhere. TDD seems a little odd when you first try it, but it is strangely empowering, and writing the tests first make you consider what a perfect implementation should do before you become biased by the techniques that you use to write the code. 77 The drawback of TDD is that it requires discipline. As deadlines get closer, the temptation is always to discard TDD and just start writing code or, as I have witnessed several times on projects, sneakily discard problematic tests to make code appear in better shape than it really is. For these reasons, TDD should be used in established and mature development teams where there is generally a high level of skill and discipline or in teams where there the team leads can enforce good practice, even in the face of time constraints. Tip You can see a simple example of TDD in Chapter 6 when I demonstrate the testing tools built into Visual Studio. Understanding Integration Testing For Web applications, the most common approach to integration testing is UI automation, which means simulating or automating a Web browser to exercise the application’s entire technology stack by reproducing the actions that a user would perform, such as pressing buttons, following links, and submitting forms. The two best-known open source browser automation options for .NET developers are Selenium RC (http://seleniumhq.org/), which consists of a Java “server” application that can send automation commands to Internet Explorer, Firefox, Safari, or Opera, plus clients for .NET, Python, Ruby, and multiple others so that you can write test scripts in the language of your choice. Selenium is powerful and mature; its only drawback is that you have to run its Java server. WatiN (http://watin.org), a .NET library that can send automation commands to Internet Explorer or Firefox. Its API isn’t as powerful as Selenium, but it comfortably handles most common scenarios and is easy to set up. You need only reference a single DLL. Integration testing is an ideal complement to unit testing. Although unit testing is well suited to validating the behavior of individual components at the server, integration testing lets you create tests that are client-focused, recreating the actions of a user. As a result, it can highlight problems that come from the interaction between components, hence the term integration testing. And because integration testing for a Web application is done through the browser, you can test that JavaScript behaviors work the way they are supposed to, something that is difficult with unit testing. There are some drawbacks. Integration testing takes more time. It takes longer to create the tests and longer to perform them. And integration tests can be brittle. If you change the id attribute of an element that is checked in a test, for example, the test can (and usually will) fail. As a consequence of the additional time and effort required, integration testing is often done at key project milestones, perhaps after a weekly source code check-in, or when major functional blocks are completed. Integration testing is every bit as useful as unit testing and it can highlight problems that unit testing cannot. The time required to set up and run integration testing is worthwhile, and I encourage you to add it to your development process. I am not going to get into integration testing in this book. It is outside of my focus on the MVC Framework. Any web app can benefit from integration testing and there are no special features in the MVC Framework to support this activity. Integration testing is a separate art and what is true when performing integration testing on any Web application is also true for MVC. Summary In this chapter, I introduced you to the MVC architectural pattern and compared it to some other patterns you may have seen or heard of before. I discussed the significance of the domain model and introduced dependency injection, which allows us to decouple components to enforce a strict separation between the parts of an application. I demonstrated a simple unit test and you saw how decoupled components and dependency injection make unit testing simple and easy. In the next chapter, I describe the essential C# language features that are used in MVC Framework applications. 78 CHAPTER 4 Essential Language Features C# is a feature-rich language and not all programmers are familiar with all of the features I rely on in this book. In this chapter, I describe the C# language features that a good MVC programmer needs to know and that I use in examples throughout this book. I provide only a short summary of each feature. If you want more in-depth coverage of C# or LINQ, three of my books may be of interest. For a complete guide to C#, try Introducing Visual C#; for in-depth coverage of LINQ, check out Pro LINQ in C#; and for a detailed examination of the .NET support for asynchronous programming see Pro .Net Parallel Programming in C#. All of these books are published by Apress. Table 4-1 provides the summary for this chapter. Table 4-1. Chapter Summary Problem Solution Listing Simplify C# properties Use automatically implemented properties 1–7 Create an object and sets its properties in a single step Use an object or collection initializer 8–10 Add functionality to a class which cannot be modified Use an extension method 11–18 Simplify the use of delegates Use a lambda expression 19–23 Use implicit typing Use the var keyword 24 Create objects without defining a type Use an anonymous type 25–26 Query collections of objects as though there were a database Use LINQ 27–31 Simplify the use of asynchronous methods Use the async and await keywords 32–33 Preparing the Example Project To demonstrate the language features in this part of the book, I have created a new Visual Studio project called LanguageFeatures using the ASP.NET MVC Web Application template. I selected the Empty option for the initial content and checked the option for MVC folders and references, just as I did in Chapter 2. The language features that I describe in this chapter are not specific to MVC, but Visual Studio Express 2013 for Web doesn’t support creating projects that can write to the console, so you will have to create an MVC app if you want to follow along with the examples. I need a simple controller to demonstrate these language features, so I created the HomeController.cs file in the Controllers folder–I did this by right-clicking on the Controllers folder in the Solution Explorer, selecting Add Controller from the pop-up menu, selecting MVC 5 Controller–Empty from the Add Scaffold menu, and clicking the Add button. I set the name to HomeController in the Add Controller dialog and clicked the Add button to create the controller class file, the edited contents of which you can see in Listing 4-1. Listing 4-1. The Initial Content of the HomeController.cs File using System; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { 79 return "Navigate to a URL to show an example"; } } } I will create action methods for each example, so the result from the Index action method is a basic message to keep the project simple. Caution The HomeController class won’t compile at the moment because it imports the LanguageFeatures.Models namespace. This namespace won’t create until I add a class to the Models folder, which I do as part of the first example in the next section. To display the results from my action methods, I right-clicked the Index action method, selected Add View and created a new view called Result. You can see the contents of the view file in Listing 4-2. (It doesn’t matter which options you select in the Add View dialog because you will replace the initial content of the file with the markup shown in the listing). Listing 4-2. The Contents of the Result.cshtml File @model String @{ Layout = null; } Result
@Model
You can see that this is a strongly typed view, where the model type is String–for the most part, the examples that follow are not complex examples and I can represent the results as a simple string. Adding the System.Net.Http Assembly Later in the chapter, I’ll be using an example that relies on the System.Net.Http assembly, which isn’t added to MVC projects by default. Select Add Reference from the Visual Studio Project menu to open the Reference Manager window. Ensure that the Assemblies section is selected on the left-hand side and locate and check the System.Net.Http item, as shown in Figure 4-1. 80 Figure 4-1. Adding an assembly to the project Using Automatically Implemented Properties The regular C# property feature lets you expose a piece of data from a class in a way that decouples the data from how it is set and retrieved. Listing 4-3 contains a simple example in a class called Product, which I added to the Models folder of the LanguageFeatures project in a class file called Product.cs Listing 4-3. Defining a Property in the Product.cs File namespace LanguageFeatures.Models { public class Product { private string name; public string Name { get { return name; } set { name = value; } } } } The property, called Name, is shown in bold. The statements in the get code block (known as the getter) are performed when the value of the property is read, and the statements in the set code block (known as the setter) are performed when a value is assigned to the property (the special variable value represents the assigned value). A property is consumed by other classes as though it were a field, as shown in Listing 4-4, which shows an AutoProperty action method I added to the Home controller. Listing 4-4. Consuming a Property in the HomeController.cs File using System; using System.Web.Mvc; using LanguageFeatures.Models; 81 namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } public ViewResult AutoProperty() { // create a new Product object Product myProduct = new Product(); // set the property value myProduct.Name = "Kayak"; // get the property string productName = myProduct.Name; // generate the view return View("Result", (object)String.Format("Product name: {0}", productName)); } } } You can see that the property value is read and set just like a regular field. Using properties is preferable to using fields because you can change the statements in the get and set blocks without needing to change the classes that depend on the property. Tip You may notice that I cast the second argument to the View method to an object in Listing 4-4. This is because the View method has an overload that accepts two String arguments and which has a different meaning to the overload that accepts a String and an object. To avoid calling the wrong one, I explicitly cast the second argument. I return to the View method and its overloads in Chapter 20. You can see the effect of this example by starting the project and navigating to /Home/AutoProperty (which targets the AutoProperty action method and will be the pattern for testing each example in this chapter). Because I pass a string from the action method to the view, I am going to show you the results as text, rather than a screen shot. Here is the result of targeting the action method in Listing 4-4: Product name: Kayak Properties are all well and good, but they become tedious when you have a class that has a lot of properties, all of which mediate access to a field, producing a class file that is needlessly verbose, as shown in Listing 4-5, which shows some additional properties I added to the Product class in the Product.cs file. Listing 4-5. Verbose Property Definitions in the Product.cs File namespace LanguageFeatures.Models { public class Product { private int productID; private string name; private string description; private decimal price; private string category; public int ProductID { get { return productID; } 82 set { productID = value; } } public string Name { get { return name; } set { name = value; } } public string Description { get { return description; } set { description = value; } } //...and so on... } } What I want is the flexibility of properties without having to duplicate the getters and setters. The solution is an automatically implemented property, also known as an automatic property. With an automatic property, you can create the pattern of a field- backed property, without defining the field or specifying the code in the getter and setter, as Listing 4-6 shows. Listing 4-6. Using Automatically Implemented Properties in the Product.cs File namespace LanguageFeatures.Models { public class Product { public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; } } } Notice that I do not define the bodies of the getter and setter or the field that the property is backed by. Both of these are done for me by the C# compiler when the class is compiled. Using an automatic property is no different from using a regular property; the code in the action method in Listing 4-4 will work without any modification. By using automatic properties, I save myself some typing, create code that is easier to read, but still preserve the flexibility that a property provides. If the day comes when I need to change the way a property is implemented, I can return to the regular property format. As a demonstration, Listing 4-7 shows what I would have to do if I needed to change the way the Name property is composed. Listing 4-7. Reverting from an Automatic to a Regular Property in the Product.cs File namespace LanguageFeatures.Models { public class Product { private string name; public int ProductID { get; set; } public string Name { get { return ProductID + name; } 83 set { name = value; } } public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; } } } Note Notice that I must implement both the getter and setter to return to a regular property. C# does not support mixing automatic- and regular-style getters and setters in a single property. Using Object and Collection Initializers Another tiresome programming task is constructing a new object and then assigning values to the properties, as illustrated by Listing 4-8, which shows the addition of a CreateProduct action method to the Home controller. Listing 4-8. Constructing and Initializing an Object with Properties in the HomeController.cs File using System; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } public ViewResult AutoProperty() { // ...statements omitted for brevity... } public ViewResult CreateProduct() { // create a new Product object Product myProduct = new Product(); // set the property values myProduct.ProductID = 100; myProduct.Name = "Kayak"; myProduct.Description = "A boat for one person"; myProduct.Price = 275M; myProduct.Category = "Watersports"; return View("Result", (object)String.Format("Category: {0}", myProduct.Category)); } } } 84 I go through three stages to create a Product object and produce a result: create the object, set the parameter values, and then call the View method so I can display the result through the view. Fortunately, I can use the object initializer feature, which allows me to create and populate the Product instance in a single step, as shown in Listing 4-9. Listing 4-9. Using the Object Initializer Feature in the HomeController.cs File ... public ViewResult CreateProduct() { // create and populate a new Product object Product myProduct = new Product { ProductID = 100, Name = "Kayak", Description = "A boat for one person", Price = 275M, Category = "Watersports" }; return View("Result", (object)String.Format("Category: {0}", myProduct.Category)); } ... The braces ({}) after the call to the Product name form the initializer, which I use to supply values to the parameters as part of the construction process. The same feature let me initialize the contents of collections and arrays as part of the construction process, as demonstrated by Listing 4-10. Listing 4-10. Initializing Collections and Arrays in the HomeController.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } // ...other action methods omitted for brevity... public ViewResult CreateCollection() { string[] stringArray = { "apple", "orange", "plum" }; List intList = new List { 10, 20, 30, 40 }; Dictionary myDict = new Dictionary { { "apple", 10 }, { "orange", 20 }, { "plum", 30 } }; return View("Result", (object)stringArray[1]); } } } 85 The listing demonstrates how to construct and initialize an array and two classes from the generic collection library. This feature is a syntax convenience—it just makes C# more pleasant to use but does not have any other impact or benefit. Using Extension Methods Extension methods are a convenient way of adding methods to classes that you do not own and cannot modify directly. Listing 4- 11 shows a ShoppingCart class, which I added to the Models folder in a file called ShoppingCart.cs file and which represents a collection of Product objects. Listing 4-11. The ShoppingCart Class in the ShoppingCart.cs File using System.Collections.Generic; namespace LanguageFeatures.Models { public class ShoppingCart { public List Products { get; set; } } } This is a simple class that acts as a wrapper around a List of Product objects (I only need a basic class for this example). Suppose I need to be able to determine the total value of the Product objects in the ShoppingCart class, but I cannot modify the class itself, perhaps because it comes from a third party and I do not have the source code. I can use an extension method to add the functionality I need. Listing 4-12 shows the MyExtensionMethods class that I added to the Models folder in the MyExtensionMethods.cs file. Listing 4-12. Defining an Extension Method in the MyExtensionMethods.cs File namespace LanguageFeatures.Models { public static class MyExtensionMethods { public static decimal TotalPrices(this ShoppingCart cartParam) { decimal total = 0; foreach (Product prod in cartParam.Products) { total += prod.Price; } return total; } } } The this keyword in front of the first parameter marks TotalPrices as an extension method. The first parameter tells .NET which class the extension method can be applied to—ShoppingCart in this case. I can refer to the instance of the ShoppingCart that the extension method has been applied to by using the cartParam parameter. My method enumerates the Products in the ShoppingCart and returns the sum of the Product.Price property. Listing 4-13 shows how I apply an extension method in a new action method called UseExtension I added to the Home controller. Listing 4-13. Applying an Extension Method in the HomeController.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using LanguageFeatures.Models; 86 namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } // ...other action methods omitted for brevity... public ViewResult UseExtension() { // create and populate ShoppingCart ShoppingCart cart = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Price = 275M}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} } }; // get the total value of the products in the cart decimal cartTotal = cart.TotalPrices(); return View("Result", (object)String.Format("Total: {0:c}", cartTotal)); } } } Note Extension methods do not let you break through the access rules that classes define for their methods, fields, and properties. You can extend the functionality of a class by using an extension method, but only using the class members that you had access to anyway. The key statement is this one: ... decimal cartTotal = cart.TotalPrices(); ... I call the TotalPrices method on a ShoppingCart object as though it were part of the ShoppingCart class, even though it is an extension method defined by a different class altogether. .NET will find extension classes if they are in the scope of the current class, meaning that they are part of the same namespace or in a namespace that is the subject of a using statement. Here is the result from the UseExtension action method, which you can see by starting the application and navigating to the /Home/UseExtension URL: Total: $378.40 Applying Extension Methods to an Interface I can also create extension methods that apply to an interface, which allows me to call the extension method on all of the classes that implement the interface. Listing 4-14 shows the ShoppingCart class updated to implement the IEnumerable interface. Listing 4-14. Implementing an Interface in the ShoppingCart.cs File 87 using System.Collections; using System.Collections.Generic; namespace LanguageFeatures.Models { public class ShoppingCart : IEnumerable { public List Products { get; set; } public IEnumerator GetEnumerator() { return Products.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } I can now update my extension method so that it deals with IEnumerable, as shown in Listing 4-15. Listing 4-15. An Extension Method That Works on an Interface in the MyExtensionMethods.cs File using System.Collections.Generic; namespace LanguageFeatures.Models { public static class MyExtensionMethods { public static decimal TotalPrices(this IEnumerable productEnum) { decimal total = 0; foreach (Product prod in productEnum) { total += prod.Price; } return total; } } } The first parameter type has changed to IEnumerable, which means that the foreach loop in the method body works directly on Product objects. The switch to the interface means that I can calculate the total value of the Product objects enumerated by any IEnumerable, which includes instances of ShoppingCart but also arrays of Products, as shown in Listing 4-16. Listing 4-16. Extension Methods Applies to Implementations of an Interface in the HomeController.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { 88 public string Index() { return "Navigate to a URL to show an example"; } // ...other action methods omitted for brevity... public ViewResult UseExtensionEnumerable() { IEnumerable products = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Price = 275M}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} } }; // create and populate an array of Product objects Product[] productArray = { new Product {Name = "Kayak", Price = 275M}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} }; // get the total value of the products in the cart decimal cartTotal = products.TotalPrices(); decimal arrayTotal = products.TotalPrices(); return View("Result", (object)String.Format("Cart Total: {0}, Array Total: {1}", cartTotal, arrayTotal)); } } } Note The way that C# arrays implement the IEnumerable interface is a little unusual. You will not find it included in the list of implemented interfaces in the MSDN documentation. The support is handled by the compiler so that code for earlier versions C# will still compile. Odd, but true. I could have used another generic collection class in this example, but I wanted to show off my knowledge of the dark corners of the C# specification. Also odd, but true. If you start the project and target the action method, you will see the following results, which demonstrate that I get the same result from the extension method, irrespective of how the Product objects are collected: Cart Total: 378.40, Array Total: 378.40 Creating Filtering Extension Methods The last thing I want to show you about extension methods is that they can be used to filter collections of objects. An extension method that operates on an IEnumerable and that also returns an IEnumerable can use the yield keyword to apply selection criteria to items in the source data to produce a reduced set of results. Listing 4-17 demonstrates such a method, which I have added to the MyExtensionMethods class. Listing 4-17. A Filtering Extension Method in the MyExtensionMethods.cs File 89 using System.Collections.Generic; namespace LanguageFeatures.Models { public static class MyExtensionMethods { public static decimal TotalPrices(this IEnumerable productEnum) { decimal total = 0; foreach (Product prod in productEnum) { total += prod.Price; } return total; } public static IEnumerable FilterByCategory( this IEnumerable productEnum, string categoryParam) { foreach (Product prod in productEnum) { if (prod.Category == categoryParam) { yield return prod; } } } } } This extension method, called FilterByCategory, takes an additional parameter that allows me to inject a filter condition when I call the method. Those Product objects whose Category property matches the parameter are returned in the result IEnumerable and those that do not match are discarded. Listing 4-18 shows this method being used. Listing 4-18. Using the Filtering Extension Method in the HomeController.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } // ... other action methods omitted for brevity... public ViewResult UseFilterExtensionMethod() { IEnumerable products = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Category = "Watersports", 90 Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} } }; decimal total = 0; foreach (Product prod in products.FilterByCategory("Soccer")) { total += prod.Price; } return View("Result", (object)String.Format("Total: {0}", total)); } } } When I call the FilterByCategory method on the ShoppingCart, only those Products in the Soccer category are returned. If you start the project and target the UseFilterExtensionMethod action method, you will see the following result, which is the sum of the Soccer product prices: Total: 54.45 Using Lambda Expressions I can use a delegate to make my FilterByCategory method more general. That way, the delegate that will be invoked against each Product can filter the objects in any way I choose, as illustrated by Listing 4-19, which shows the Filter extension method I added to the MyExtensionMethods class. Listing 4-19. Using a Delegate in an Extension Method in the MyExtensionMethods.cs File using System; using System.Collections.Generic; namespace LanguageFeatures.Models { public static class MyExtensionMethods { public static decimal TotalPrices(this IEnumerable productEnum) { decimal total = 0; foreach (Product prod in productEnum) { total += prod.Price; } return total; } 91 public static IEnumerable FilterByCategory( this IEnumerable productEnum, string categoryParam) { foreach (Product prod in productEnum) { if (prod.Category == categoryParam) { yield return prod; } } } public static IEnumerable Filter( this IEnumerable productEnum, Func selectorParam) { foreach (Product prod in productEnum) { if (selectorParam(prod)) { yield return prod; } } } } } I used a Func as the filtering parameter, which means that I do not need to define the delegate as a type. The delegate takes a Product parameter and returns a bool, which will be true if that Product should be included in the results. The other end of this arrangement is a little verbose, as illustrated by Listing 4-20, which shows the changes I made to the UseFilterExtensionMethod action method in the Home controller. Listing 4-20. Using the Filtering Extension Method with a Func in the HomeController.cs File ... public ViewResult UseFilterExtensionMethod() { // create and populate ShoppingCart IEnumerable products = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} } }; Func categoryFilter = delegate(Product prod) { return prod.Category == "Soccer"; }; decimal total = 0; 92 foreach (Product prod in products.Filter(categoryFilter)) { total += prod.Price; } return View("Result", (object)String.Format("Total: {0}", total)); } ... I have taken a step forward, in the sense that I can now filter the Product objects using any criteria specified in the delegate, but I must define a Func for each kind of filtering that I want, which is not ideal. The less verbose alternative is to use a lambda expression, which is a concise format for expressing a method body in a delegate. I can use it to replace my delegate definition in the action method, as shown in Listing 4-21. Listing 4-21. Using a Lambda Expression to Replace a Delegate Definition in the HomeController.cs File ... public ViewResult UseFilterExtensionMethod() { // create and populate ShoppingCart IEnumerable products = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} } }; Func categoryFilter = prod => prod.Category == "Soccer"; decimal total = 0; foreach (Product prod in products.Filter(categoryFilter)) { total += prod.Price; } return View("Result", (object)String.Format("Total: {0}", total)); } ... The lambda expression is shown in bold. The parameter is expressed without specifying a type, which will be inferred automatically. The => characters are read aloud as “goes to” and links the parameter to the result of the lambda expression. In my example, a Product parameter called prod goes to a bool result, which will be true if the Category parameter of prod is equal to Soccer. I can make my syntax even tighter by doing away with the Func entirely, as shown in Listing 4- 22. Listing 4-22. A Lambda Expression Without a Func in the HomeController.cs File ... public ViewResult UseFilterExtensionMethod() { IEnumerable products = new ShoppingCart { 93 Products = new List { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} } }; decimal total = 0; foreach (Product prod in products.Filter (prod => prod.Category == "Soccer")) { total += prod.Price; } return View("Result", (object)String.Format("Total: {0}", total)); } ... In this example, I supplied the lambda expression as the parameter to the Filter method. This is a nice and natural way of expressing the filter I want to apply. I can combine multiple filters by extending the result part of the lambda expression, as shown in Listing 4-23. Listing 4-23. Extending the Filtering Expressed by the Lambda Expression in the HomeController.cs File ... public ViewResult UseFilterExtensionMethod() { IEnumerable products = new ShoppingCart { Products = new List { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} } }; decimal total = 0; foreach (Product prod in products .Filter(prod => prod.Category == "Soccer" || prod.Price > 20)) { total += prod.Price; } return View("Result", (object)String.Format("Total: {0}", total)); } ... This revised lambda expression will match Product objects that are in the Soccer category or whose Price property 94 is greater than 20. OTHER FORMS FOR LAMBDA EXPRESSIONS I don’t need to express the logic of my delegate in the lambda expression. I can as easily call a method, like this: prod => EvaluateProduct(prod) If I need a lambda expression for a delegate that has multiple parameters, I must wrap the parameters in parentheses, like this: (prod, count) => prod.Price > 20 && count > 0 And, finally, if I need logic in the lambda expression that requires more than one statement, I can do so by using braces ({}) and finishing with a return statement, like this: (prod, count) => { //...multiple code statements return result; } You do not need to use lambda expressions in your code, but they are a neat way of expressing complex functions simply and in a manner that is readable and clear. I like them a lot, and you will see them used liberally throughout this book. Using Automatic Type Inference The C# var keyword allows you to define a local variable without explicitly specifying the variable type, as demonstrated by Listing 4-24. This is called type inference, or implicit typing. Listing 4-24. Using Type Inference .. var myVariable = new Product { Name = "Kayak", Category = "Watersports", Price = 275M }; string name = myVariable.Name; // legal int count = myVariable.Count; // compiler error ... It is not that myVariable does not have a type. It is just that I am asking the compiler to infer it from the code. You can see from the statements that follow that the compiler will allow only members of the inferred class—Product in this case—to be called. Using Anonymous Types By combining object initializers and type inference, I can create simple data-storage objects without needing to define the corresponding class or struct. Listing 4-25 shows an example. Listing 4-25. Creating an Anonymous Type ... var myAnonType = new { Name = "MVC", Category = "Pattern" }; 95 ... In this example, myAnonType is an anonymously typed object. This does not mean that it is dynamic in the sense that JavaScript variables are dynamic. It just means that the type definition will be created automatically by the compiler. Strong typing is still enforced. You can get and set only the properties that have been defined in the initializer, for example. The C# compiler generates the class based on the name and type of the parameters in the initializer. Two anonymously typed objects that have the same property names and types will be assigned to the same automatically generated class. This means I can create arrays of anonymously typed objects, as demonstrated by Listing 4-26, which shows the CreateAnonArray action method I added to the Home controller. Listing 4-26. Creating an Array of Anonymously Typed Objects in the HomeController.cs File using System; using System.Collections.Generic; using System.Text; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index() { return "Navigate to a URL to show an example"; } // ...other action methods omitted for brevity... public ViewResult CreateAnonArray() { var oddsAndEnds = new[] { new { Name = "MVC", Category = "Pattern"}, new { Name = "Hat", Category = "Clothing"}, new { Name = "Apple", Category = "Fruit"} }; StringBuilder result = new StringBuilder(); foreach (var item in oddsAndEnds) { result.Append(item.Name).Append(""); } return View("Result", (object)result.ToString()); } } } Notice that I use var to declare the variable array. I must do this because I do not have a type to specify, as I would in a regularly typed array. Even though I have not defined a class for any of these objects, I can still enumerate the contents of the array and read the value of the Name property from each of them. This is important, because without this feature, I would not be able to create arrays of anonymously typed objects at all. Or, rather, I could create the arrays, but I would not be able to do anything useful with them. You will see the following results if you run the example and target the action method: MVC Hat Apple Performing Language Integrated Queries 96 All of the features I have described so far are put to good use in LINQ. I love LINQ. It is a wonderful and compelling addition to .NET. If you have never used LINQ, you have been missing out. LINQ is a SQL-like syntax for querying data in classes. Imagine that I have a collection of Product objects, and I want to find the three highest prices and pass them to the View method. Without LINQ, I would end up with something similar to Listing 4-27, which shows the FindProducts action method I added to the Home controller. Listing 4-27. Querying Without LINQ in the HomeController.cs File ... public ViewResult FindProducts() { Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; // define the array to hold the results Product[] foundProducts = new Product[3]; // sort the contents of the array Array.Sort(products, (item1, item2) => { return Comparer.Default.Compare(item1.Price, item2.Price); }); // get the first three items in the array as the results Array.Copy(products, foundProducts, 3); // create the result StringBuilder result = new StringBuilder(); foreach (Product p in foundProducts) { result.AppendFormat("Price: {0} ", p.Price); } return View("Result", (object)result.ToString()); } ... With LINQ, I can significantly simplify the querying process, as demonstrated in Listing 4-28. Listing 4-28. Using LINQ to Query Data in the HomeController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Mvc; using LanguageFeatures.Models; namespace LanguageFeatures.Controllers { public class HomeController : Controller { 97 public string Index() { return "Navigate to a URL to show an example"; } // ...other action methods omitted for brevity... public ViewResult FindProducts() { Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; var foundProducts = from match in products orderby match.Price descending select new { match.Name, match.Price }; // create the result int count = 0; StringBuilder result = new StringBuilder(); foreach (var p in foundProducts) { result.AppendFormat("Price: {0} ", p.Price); if (++count == 3) { break; } } return View("Result", (object)result.ToString()); } } } This is a lot neater. You can see the SQL-like query shown in bold. I order the Product objects in descending order and use the select keyword to return an anonymous type that contains just the Name and Price properties. This style of LINQ is known as query syntax, and it is the kind that developers find most comfortable when they start using LINQ. The wrinkle in this query is that it returns one anonymously typed object for every Product in the array that I used in the source query, so I need to play around with the results to get the first three and print out the details. However, if you are willing to forgo the simplicity of the query syntax, you can get a lot more power from LINQ. The alternative is the dot-notation syntax, or dot notation, which is based on extension methods. Listing 4-29 shows this alternative syntax used to process the Product objects. Listing 4-29. Using LINQ Dot Notation in the HomeController.cs File ... public ViewResult FindProducts() { Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 98 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; var foundProducts = products.OrderByDescending(e => e.Price) .Take(3) .Select(e => new { e.Name, e.Price }); StringBuilder result = new StringBuilder(); foreach (var p in foundProducts) { result.AppendFormat("Price: {0} ", p.Price); } return View("Result", (object)result.ToString()); } ... This LINQ query, shown in bold, is not as nice to look at as the one expressed in query syntax, but not all LINQ features have corresponding C# keywords. For serious LINQ queries, I need to switch to using extension methods. Each of the LINQ extension methods in the listing is applied to an IEnumerable and returns an IEnumerable too, which allows me to chain the methods together to form complex queries. Note All of the LINQ extension methods are in the System.Linq namespace, which you must bring into scope with a using statement before you can make queries. Visual Studio adds the System.Linq namespace to controller classes automatically, but you may need to add it manually elsewhere in an MVC project. The OrderByDescending method rearranges the items in the data source. In this case, the lambda expression returns the value I want used for comparisons. The Take method returns a specified number of items from the front of the results (this is what I couldn’t do using query syntax). The Select method allows me to project my results, specifying the structure I want. In this case, I am projecting an anonymous object that contains the Name and Price properties. Tip Notice that I have not needed to specify the names of the properties in the anonymous type. C# has inferred this from the properties I picked in the Select method. Table 4-2 describes the most useful LINQ extension methods. I use LINQ liberally throughout the rest of this book, and you may find it useful to return to this table when you see an extension method that you have not encountered before. All of the LINQ methods shown in the table operate on IEnumerable. Table 4-2. Some Useful LINQ Extension Methods Extension Method Description Deferred All Returns true if all the items in the source data match the predicate No Any Returns true if at least one of the items in the source data matches the predicate No Contains Returns true if the data source contains a specific item or value No Count Returns the number of items in the data source No First Returns the first item from the data source No FirstOrDefault Returns the first item from the data source or the default value if there are no items No Last Returns the last item in the data source No LastOrDefault Returns the last item in the data source or the default value if there are no items No 99 Max Min Returns the largest or smallest value specified by a lambda expression No OrderBy OrderByDescending Sorts the source data based on the value returned by the lambda expression Yes Reverse Reverses the order of the items in the data source Yes Select Projects a result from a query Yes SelectMany Projects each data item into a sequence of items and then concatenates all of those resulting sequences into a single sequence Yes Single Returns the first item from the data source or throws an exception if there are multiple matches No SingleOrDefault Returns the first item from the data source or the default value if there are no items, or throws an exception if there are multiple matches No Skip SkipWhile Skips over a specified number of elements, or skips while the predicate matches Yes Sum Totals the values selected by the predicate No Take TakeWhile Selects a specified number of elements from the start of the data source or selects items while the predicate matches Yes ToArray ToDictionary ToList Converts the data source to an array or other collection type No Where Filters items from the data source that do not match the predicate Yes Understanding Deferred LINQ Queries You will notice that Table 2 includes a Deferred column. There is an interesting variation in the way that the extension methods are executed in a LINQ query. A query that contains only deferred methods is not executed until the items in the result are enumerated, as demonstrated by Listing 4-30, which shows a simple change to the FindProducts action method. Listing 4-30. Using Deferred LINQ Extension Methods in a Query in the HomeController.cs File ... public ViewResult FindProducts() { Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; var foundProducts = products.OrderByDescending(e => e.Price) .Take(3) .Select(e => new { e.Name, e.Price }); products[2] = new Product { Name = "Stadium", Price = 79600M }; StringBuilder result = new StringBuilder(); foreach (var p in foundProducts) { result.AppendFormat("Price: {0} ", p.Price); } 100 return View("Result", (object)result.ToString()); } ... Between defining the LINQ query and enumerating the results, I changed one of the items in the products array. The output from this example is as follows: Price: 79600 Price: 275 Price: 48.95 You can see that the query is not evaluated until the results are enumerated, and so the change I made—introducing Stadium into the Product array—is reflected in the output. Tip One interesting feature that arises from deferred LINQ extension methods is that queries are evaluated from scratch every time the results are enumerated, meaning that you can perform the query repeatedly as the source data for the changes and get results that reflect the current state of the source data. By contrast, using any of the non-deferred extension methods causes a LINQ query to be performed immediately. Listing 4-31 shows the SumProducts action method I added to the Home controller. Listing 4-31. An Immediately Executed LINQ Query in the HomeController.cs File ... public ViewResult SumProducts() { Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; var results = products.Sum(e => e.Price); products[2] = new Product { Name = "Stadium", Price = 79500M }; return View("Result", (object)String.Format("Sum: {0:c}", results)); } ... This example uses the Sum method, which is not deferred, and produces the following result: Sum: $378.40 You can see that the Stadium item, with its much higher price, has not been included in the results—this is because the results from the Sum method are evaluated as soon as the method is called, rather than being deferred until the results are used. Using Async Methods One of the big recent additions to C# is improvements in the way that asynchronous methods are dealt with. Asynchronous methods go off and do work in the background and notify you when they are complete, allowing your code to take care of other 101 business while the background work is performed. Asynchronous methods are an important tool in removing bottlenecks from code and allow applications to take advantage of multiple processors and processor cores to perform work in parallel. C# and .NET have excellent support for asynchronous methods, but the code tends to be verbose and developers who are not used to parallel programming often get bogged down by the unusual syntax. As an example, Listing 4-32 shows an asynchronous method called GetPageLength, which I defined in a class called MyAsyncMethods and added to the Models folder in a class file called MyAsyncMethods.cs. Listing 4-32. A Simple Asynchronous Method in the MyAsyncMethods.cs File using System.Net.Http; using System.Threading.Tasks; namespace LanguageFeatures.Models { public class MyAsyncMethods { public static Task GetPageLength() { HttpClient client = new HttpClient(); var httpTask = client.GetAsync("http://apress.com"); // we could do other things here while we are waiting // for the HTTP request to complete return httpTask.ContinueWith((Task antecedent) => { return antecedent.Result.Content.Headers.ContentLength; }); } } } Caution This example requires the System.Net.Http assembly, which I added to the project at the start of the chapter. This is a simple method that uses a System.Net.Http.HttpClient object to request the contents of the Apress home page and returns its length. I have highlighted the part of the method that tends to cause confusion, which is an example of a task continuation. .NET represents work that will be done asynchronously as a Task. Task objects are strongly typed based on the result that the background work produces. So, when I call the HttpClient.GetAsync method, what I get back is a Task. This tells me that the request will be performed in the background and that the result of the request will be an HttpResponseMessage object. Tip When I use words like background, I am skipping over a lot of detail in order to make the key points that are important to the world of MVC. The .NET support for asynchronous methods and parallel programming in general is excellent and I encourage you to learn more about it if you want to create truly high-performing applications that can take advantage of multicore and multiprocessor hardware. I come back to asynchronous methods for MVC in Chapter 19. The part that most programmers get bogged down with is the continuation, which is the mechanism by which you specify what you want to happen when the background task is complete. In the example, I have used the ContinueWith method to process the HttpResponseMessage object I get from the HttpClient.GetAsync method, which I do using a lambda expression that returns the value of a property that returns the length of the content I get from the Apress Web server. Notice that I use the return keyword twice: ... 102 return httpTask.ContinueWith((Task antecedent) => { return antecedent.Result.Content.Headers. ContentLength ; }); ... This is the part that makes heads hurt. The first use of the return keyword specifies that I am returning a Task object, which, when the task is complete, will return the length of the ContentLength header. The ContentLength header returns a long? result (a nullable long value) and this means that the result of my GetPageLength method is Task, like this: ... public static Task GetPageLength() { ... Do not worry if this does not make sense—you are not alone in your confusion. And this is a simple example—complex asynchronous operations can chain large numbers of tasks together using the ContinueWith method, which creates code that can be hard to read and harder to maintain. Applying the async and await Keywords Microsoft introduced two keywords to C# that are specifically intended to simplify using asynchronous methods like HttpClient.getAsync. The keywords are async and await and you can see how I have used them to simplify my example method in Listing 4-33. Listing 4-33. Using the Async and Await Keywords in the MyAsyncMethods.cs File using System.Net.Http; using System.Threading.Tasks; namespace LanguageFeatures.Models { public class MyAsyncMethods { public async static Task GetPageLength() { HttpClient client = new HttpClient(); var httpMessage = await client.GetAsync("http://apress.com"); // we could do other things here while we are waiting // for the HTTP request to complete return httpMessage.Content.Headers.ContentLength; } } } I used the await keyword when calling the asynchronous method. This tells the C# compiler that I want to wait for the result of the Task that the GetAsync method returns and then carry on executing other statements in the same method. Applying the await keyword means I can treat the result from the GetAsync method as though it were a regular method and just assign the HttpResponseMessage object that it returns to a variable. And, even better, I can then use the return keyword in the normal way to produce a result from other method—in this case, the value of the ContentLength property. This is a much more natural technique and it means I do not have to worry about the ContinueWith method and multiple uses of the return keyword. When you use the await keyword, you must also add the async keyword to the method signature, as I have done in the example. The method result type does not change—my example GetPageLength method still returns a Task. This is because the await and async are implemented using some clever compiler tricks, meaning that they allow a more 103 natural syntax, but they do not change what is happening in the methods to which they are applied. Someone who is calling my GetPageLength method still has to deal with a Task result because there is still a background operation that produces a nullable long—although, of course, that programmer can also choose to use the await and async keywords as well. Note You will have noticed that I did not provide an MVC example for you to test out the async and await keywords. This is because using asynchronous methods in MVC controllers requires a special technique, and I have a lot of information to present to you before I introduce it in Chapter 19. Summary In this chapter, I gave you an overview of the key C# language features that an effective MVC programmer needs to know. These features are combined in LINQ, which I use to query data throughout this book. As I said, I am a big fan of LINQ, and it plays an important role in MVC applications. I also showed you the async and await keywords, which make it easier to work with asynchronous methods—this is a topic that I return to in Chapter 19 when I show you an advanced technique for integrating asynchronous programming into your MVC controllers. In the next chapter, I turn my attention to the Razor View Engine, which is the mechanism by which dynamic data is inserted into views. 104 CHAPTER 5 Working with Razor A view engine procezses ASP.NET content and looks for instructions, typically to insert dynamic content into the output sent to a browser and Razor is the name of the MVC Framework view engine. There are no changes to Razor in MVC 5 and if you are already familiar with the syntax from earlier versions you can skip ahead. In this chapter, I give you a quick tour of the Razor syntax so you can recognize Razor expressions when you see them. I am not going to supply an exhaustive Razor reference in this chapter; think of this more as a crash course in the syntax. I explore Razor in depth as I continue through the book, within the context of other MVC Framework features. Table 5-1 provides the summary for this chapter. Table 5-1. Chapter Summary Problem Solution Listing Define and access the model type Use the @model and @Model expressions 1–4, 15 Reduce duplication in views Use a layout 5–7, 10–12 Specify a default layout Use the view start view 8, 9 Pass data values to the view from the controller Pass a view model object or the view bag 13, 14 Generate different content based on data values Use Razor conditional statements 16, 17 Enumerate an array or a collection Use a @foreach expression 18, 19 Add a namespace to a view Use a @using expression 20 Preparing the Example Project To demonstrate Razor, I created a new Visual Studio project called Razor using the ASP.NET MVC Web Application template. I selected the Empty option and checked the box to get the initial configuration for an MVC project. Defining the Model I am going to start with a simple domain model called Product, defined in a class file called Product.cs, which I added to the Models folder. You can see the contents of the new file in Listing 5-1. Listing 5-1. The Contents of the Product.cs File namespace Razor.Models { public class Product { public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; } } 105 } Defining the Controller I am going to follow the MVC Framework convention and define a controller called Home as the initial starting point for my project. Right-click the Controllers folder in the Solution Explorer, select Add Controller, select MVC 5 Controller–Empty, click Add and set the name to HomeController. When you click the second Add button, Visual Studio will create the Controllers/HomeController.cs file. Edit the contents to match those shown in Listing 5-2. Listing 5-2. The Content of the HomeController.cs File using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers { public class HomeController : Controller { Product myProduct = new Product { ProductID = 1, Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275M }; public ActionResult Index() { return View(myProduct); } } } The controller defines an action method called Index, in which I create and populate the properties of a Product object. I pass the Product to the View method so that it is used as the model when the view is rendered. I do not specify the name of a view file when I call the View method, so the default view for the action method will be used. Creating the View Right-click on the Index method in the HomeController class and select Add View from the pop-up menu. Ensure that the name of the view is Index, change the Template to Empty and select Product for the model class. (If you don’t see Product as an option for the model, compile the project and start over). Uncheck the View Option boxes and click Add to create the Index.cshtml file in the Views/Home folder. The initial contents of the new view are shown in Listing 5-3. Listing 5-3. The Contents of the Index.cshtml File @model Razor.Models.Product @{ Layout = null; } 106 Index
In the sections that follow, I go through the different parts of a Razor view and demonstrate some of the different things you can do with one. When learning about Razor, it is helpful to bear in mind that views exist to express one or more parts of the model to the user—and that means generating HTML that displays data that is retrieved from one or more objects. If you remember that I am always trying to build an HTML page that can be sent to the client, then everything that Razor does begins to make sense. Note I repeat some information in the following sections that I already touched on in Chapter 2. I want to provide you with a single place in the reference that you can turn to when you need to look up a Razor feature and I thought that a small amount of duplication made this worthwhile. Working with the Model Object Let us start with the first line in the view: ... @model Razor.Models.Product ... Razor statements start with the @ character. In this case, the @model statement declares the type of the model object that I will pass to the view from the action method. This allows me to refer to the methods, fields, and properties of the view model object through @Model, as shown in Listing 5-4, which shows a simple addition to the Index view. Listing 5-4. Referring to a View Model Object Property in the Index.cshtml File @model Razor.Models.Product @{ Layout = null; } Index
@Model.Name
107 Note Notice that I declare the view model object type using @model (lower case m) and access the Name property using @Model (upper case M). This is slightly confusing when you start working with Razor, but it becomes second nature pretty quickly. If you start the project, you’ll see the output shown in Figure 5-1. Figure 5-1. The Effect of Reading a Property Value in the View By using the @model expression, I tell MVC what kind of object I will be working with and Visual Studio takes advantage of this in a couple of ways. First, as you are writing your view, Visual Studio will pop up suggestions of member names when you type @Model followed by a period, as shown in Figure 5-2. This is similar to the way that autocomplete for lambda expressions passed to HTML helper methods works, which I described in Chapter 4. Figure 5-2. Visual Studio offering suggestions for member names based on the @Model expression Equally useful is that Visual Studio will flag errors when there are problems with the view model object members you are referring to. You can see an example of this in Figure 5-3, where I have tried to reference @Model.NotARealProperty. Visual Studio has realized that the Product class I specified at the model type does not have such a property and has highlighted an error in the editor. 108 Figure 5-3. Visual Studio reporting a problem with an @Model expression Working with Layouts The other Razor expression in the Index.cshtml view file is this one: ... @{ Layout = null; } ... This is an example of a Razor code block, which allows me to include C# statements in a view. The code block is opened with @{ and closed with } and the statements it contains are evaluated when the view is rendered. This code block sets the value of the Layout property to null. As I explain in detail in Chapter 20, Razor views are compiled into C# classes in an MVC application and the base class that is used defines the Layout property. I’ll show you how this all works in Chapter 20, but the effect of setting the Layout property to null is to tell the MVC framework that the view is self-contained and will render all of the content required for the client. Self-contained views are fine for simple example apps, but a real project can have dozens of views. Layouts are effectively templates that contain markup that you use to create consistency across your app—this could be to ensure that the right JavaScript libraries are included in the result or that a common look and feel is used throughout. Creating the Layout To create a layout, right-click on the Views folder in the Solution Explorer, click Add New Item from the Add menu and select the MVC 5 Layout Page (Razor) template, as shown in Figure 5-4. 109 Figure 5-4. Creating a new layout Set the name of the file to _BasicLayout.cshtml (notice the first character is an underscore) and click the Add button to create the file. Listing 5-5 shows the contents of the file as it is created by Visual Studio. Note Files in the Views folder whose names begin with an underscore (_) are not returned to the user, which allows the file name to differentiate between views that you want to render and the files that support them. Layouts, which are support files, are prefixed with an underscore. Listing 5-5. The Initial Contents of the _BasicLayout.cshtml File @ViewBag.Title
@RenderBody()
110 Layouts are a specialized form of view and I have highlighted the @ expressions in the listing. The call to the @RenderBody method inserts the contents of the view specified by the action method into the layout markup. The other Razor expression in the layout looks for a property called Title in the ViewBag in order to set the contents of the title element. The elements in the layout will be applied to any view that uses the layout and this is why layouts are essentially templates. In Listing 5-6, I have added some simple markup to the layout to demonstrate how this works. Listing 5-6. Adding Elements to the _BasicLayout.cshtml File @ViewBag.Title

Product Information

@RenderBody()

Visit Apress

I have added a couple of header elements and applied some CSS styles to the div element which contains the @RenderBody expression, just to make it clear what content comes from the layout and what comes from the view. Applying a Layout To apply the layout to the view, I just need to set the value of the Layout property. The layout contains the HTML elements that define the structure of the response to the browser, so I can also remove those elements from the view. You can see how I have applied the layout in Listing 5-7, which shows a drastically simplified Index.cshtml file. Tip I also set a value for the ViewBag.Title property, which will be used as the contents of the title element in the HTML document sent back to the user—this is optional, but good practice. If there is no value for the property, the MVC framework will return an empty title element. Listing 5-7. Using the Layout Property in the Index.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "Product Name"; Layout = "∼/Views/_BasicLayout.cshtml"; } Product Name: @Model.Name The transformation is dramatic, even for such a simple view. What I am left with is focused on presenting data from the view model object to the user, which is ideal–not only does it let me work with simpler markup, but it means that I don’t have to duplicate common elements in every view that I create. To see the effect of the layout, run the example app. The results are shown in Figure 5-5. 111 Figure 5-5. The Effect of Applying a Simple Layout to a View Using a View Start File I still have a tiny wrinkle to sort out, which is that I have to specify the layout file I want in every view. That means that if I need to rename the layout file, I am going to have to find every view that refers to it and make a change, which will be an error-prone process and counter to the general theme of easy maintenance that runs through the MVC framework. I can resolve this by using a view start file. When it renders a view, the MVC framework will look for a file called _ViewStart.cshtml. The contents of this file will be treated as though they were contained in the view file itself and I can use this feature to automatically set a value for the Layout property. To create a view start file, add a new layout file to the Views folder and set the name of the file to _ViewStart.cshtml (once again, notice the leading underscore). Edit the contents of the new file so that they match those shown in Listing 5-8. Listing 5-8. The contents of the _ViewStart.cshtml File @{ Layout = "∼/Views/_BasicLayout.cshtml"; } My view start file contains a value for the Layout property, which means that I can remove the corresponding expression from the Index.cshtml file, as shown in Listing 5-9. Listing 5-9. Updating the Index.cshtml File to Reflect the Use of a View Start File @model Razor.Models.Product @{ ViewBag.Title = "Product Name"; } Product Name: @Model.Name I do not have to specify that I want to use the view start file. The MVC framework will locate the file and use its contents automatically. The values defined in the view file take precedence, which makes it easy to override the view start file. Caution It is important to understand the difference between omitting the Layout property from the view file and setting it to null. If your view is self-contained and you do not want to use a layout, then set the Layout property to null. If you 112 omit the Layout property, then the MVC framework will assume that you do want a layout and that it should use the value it finds in the view start file. Demonstrating Shared Layouts As a quick and simple demonstration of how layouts are shared, I have added a new action method to the Home controller called NameAndPrice. You can see the definition of this method in Listing 5-10, which shows the changes I made to the HomeController.cs file. Listing 5-10. Adding a New Action Method to the HomeController.cs File using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers { public class HomeController : Controller { Product myProduct = new Product { ProductID = 1, Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275M }; public ActionResult Index() { return View(myProduct); } public ActionResult NameAndPrice() { return View(myProduct); } } } The action method passes the myProduct object to the view method, just like the Index action method does—this is not something that you would do in a real project, but I am demonstrating Razor functionality and so a simple example suits my needs. Right-click on the NameAndPrice action method and select Add View from the pop-up menu. Fill in the Add View dialog to match Figure 5-6: set View Name to NameAndPrice; set Template to Empty and set the Model Class to Product. 113 Figure 5-6. Adding a view that relies on a layout Ensure that the Use a Layout Page box is checked–and notice the text beneath the text field. It says that you should leave the textbox empty if you have specified the view you want to use in a view start file. If you were to click the Add button at this point, the view would be created without a value for the Layout property. I want to explicitly specify the view, so click on the button with an ellipsis label (...) that is to the right of the text field. Visual Studio will present you with a dialog that allows you to select a layout file, as shown in Figure 5-7. Figure 5-7. Selecting the layout file 114 The convention for an MVC project is to place layout files in the Views folder, but is only a convention, which is why the left-hand panel of the dialog lets you navigate around the project, just in case you have decided to put them somewhere else. I have only defined one layout file, so select _BasicLayout.cshtml and click the OK button to return to the Add View dialog. You will see that the name of the layout file has been placed in the textbox, as shown in Figure 5-8. Figure 5-8. Specifying a layout file when creating a view Click the Add button to create the /Views/Home/NameAndPrice.cshtml file. You can see the contents of this file in Listing 5-11. Listing 5-11. The Contents of the NameAndPrice.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "NameAndPrice"; Layout = "∼/Views/_BasicLayout.cshtml"; }

NameAndPrice

Visual Studio uses slightly different default content for view files when you specify a layout, but you can see that the result contains the same Razor expressions I used when I applied the layout to a view directly. To complete this example, Listing 5-12 shows a simple addition to the NameAndPrice.cshtml file that displays data values from the view model object. Listing 5-12. Adding to the NameAndPrice.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "NameAndPrice"; Layout = "∼/Views/_BasicLayout.cshtml"; }

NameAndPrice

The product name is @Model.Name and it costs $@Model.Price If you start the app and navigate to /Home/NameAndPrice, you will see the results illustrated by Figure 5-9. As you might have expected, the common elements and styles defined in the layout have been applied to the view, demonstrating how a layout can be used as a template to create a common look and feel (albeit a simple and unattractive one in this example). 115 Figure 5-9. The content in the layout file applied to the NameAndPrice view Note I would have gotten the same result if I had left the text field in the Add View dialog empty and relied on the view start file. I specified the file explicitly only because I wanted to show you the Visual Studio feature which helps you make a selection. Using Razor Expressions Now that I have shown you the basics of views and layouts, I am going to turn to the different kinds of expressions that Razor supports and how you can use them to create view content. In a good MVC Framework application, there is a clear separation between the roles that the action method and view perform. The rules are simple and I have summarized them in Table 5-2. Table 5-2. The Roles Played by the Action Method and the View Component Does Do Doesn’t Do Action Method Passa view model object to the view Pass formatted data to the view View Use the view model object to present content to the user Change any aspect of the view model object I am going to come back to this theme throughout this book. To get the best from the MVC Framework, you need to respect and enforce the separation between the different parts of the app. As you will see, you can do quite a lot with Razor, including using C# statements—but you should not use Razor to perform business logic or manipulate your domain model objects in any way. Equally, you should not format the data that your action method passed to the view. Instead, let the view figure out data it needs to display. You can see a simple example of this in the previous section of this chapter. I defined an action method called NameAndPrice, which displays the value of the Name and Price properties of a Product object. Even though I knew which properties I needed to display, I passed the complete Product object to the view model, like this: ... public ActionResult NameAndPrice() { 116 return View(myProduct); } ... I then used the Razor @Model expression in the view to get the value of the properties I was interested in, like this: ... The product name is @Model.Name and it costs $ @Model.Price ... I could have created the string I wanted to display in the action method and passed it as the view model object to the view. It would have worked, but taking this approach undermines the benefit of the MVC pattern and reduces my ability to respond to changes in the future. As I said, I will return to this theme again, but you should remember that the MVC Framework does not enforce proper use of the MVC pattern and that you must remain aware of the effect of the design and coding decisions you make. PROCESSING VERSUS FORMATTING DATA It is important to differentiate between processing data and formatting it. Views format data, which is why I passed the Product object in the previous section to the view, rather than formatting the object's properties into a display string. Processing data–including selecting the data objects to display–is the responsibility of the controller, which will call on the model to get and modify the data it requires. It can sometimes be hard to figure out where the line between processing and formatting is, but as a rule of thumb, I recommend erring on the side of caution and pushing anything but the most simple of expressions out of the view and into the controller. Inserting Data Values The simplest thing you can do with a Razor expression is to insert a data value into the markup. You can do this using the @Model expression to refer to properties and methods defined by the view model object or use the @ViewBag expression to refer to properties you have defined dynamically using the view bag feature (which I introduced in Chapter 2). You have already seen examples of both these expressions, but for completeness, I have added a new action method to the Home controller called DemoExpressions that passes data to the view using a model object and the view bag. You can see the definition of the new action method in Listing 5-13. Listing 5-13. The DemoExpression Action Method in the HomeController.cs File using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers { public class HomeController : Controller { Product myProduct = new Product { ProductID = 1, Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275M }; public ActionResult Index() { return View(myProduct); } public ActionResult NameAndPrice() { return View(myProduct); } 117 public ActionResult DemoExpression() { ViewBag.ProductCount = 1; ViewBag.ExpressShip = true; ViewBag.ApplyDiscount = false; ViewBag.Supplier = null; return View(myProduct); } } } I have created a strongly typed view called DemoExpression.cshtml in the Views/Home folder to show these basic expressions and you can see the contents of the view file in Listing 5-14. Listing 5-14. The Contents of the DemoExpression.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "DemoExpression"; }
PropertyValue
Name@Model.Name
Price@Model.Price
Stock Level@ViewBag.ProductCount
I created a simple HTML table and used the properties from the model object and the view bag to populate some of the cell values. You can see the result of starting the app and navigating to the /Home/DemoExpression URL, as shown in Figure 5-10. This is just a reconfirmation of the basic Razor expressions that I have been using in the examples so far. 118 Figure 5-10. Using basic Razor expressions to insert data values into the HTML markup The result is not pretty because I have not applied any CSS styles to the HTML elements that the view and the layout generate, but the example serves to reinforce the way in which the basic Razor expressions can be used to display the data passed from the action method to the view. Setting Attribute Values All of my examples so far have set the content of elements, but you can also use Razor expressions to set the value of element attributes. Listing 5-15 shows how I have changed the DemoExpression.cshtml view to use view bag properties to set attribute values. Listing 5-15. Using a Razor Expression to Set an Attribute Value in the DemoExpression.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "DemoExpression"; Layout = "∼/Views/_BasicLayout.cshtml"; } 119
PropertyValue
Name@Model.Name
Price@Model.Price
Stock Level@ViewBag.ProductCount
The containing element has data attributes
Discount: Express: Supplier: I used basic Razor expressions to set the value for some data attributes on a div element. Tip Data attributes, which are attributes whose names are prefixed by data-, have been an informal way of creating custom attributes for many years and have been made part of the formal standard as part of HTML5. I have used the values of the ApplyDiscount, ExpressShip and Supplier view bag properties to set the value of these attributes. Start the example app, target the action method, and look at the HTML source that has been used to render the page. You will see that Razor has set the value of the attributes like this: ...
The containing element has data attributes
... The False and True values correspond to the Boolean view bag values, but Razor has done something sensible for the property whose value is null, which is to render an empty string. But things get interesting in the second set of additions to the view, which are a series of checkboxes whose checked attribute is set to the same view bag properties that I used for the data attributes. The HTML that Razor has rendered is as follows: ... Discount: Express: Supplier: ... Razor is aware of the way that attributes such as checked are used, where the presence of the attribute rather than its value changes the configuration of the element (known as a Boolean attribute in the HTML specification). If Razor had inserted False or null or the empty string as the value of the checked attribute, then the checkbox that the browser displays would be checked. Instead, Razor has deleted the attribute from the element entirely when the value is false or null, creating an effect that is consistent with the view data, as shown in Figure 5-11. 120 Figure 5-11. The effect of deleting attributes whose presence configures their element Using Conditional Statements Razor is able to process conditional statements, which means that I can tailor the output from a view based on values in the view data. This kind of technique is at the heart of Razor, and allows you to create complex and fluid layouts that are still reasonably simple to read and maintain. In Listing 5-16, I have updated the DemoExpression.cshtml view file to include a conditional statement. Listing 5-16. Using a Conditional Razor Statement in the DemoExpression.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "DemoExpression"; Layout = "∼/Views/_BasicLayout.cshtml"; } 121
PropertyValue
Name@Model.Name
Price@Model.Price
Stock Level @switch ((int)ViewBag.ProductCount) { case 0: @: Out of Stock break; case 1: Low Stock (@ViewBag.ProductCount) break; default: @ViewBag.ProductCount break; }
To start a conditional statement, you place an @ character in front of the C# conditional keyword, which is switch in this example. You terminate the code block with a close brace character (}) just as you would with a regular C# code block. Tip Notice that I had to cast the value of the ViewBag.ProductCount property to an int in order to use it with a switch statement. This is required because the Razor switch expression cannot evaluate a dynamic property–you must cast to a specific type so that it knows how to perform comparisons. Inside of the Razor code block, you can include HTML elements and data values into the view output just by defining the HTML and Razor expressions, like this: ... Low Stock (@ViewBag.ProductCount) ... Or like this: ... @ViewBag.ProductCount ... I do not have to put the elements or expressions in quotes or denote them in any special way—the Razor engine will interpret these as output to be processed. However, if you want to insert literal text into the view when it is not contained in an HTML element, then you need to give Razor a helping hand and prefix the line like this: ... @: Out of Stock ... The @: characters prevent Razor from interpreting this as a C# statement, which is the default behavior when it encounters text. You can see the result of the conditional statement in Figure 5-12. 122 Figure 5-12. Using a switch statement in a Razor view Conditional statements are important in Razor views because they allow content to be varied based on the data values that the view receives from the action method. As an additional demonstration, Listing 5-17 shows the addition of an if statement to the DemoExpression.cshtml view. As you might imagine, this is a commonly used conditional statement. Listing 5-17. Using an if Statement in a Razor View in the DemoExpression.cshtml File @model Razor.Models.Product @{ ViewBag.Title = "DemoExpression"; Layout = "∼/Views/_BasicLayout.cshtml"; }
PropertyValue
Name@Model.Name
Price@Model.Price
Stock Level @if (ViewBag.ProductCount == 0) { @:Out of Stock } else if (ViewBag.ProductCount == 1) { Low Stock (@ViewBag.ProductCount) 123 } else { @ViewBag.ProductCount }
This conditional statement produces the same results as the switch statement, but I wanted to demonstrate how you can mesh C# conditional statements with Razor views. I explain how this all works in Chapter 20, when I explore views in depth. Enumerating Arrays and Collections When writing an MVC application, you will often want to enumerate the contents of an array or some other kind of collection of objects and generate content that details each one. To demonstrate how this is done, I have defined a new action method in the Home controller called DemoArray which you can see in Listing 5-18. Listing 5-18. The DemoArray Action Method in the HomeController.cs File using System.Web.Mvc; using Razor.Models; namespace Razor.Controllers { public class HomeController : Controller { Product myProduct = new Product { ProductID = 1, Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275M }; // ...other action methods omitted for brevity... public ActionResult DemoArray() { Product[] array = { new Product {Name = "Kayak", Price = 275M}, new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M} }; return View(array); } } } This action method creates a Product[] object that contains simple data values and passes them to the View method so that the data is rendered using the default view. The Visual Studio scaffold feature won’t let you specify an array as a model type. (I don’t know why, since Razor itself happily supports array.) To create a view for an action method that passes an array, the best approach is to create a view without a model and then manually add the @model expression after the file has been created. In Listing 5-19, you can see the contents of the DemoArray.cshtml file that I created this way in the Views/Home folder and then edited. 124 Listing 5-19. The Contents of the DemoArray.cshtml File @model Razor.Models.Product[] @{ ViewBag.Title = "DemoArray"; Layout = "∼/Views/_BasicLayout.cshtml"; } @if (Model.Length > 0) { @foreach (Razor.Models.Product p in Model) { }
ProductPrice
@p.Name $@p.Price
} else {

No product data

} I used an @if statement to vary the content based on the length of the array that I receive as the view model and a @foreach expression to enumerate the contents of the array and generate a row in an HTML table for each of them. You can see how these expressions match their C# counterpart. You can also see how I created a local variable called p in the foreach loop and then referred to its properties using the Razor expressions @p.Name and @p.Price. The result is that I generate an h2 element if the array is empty and produce one row per array item in an HTML table otherwise. Because my data is static in this example, you will always see the same result, which I have shown in Figure 5-13. 125 Figure 5-13. Generating elements using a foreach statement Dealing with Namespaces You will notice that I had to refer to the Product class by its fully qualified name in the foreach loop in the last example, like this: ... @foreach (Razor.Models.Product p in Model) { ... This can become annoying in a complex view, where you will have many references to view model and other classes. I can tidy up my views by applying the @using expression to bring a namespace into context for a view, just like I would in a regular C# class. Listing 5-20 shows how I have applied the @using expression to the DemoArray.cshtml view. Listing 5-20. Applying the @using Expression in the DemoArray.cshtml File @using Razor.Models @model Product[] @{ ViewBag.Title = "DemoArray"; Layout = "∼/Views/_BasicLayout.cshtml"; } @if (Model.Length > 0) { 126 @foreach (Product p in Model) { }
ProductPrice
@p.Name $@p.Price
} else {

No product data

} A view can contain multiple @using expressions. I have used the @using expression to import the Razor.Models namespace, which means that I can remove the namespace from the @model expression and from within the foreach loop. Summary In this chapter, I have given you an overview of the Razor view engine and how it can be used to generate HTML. I showed you how to refer to data passed from the controller via the view model object and the view bag, and how Razor expressions can be used to tailor responses to the user based on data values. You will see many different examples of how Razor can be used in the rest of the book and I describe how the MVC view mechanism works in detail in Chapter 20. In the next chapter, I describe the essential development and testing tools that underpin the MVC Framework and that help you to get the best from your projects. 127 CHAPTER 6 Essential Tools for MVC In this chapter, I am going to look at three tools that should be part of every MVC programmer’s arsenal: a dependency injection (DI) container, a unit test framework, and a mocking tool. I have picked three specific implementations of these tools for this book, but there are many alternatives for each type of tool. If you cannot get along with the ones I use, do not worry. There are so many out there, that you are certain to find something that suits the way your mind and workflow operate. As I noted in Chapter 3, Ninject is my preferred DI container. It is simple, elegant, and easy to use. There are more sophisticated alternatives, but I like the way that Ninject works with a minimum of configuration. If you do not like Ninject, I recommend trying Unity, which is an alternative from Microsoft. For unit testing, I am going to be using the built-in Visual Studio testing tools. I used to use NUnit, which is a popular .NET unit-testing framework, but Microsoft has made a big push to improve the unit-testing support in Visual Studio and now includes it in the free Visual Studio editions. The result is a unit test framework that is tightly integrated into the rest of the IDE and which has actually become pretty good. The third tool I selected is Moq, which is a mocking tool kit. I use Moq to create implementations of interfaces to use in unit tests. Programmers either love or hate Moq; there is nothing in the middle. Either you will find the syntax elegant and expressive, or you will be cursing every time you try to use it. If you just cannot get along with it, I suggest looking at Rhino Mocks, which is a nice alternative. I introduce each of these tools and demonstrate their core features, but I do not provide exhaustive coverage of these tools. Each could easily fill a book in its own right. I have given you enough to get started and, critically, to follow the examples in the rest of the book. Table 6-1 provides the summary for this chapter. Table 6-1. Chapter Summary Problem Solution Listing Decouple classes Introduce interfaces and declare dependencies on them in the class constructor 1–9, 13– 16 Automatically resolve dependencies expressed using interfaces Use Ninject or another dependency injection container 10 Integrate Ninject into an MVC application Create an implementation of the IDependencyResolver interface that calls the Ninject kernel and register it as a resolver by calling the System.Web.Mvc.DependencyResolver.SetResolver method 11, 12 Inject property and constructor values into newly created objects Use the WithPropertyValue and WithConstructorArgument methods 17–20 Dynamically select an implementation class for an interface Use an Ninject conditional binding 21, 22 Control the lifecycle of the objects that Ninject creates Set an object scope 23–25 Create a unit test Add a unit test project to the solution and annotate a class file with TestClass and TestMethod attributes 26, 27, 29, 30 Check for expected outcomes in a unit test Use the Assert class 28 Focus a unit test on a single feature of component Isolate the test target using mock objects 31–34 128 Note This chapter assumes that you want all of the benefits that come from the MVC Framework, including an architecture that supports lots of testing and an emphasis on creating applications that are easily modified and maintained. I love this stuff, but I know that some readers will just want to understand the features that the MVC Framework offers and not get into the development philosophy and methodology. I am not going to try to convert you. It is a personal decision and you know what you need to do to deliver your projects. I suggest that you at least have a quick skim through this chapter to see what is available, but if you are not the unit-testing type, then you can skip to the next chapter and see how to build a more realistic example MVC app. Preparing the Example Project I am going to start by creating a simple example project, called EssentialTools, which I will use throughout this chapter. I used the ASP.NET MVC Web Application template, selected the Empty option and checked the box to add the basic MVC project content. Creating the Model Classes I added a class file to the Models project folder called Product.cs and set the content as shown in Listing 6-1. This is the model class from in previous chapters and the only change is that the namespace matches that of the EssentialTools project. Listing 6-1. The Contents of the Product.cs File namespace EssentialTools.Models { public class Product { public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; } } } I also want to add a class that will calculate the total price of a collection of Product objects. I added a new class file to the Models folder called LinqValueCalculator.cs and set the contents to match Listing 6-2. Listing 6-2. The Contents of the LinqValueCalculator.cs File using System.Collections.Generic; using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator { public decimal ValueProducts(IEnumerable products) { return products.Sum(p => p.Price); } } } The LinqValueCalculator class defines a single method called ValueProducts, which uses the LINQ Sum method to add together the value of the Price property of each Product object in an enumerable passed to the method (a nice LINQ feature that I use often). My final model class is ShoppingCart and it represents a collection of Product objects and uses a 129 LinqValueCalculator to determine the total value. I created a new class file called ShoppingCart.cs and added the statements shown in Listing 6-3. Listing 6-3. The contents of the ShoppingCart.cs File using System.Collections.Generic; namespace EssentialTools.Models { public class ShoppingCart { private LinqValueCalculator calc; public ShoppingCart(LinqValueCalculator calcParam) { calc = calcParam; } public IEnumerable Products { get; set; } public decimal CalculateProductTotal() { return calc.ValueProducts(Products); } } } Adding the Controller I added a new controller to the Controllers folder called HomeController and set the content to match Listing 6-4. The Index action method creates an array of Product objects and uses a LinqValueCalculator object to produce the total value, which I then pass to the View method. I do not specify a view when I call the View method, so the MVC Framework will select the default view associated with the action method (the Views/Home/Index.cshtml file). Listing 6-4. The Contents of the HomeController.cs File using System.Web.Mvc; using System.Linq; using EssentialTools.Models; namespace EssentialTools.Controllers { public class HomeController : Controller { private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; public ActionResult Index() { LinqValueCalculator calc = new LinqValueCalculator(); ShoppingCart cart = new ShoppingCart(calc) { Products = products }; 130 decimal totalValue = cart.CalculateProductTotal(); return View(totalValue); } } } Adding the View The last addition to the project is the view, called Index. It does not matter which options you check as you create the view as long as you set the contents to match those shown in Listing 6-5. Listing 6-5. The Contents of the Index.cshtml File @model decimal @{ Layout = null; } Value
Total value is $@Model
This view uses the @Model expression to display the value of the decimal passed from the action method. If you start the project, you will see the total value, as calculated by the LinqValueCalculator class, illustrated by Figure 6-1. This is a simple project, but it sets the scene for the different tools and techniques that I describe in this chapter. Figure 6-1. Testing the example app Using Ninject I introduced dependency injection (DI) in Chapter 3. To recap, the idea is to decouple the components in an MVC application, with a combination of interfaces and DI container that creates instances of objects by creating implementations of the interfaces they depend on and injecting them into the constructor. 131 In the sections that follow, I explain a problem I deliberately created in the example for this chapter and show how to use Ninject–my preferred DI container–to solve it. Do not worry if you find that you cannot get along with Ninject–the basic principles are the same for all DI containers and there are many alternatives from which to choose. Understanding the Problem In the example app, I created an example of the basic problem that DI addresses: tightly coupled classes. The ShoppingCart class is tightly coupled to the LinqValueCalculator class and the HomeController class is tightly coupled to both ShoppingCart and LinqValueCalculator. This means that if I want to replace the LinqValueCalculator class, I have to locate and the change the references in the classes that are tightly coupled to it. This is not a problem with such a simple project, but it becomes a tedious and error- prone process in a real project, especially if I want to switch between different calculator implementations (for testing, for example), rather than just replace one class with another. Applying an Interface I can solve part of the problem by using a C# interface to abstract the definition of the calculator functionality from its implementation. To demonstrate this, I have added the IValueCalculator.cs class file to the Models folder and created the interface shown in Listing 6-6. Listing 6-6. The Contents of the IValueCalculator.cs File using System.Collections.Generic; namespace EssentialTools.Models { public interface IValueCalculator { decimal ValueProducts(IEnumerable products); } } I can then implement this interface in the LinqValueCalculator class, as shown in Listing 6-7. Listing 6-7. Applying an Interface in the LinqValueCalculator.cs File using System.Collections.Generic; using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator : IValueCalculator { public decimal ValueProducts(IEnumerable products) { return products.Sum(p => p.Price); } } } The interface allows me to break the tight coupling between the ShoppingCart and LinqValueCalculator class, as shown in Listing 6-8. Listing 6-8. Applying the Interface in the ShoppingCart.cs File using System.Collections.Generic; 132 namespace EssentialTools.Models { public class ShoppingCart { private IValueCalculator calc; public ShoppingCart(IValueCalculator calcParam) { calc = calcParam; } public IEnumerable Products { get; set; } public decimal CalculateProductTotal() { return calc.ValueProducts(Products); } } } I have made some progress, but C# requires me to specify the implementation class for an interface during instantiation, which is understandable because it needs to know which implementation class I want to use–but it means I still have a problem in the Home controller when I create the LinqValueCalculator object, as shown in Listing 6-9. Listing 6-9. Applying the Interface to the HomeController.cs File ... public ActionResult Index() { IValueCalculator calc = new LinqValueCalculator(); ShoppingCart cart = new ShoppingCart(calc) { Products = products }; decimal totalValue = cart.CalculateProductTotal(); return View(totalValue); } ... My goal with Ninject is to reach the point where I specify that I want to instantiate an implementation of the IValueCalculator interface, but the details of which implementation is required are not part of the code in the Home controller. This will mean telling Ninject that LinqValueCalculator is the implementation of the IValueCalculator interface that I want it to use and updating the HomeController class so that it obtains its objects via Ninject, rather than by using the new keyword. Adding Ninject to the Visual Studio Project The simplest way to add Ninject to an MVC project is to use the integrated Visual Studio support for NuGet, which makes it easy to install a wide range of packages and keep them up to date. I used NuGet in Chapter 2 to install the Bootstrap library, but there is a huge catalog of packages available, including Ninject. Select Tools Library Package Manager Package Manager Console in Visual Studio to open the NuGet command line and enter the following commands: Install-Package Ninject -version 3.0.1.10 Install-Package Ninject.Web.Common -version 3.0.0.7 Install-Package Ninject.MVC3 -Version 3.0.0.6 The first command installs the Ninject core package and the others install extensions to the core that makes Ninject work nicely with ASP.NET applications (as I explain shortly). Do not be put off by the reference to MVC3 in the last package name–it works 133 just fine with MVC 5. I have used the version argument to install specific versions of these packages. These are the latest versions as I write this. You should use the version argument to ensure that you get the right results in the examples for this chapter, but you can omit the argument and get the latest (and potentially more recent) releases for real projects. Getting Started with Ninject There are three stages to getting the basic Ninject functionality working, and you can see all of them in Listing 6-10, which shows changes I have made to the Home controller. Tip I am going to go slowly in this section and the sections that follow. Dependency Injection can take a while to understand and I do not want to skip over anything that might help reduce confusion. Listing 6-10. Adding the Basic Ninject Functionality to the Index Action Method in the HomeController.cs File using System.Web.Mvc; using EssentialTools.Models; using Ninject; namespace EssentialTools.Controllers { public class HomeController : Controller { private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; public ActionResult Index() { IKernel ninjectKernel = new StandardKernel(); ninjectKernel.Bind().To (); IValueCalculator calc = ninjectKernel.Get(); ShoppingCart cart = new ShoppingCart(calc) { Products = products }; decimal totalValue = cart.CalculateProductTotal(); return View(totalValue); } } } The first stage is to prepare Ninject for use. To do this, I create an instance of a Ninject kernel, which is the object that is responsible for resolving dependencies and creating new objects. When I need an object, I will use the kernel instead of the new keyword. Here is the statement that creates the kernel from the listing: 134 ... IKernel ninjectKernel = new StandardKernel(); ... I need to create an implementation of the Ninject.IKernel interface, which I do by creating a new instance of the StandardKernel class. Ninject can be extended and customized to use different kinds of kernel, but I only need the built- in StandardKernel in this chapter. (In fact, I have been using Ninject for years and I have only ever needed the StandardKernel). The second stage of the process is to configure the Ninject kernel so that it understands which implementation objects I want to use for each interface I am working with. Here is the statement from the listing that does that: ... ninjectKernel.Bind< IValueCalculator >().To< LinqValueCalculator >(); ... Ninject uses C# type parameters to create a relationship: I set the interface I want to work with as the type parameter for the Bind method and call the To method on the result it returns. I set the implementation class I want instantiated as the type parameter for the To method. This statement tells Ninject that dependencies on the IValueCalculator interface should be resolved by creating an instance of the LinqValueCalculator class. The last step is to use Ninject to create an object, which I do through the kernel Get method, like this: ... IValueCalculator calc = ninjectKernel.Get() ; ... The type parameter used for the Get method tells Ninject which interface I am interested in and the result from this method is an instance of the implementation type I specified with the To method a moment ago. Setting up MVC Dependency Injection The result of the three steps I showed you in the previous section is that the knowledge about the implementation class that should be instantiated to fulfill requests for the IValueCalculator interface has been set up in Ninject. Of course, I have not improved my application because that knowledge remains defined in the Home controller, meaning that the Home controller is still tightly coupled to the LinqValueCalculator class. In the following sections, I will show you how to embed Ninject at the heart of the MVC application, which will allow me to simplify the controller, expand the influence Ninject has so that it works across the app, and move the configuration out of the controller. Creating the Dependency Resolver The first change I am going to make is to create a custom dependency resolver. The MVC Framework uses a dependency resolver to create instances of the classes it needs to service requests. By creating a custom resolver, I can ensure that the MVC Framework uses Ninject whenever it creates an object–including instances of controllers, for example. To set up the resolver, I created a new folder called Infrastructure, which is the folder that I use to put classes that do not fit into the other folders in an MVC application. I added a new class file to the folder called NinjectDependencyResolver.cs, the contents of which you can see in Listing 6-11. Listing 6-11. The Contents of the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using EssentialTools.Models; using Ninject; 135 namespace EssentialTools.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind().To(); } } } The NinjectDependencyResolver class implements the IDependencyResolver interface, which is part of the System.Mvc namespace and which the MVC Framework uses to get the objects it needs. The MVC Framework will call the GetService or GetServices methods when it needs an instance of a class to service an incoming request. The job of a dependency resolver is to create that instance, a task that I perform by calling the Ninject TryGet and GetAll methods. The TryGet method works like the Get method I used previously, but it returns null when there is no suitable binding rather than throwing an exception. The GetAll method supports multiple bindings for a single type, which is used when there are several different implementation objects available. My dependency resolver class is also where I set up my Ninject binding. In the AddBindings method, I use the Bind and To methods to configure up the relationship between the IValueCalculator interface and the LinqValueCalculator class. Register the Dependency Resolver It is not enough to simply create an implementation of the IDependencyResolver interface–I also have to tell the MVC Framework that I want to use it. The Ninject packages I added with NuGet created a file called NinjectWebCommon.cs in the App_Start folder that defines methods called automatically when the application starts, in order to integrate into the ASP.NET request lifecycle. (This is to provide the scopes feature that I describe later in the chapter.) In the RegisterServices method of the NinjectWebCommon class, I add a statement that creates an instance of my NinjectDependencyResolver class and uses the static SetResolver method defined by the System.Web.Mvc.DependencyResolver class to register the resolver with the MVC Framework, as shown in Listing 6-12. Do not worry if this does not make complete sense. The effect of this statement is to create a bridge between Ninject and the MVC Framework support for DI. Listing 6-12. Registering the Resolver in the NinjectWebCommon.cs File ... private static void RegisterServices(IKernel kernel) { System.Web.Mvc.DependencyResolver.SetResolver(new EssentialTools.Infrastructure.NinjectDependencyResolver(kernel)); } ... 136 Refactoring the Home Controller The final step is to refactor the Home controller so that it takes advantage of the facilities I set up in the previous sections. You can see the changes I made in Listing 6-13. Listing 6-13. Refactoring the Controller in the HomeController.cs File using System.Web.Mvc; using EssentialTools.Models; namespace EssentialTools.Controllers { public class HomeController : Controller { private IValueCalculator calc; private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; public HomeController(IValueCalculator calcParam) { calc = calcParam; } public ActionResult Index() { ShoppingCart cart = new ShoppingCart(calc) { Products = products }; decimal totalValue = cart.CalculateProductTotal(); return View(totalValue); } } } The main change I have made is to add a class constructor that accepts an implementation of the IValueCalculator interface, changing the HomeController class so that it declares a dependency. Ninject will provide an object that implements the IValueCalculator interface when it creates an instance of the controller, using the configuration I set up in the NinjectDependencyResolver class in Listing 6-10. The other change I made is to remove any mention of Ninject or the LinqValueCalculator class from the controller. At last, I have broken the tight coupling between the HomeController and LinqValueCalculator class. If you run the example, you will see the result shown in Figure 6-2. Of course, I got this same result when I was instantiating the LinqValueCalculator class directly in the controller. 137 Figure 6-2. The effect of running the example app I have created an example of constructor injection, which is one form of dependency injection. Here is what happened when you ran the example app and Internet Explorer made the request for the root URL of the app: 1. The MVC Framework received the request and figured out that the request is intended for the Home controller. (I will explain how the MVC framework figures this out in Chapter 17). 2. The MVC Framework asked my custom dependency resolver class to create a new instance of the HomeController class, specifying the class using the Type parameter of the GetService method. 3. My dependency resolver asked Ninject to create a new HomeController class, passing on the Type object to the TryGet method. 4. Ninject inspected the HomeController constructor and found that it has declared a dependency on the IValueCalculator interface, for which it has a binding. 5. Ninject creates an instance of the LinqValueCalculator class and uses it to create a new instance of the HomeController class. 6. Ninject passes the HomeController instance to the custom dependency resolver, which returns it to the MVC Framework. The MVC Framework uses the controller instance to service the request. I labored this slightly because DI can be a bit mind-bending when you see it used for the first time. One benefit of the approach I have taken here is that any controller in the application can declare a dependency and the MVC Framework will use Ninject to resolve it. The best part is that I only have to modify my dependency resolver class when I want to replace the LinqValueCalculator with another implementation, because this is the only place where I have to specify the implementation used to satisfy dependencies on the IValueCalculator interface. Creating Chains of Dependency When you ask Ninject to create a type, it examines the dependencies that the type has declared. It also looks at those dependencies to see if they rely on other types–or, put another way, if they declare their own dependencies. If there are additional dependencies, Ninject automatically resolves them and creates instances of all of the classes that are required, working its way along the chain of dependencies so that it can ultimately create an instance of the type you asked for. To demonstrate this feature, I have added a file called Discount.cs to the Models folder and used it to define a new interface and a class that implements it, as shown in Listing 6-14. Listing 6-14. The Contents of the Discount.cs File namespace EssentialTools.Models { public interface IDiscountHelper { decimal ApplyDiscount(decimal totalParam); } public class DefaultDiscountHelper : IDiscountHelper { 138 public decimal ApplyDiscount(decimal totalParam) { return (totalParam - (10m / 100m * totalParam)); } } } The IDiscountHelper defines the ApplyDiscount method, which will apply a discount to a decimal value. The DefaultDiscounterHelper class implements the IDiscountHelper interface and applies a fixed 10 percent discount. I have modified the LinqValueCalculator class so that it uses the IDiscountHelper interface when it performs calculations, as shown in Listing 6-15. Listing 6-15. Adding a Dependency in the LinqValueCalculator.cs File using System.Collections.Generic; using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator: IValueCalculator { private IDiscountHelper discounter; public LinqValueCalculator(IDiscountHelper discountParam) { discounter = discountParam; } public decimal ValueProducts(IEnumerable products) { return discounter.ApplyDiscount (products.Sum(p => p.Price)); } } } The new constructor declares a dependency on the IDiscountHelper interface. I assign the implementation object that the constructor receives to a field and use it in the ValueProducts method to apply a discount to the cumulative value of the Product objects. I bind the IDiscountHelper interface to the implementation class with the Ninject kernel in the NinjectDependencyResolver class, just as I did for IValueCalculator, as shown in Listing 6-16. Listing 6-16. Binding Another Interface to Its Implementation in the NinjectDependencyResolver.cs File ... private void AddBindings() { kernel.Bind().To(); kernel.Bind().To(); } ... I have created a dependency chain. My Home controller depends on the IValueCalculator interface, which I have told Ninject to resolve using the LinqValueCalculator class. The LinqValueCalculator class depends on the IDiscountHelper interface, which I have told Ninject to resolve using the DefaultDiscountHelper class. Ninject resolves the chain of dependencies seamlessly, creating the objects it needs to resolve every dependency and, ultimately in this example, create an instance of the HomeController class to service an HTTP request. Specifying Property and Constructor Parameter Values 139 I can configure the objects that Ninject creates by providing details of values I want applied to properties when I bind the interface to its implementation. To demonstrate this feature, I have revised the DefaultDiscountHelper class so that it defines a DiscountSize property, which I use to calculate the discount amount, as shown in Listing 6-17. Listing 6-17. Adding a Property in the Discount.cs File namespace EssentialTools.Models { public interface IDiscountHelper { decimal ApplyDiscount(decimal totalParam); } public class DefaultDiscountHelper : IDiscountHelper { public decimal DiscountSize { get; set; } public decimal ApplyDiscount(decimal totalParam) { return (totalParam - (DiscountSize / 100m * totalParam)); } } } When I tell Ninject which class to use for an interface, I can use the WithPropertyValue method to set the value for the DiscountSize property in the DefaultDiscountHelper class. You can see the change I made to the AddBindings method in the NinjectDependencyResolver class to set this up, as shown in Listing 6-18. Notice that I supply the name of the property to set as a string value. Listing 6-18. Using the Ninject WithPropertyValue Method in the NinjectDependencyResolver.cs File ... private void AddBindings() { kernel.Bind().To(); kernel.Bind() .To().WithPropertyValue("DiscountSize", 50M); } ... I do not need to change any other binding, nor change the way I use the Get method to obtain an instance of the ShoppingCart class. The property value is set following construction of the DefaultDiscountHelper class, and has the effect of halving the total value of the items. Figure 6-3 shows the result of this change. Figure 6-3. The effect of applying a discount through a property when resolving the dependency chain If you have more than one property value you need to set, you can chain calls to the WithPropertyValue method to cover them all. I can do the same thing with constructor parameters. Listing 6-19 shows the DefaultDiscounterHelper class reworked so that the size of the discount is passed as a constructor parameter. 140 Listing 6-19. Using a Constructor Property in the Discount.cs File namespace EssentialTools.Models { public interface IDiscountHelper { decimal ApplyDiscount(decimal totalParam); } public class DefaultDiscountHelper : IDiscountHelper { public decimal discountSize; public DefaultDiscountHelper(decimal discountParam) { discountSize = discountParam; } public decimal ApplyDiscount(decimal totalParam) { return (totalParam - (discountSize / 100m * totalParam)); } } } To bind this class using Ninject, I specify the value of the constructor parameter using the WithConstructorArgument method in the AddBindings method, as shown in Listing 6-20. Listing 6-20. Specifying a Constructor Parameter in the NinjectDependencyResolver.cs File ... private void AddBindings() { kernel.Bind().To(); kernel.Bind() .To ().WithConstructorArgument("discountParam", 50M); } ... Once again, I can chain these method calls together to supply multiple values and mix and match with dependencies. Ninject will figure out what I need and create it accordingly. Tip Notice that I did not just change the WithPropertyValue call to WithConstructorArgument. I also changed the name of the member targeted so that it matches the C# convention for parameter names. Using Conditional Binding Ninject supports a number of conditional binding methods that allow me to specify which classes the kernel should use to respond to requests for particular interfaces. To demonstrate this feature, I have added a new file to the Models folder of the example project called FlexibleDiscountHelper.cs, the contents of which you can see in Listing 6-21. Listing 6-21. The Contents of the FlexibleDiscountHelper.cs File namespace EssentialTools.Models { public class FlexibleDiscountHelper : IDiscountHelper { public decimal ApplyDiscount(decimal totalParam) { decimal discount = totalParam > 100 ? 70 : 25; return (totalParam - (discount / 100m * totalParam)); } 141 } } The FlexibleDiscountHelper class applies different discounts based on the magnitude of the total. Now that I have a choice of classes that implement the IDiscountHelper interface, I can modify the AddBindings method of the NinjectDependencyResolver to tell Ninject when I want to use each of them, as shown in Listing 6-22. Listing 6-22. Using Conditional Binding in the NinjectDependencyResolver.cs File ... private void AddBindings() { kernel.Bind().To(); kernel.Bind() .To ().WithConstructorArgument("discountParam", 50M); kernel.Bind().To() .WhenInjectedInto(); } ... The new binding specifies that the Ninject kernel should use the FlexibleDiscountHelper class as the implementation of the IDiscountHelper interface when it is creating a LinqValueCalculator object. Notice that I have left the original binding for IDiscountHelper in place. Ninject tries to find the best match and it helps to have a default binding for the same class or interface, so that Ninject has a fallback if the criteria for a conditional binding are not satisfied. Ninject supports a number of different conditional binding methods, the most useful of which I have listed in Table 6-2. Table 6-2. Ninject Conditional Binding Methods Method Effect When(predicate) Binding is used when the predicate—a lambda expression—evaluates to true. WhenClassHas() Binding is used when the class being injected is annotated with the attribute whose type is specified by T. WhenInjectedInto() Binding is used when the class being injected into is of type T. Setting the Object Scope The last Ninject feature helps tailor the lifecycle of the objects that Ninject creates to match the needs of your application. By default, Ninject will create a new instance of every object needed to resolve every dependency each time you request an object. To demonstrate what happens, I have modified the constructor for the LinqValueCalculator class so that it writes a message to the Visual Studio Output window when a new instance is created, as shown in Listing 6-23. Listing 6-23. Adding a Constructor in the LinqValueCalculator.cs File using System.Collections.Generic; using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator : IValueCalculator { private IDiscountHelper discounter; private static int counter = 0; public LinqValueCalculator(IDiscountHelper discountParam) { discounter = discountParam; 142 System.Diagnostics.Debug.WriteLine( string.Format("Instance {0} created", ++counter)); } public decimal ValueProducts(IEnumerable products) { return discounter.ApplyDiscount(products.Sum(p => p.Price)); } } } The System.Diagnostics.Debug class contains a number of methods that can be used to write out debugging messages and I find them useful when following code through to see how it works. I am, sadly, old enough that debuggers were not sophisticated when I started writing code and I still find myself going back to basics when it comes to debugging. In Listing 6-24, I have modified the Home controller so that it demands two implementations of the IValueCalculator interface from Ninject. Listing 6-24. Using Multiple Instances of the Calculator Class in the HomeController.cs File ... public HomeController(IValueCalculator calcParam, IValueCalculator calc2) { calc = calcParam; } ... I do not perform any useful task with the object that Ninject provides–what is important it that I asked for two implementations of the interface. If you run the example and look at the Visual Studio Output window, you will see messages that show Ninject created two instances of the LinqValueCalculator class: Instance 1 created Instance 2 created The LinqValueCalculator can be instantiated repeatedly without any problems–but that is not true for all classes. For some classes, you will want to share a single instance throughout the entire application and for others, you will want to create a new instance for each HTTP request that the ASP.NET platform receives. Ninject lets you control the lifecycle of the objects you create using a feature called a scope, which is expressed using a method call when setting up the binding between an interface and its implementation type. In Listing 6-25, you can see how I applied the most useful scope for MVC Framework applications: the request scope to the LinqValueCalculator class in the NinjectDependencyResolver. Listing 6-25. Using the Request Scope in the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using EssentialTools.Models; using Ninject; using Ninject.Web.Common; namespace EssentialTools.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } 143 public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind().To ().InRequestScope(); kernel.Bind() .To ().WithConstructorArgument("discountParam", 50M); kernel.Bind().To() .WhenInjectedInto(); } } } The InRequestScope extension method, which is in the Ninject.Web.Common namespace, tells Ninject that it should only create one instance of the LinqValueCalculator class for each HTTP request that ASP.NET receives. Each request will get its own separate object, but multiple dependencies resolved within the same request will be resolved using a single instance of the class. You can see the effect of this change by starting the application and looking at the Visual Studio Output window, which will show that Ninject has created only one instance of the LinqValueCalculator class. If you reload the browser window without restarting the application, you will see Ninject create a second object. Ninject provides a range of different object scopes and I have summarized the most useful in Table 6-3. Table 6-3. Ninject Scope Methods Name Effect InTransientScope() This is the same as not specifying a scope and creates a new object for each dependency that is resolved. InSingletonScope() ToConstant(object) Creates a single instance which is shared throughout the application. Ninject will create the instance if you use InSingletonScope or you can provide it with the ToConstant method. InThreadScope() Creates a single instance which is used to resolve dependencies for objects requested by a single thread. InRequestScope() Creates a single instance which is used to resolve dependencies for objects requested by a single HTTP request. Unit Testing with Visual Studio In this book, I use the built-in unit test support that comes with Visual Studio, but there are other .NET unit-test packages available. The most popular is probably NUnit, but all of the test packages do much the same thing. The reason I have selected the Visual Studio test tools is that I like the integration with the rest of the IDE. To demonstrate the Visual Studio unit-test support, I added a new implementation of the IDiscountHelper interface to the example project. Create a new file in the Models folder called MinimumDiscountHelper.cs and ensure that the contents match those shown in Listing 6-26. Listing 6-26. The Contents of the MinumumDiscountHelper.cs File using System; namespace EssentialTools.Models { public class MinimumDiscountHelper : IDiscountHelper { 144 public decimal ApplyDiscount(decimal totalParam) { throw new NotImplementedException(); } } } My objective in this example is to make the MinimumDiscountHelper demonstrate the following behaviors: If the total is greater than $100, the discount will be 10 percent. If the total is between $10 and $100 inclusive, the discount will be $5. No discount will be applied on totals less than $10. An ArgumentOutOfRangeException will be thrown for negative totals. The MinimumDiscountHelper class does not implement any of these behaviors yet. I am going to follow the Test Driven Development (TDD) approach of writing the unit tests and only then implement the code, as described in Chapter 3. Creating the Unit Test Project The first step I take is to create the unit test project, which I do by right-clicking the top-level item in the Solution Explorer (which is labeled Solution 'EssentialTools' for my example app) and selecting Add New Project from the pop-up menu. Tip You can choose to create a test project when you create a new MVC project: there is an Add Unit Tests option on the dialog where you choose the initial content for the project. This will open the Add New Project dialog. Select Test from the Visual C# templates section in the left panel and ensure that Unit Test Project is selected in the middle panel, as shown in Figure 6-4. 145 Figure 6-4. Creating the unit test project Set the project name to EssentialTools.Tests and click the OK button to create the new project, which Visual Studio will add to the current solution alongside the MVC application project. I need to give the test project a reference to the application project so that it can access the classes and perform tests upon them. Right-click the References item for the EssentialTools.Tests project in the Solution Explorer, and then select Add Reference from the pop-up menu. Click Solution in the left panel and check the box next to the EssentialTools item, as shown in Figure 6-5. 146 Figure 6-5. Adding a reference to the MVC project Creating the Unit Tests I will add my unit tests to the UnitTest1.cs file in the EssentialTools.Tests project. The paid-for Visual Studio editions have some nice features for automatically generating test methods for a class that are not available in the Express edition, but I can still create useful and meaningful tests. To get started, I made the changes shown in Listing 6-27. Listing 6-27. Adding the Test Methods to the UnitTest1.cs File using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using EssentialTools.Models; namespace EssentialTools.Tests { [TestClass] public class UnitTest1 { private IDiscountHelper getTestObject() { return new MinimumDiscountHelper(); } [TestMethod] public void Discount_Above_100() { // arrange IDiscountHelper target = getTestObject(); decimal total = 200; // act var discountedTotal = target.ApplyDiscount(total); 147 // assert Assert.AreEqual(total * 0.9M, discountedTotal); } } } I have added a single unit test. A class that contains tests is annotated with the TestClass attribute and individual tests are methods annotated with the TestMethod attribute. Not all methods in a unit test class have to be unit tests. To demonstrate this, I have defined the getTestObject method, which I will use to arrange my tests. Because this method does not have a TestMethod attribute, Visual Studio will not treat it as a unit test. Tip Notice that I had to add a using statement to import the EssentialTools.Models namespace into the test class. Test classes are just regular C# classes and have no special knowledge about the MVC project. It is the TestClass and TestMethod attributes which add the testing magic to the project. You can see that I have followed the arrange/act/assert (A/A/A) pattern in the unit test method that I described in Chapter 3. There are countless conventions about how to name unit tests, but my advice is simply that you use names that make it clear what the test is checking. My unit test method is called Discount_Above_100, which is clear and meaningful to me. But all that really matters is that you (and your team) understand whatever naming pattern you settle on, so you adopt a different naming scheme if you do not like mine. I set up the test method by calling the getTestObject method, which creates an instance of the object I am going to test: the MinimumDiscountHelper class in this case. I also define the total value with which I am going to test. This is the arrange section of the unit test. For the act section of the test, I call the MinimumDiscountHelper.ApplyDiscount method and assign the result to the discountedTotal variable. Finally, for the assert section of the test, I use the Assert.AreEqual method to check that the value I got back from the ApplyDiscount method is 90% of the total that I started with. The Assert class has a range of static methods that you can use in your tests. The class is in the Microsoft.VisualStudio.TestTools.UnitTesting namespace along with some additional classes that can be useful for setting up and performing tests. You can learn more about classes in the namespace at http://msdn.microsoft.com/en-us/library/ms182530.aspx. The Assert class is the one that I use the most and I have summarized the most important methods in Table 6-4. Table 6-4. Static Assert Methods Method Description AreEqual(T, T) AreEqual(T, T, string) Asserts that two objects of type T have the same value. AreNotEqual(T, T) AreNotEqual(T, T, string) Asserts that two objects of type T do not have the same value. AreSame(T, T) AreSame(T, T, string) Asserts that two variables refer to the same object. AreNotSame(T, T) AreNotSame(T, T, string) Asserts that two variables refer to different objects. Fail() Fail(string) Fails an assertion: no conditions are checked. Inconclusive() Inconclusive(string) Indicates that the result of the unit test cannot be definitively established. IsTrue(bool) IsTrue(bool, string) Asserts that a bool value is true. Most often used to evaluate an expression that returns a bool result. IsFalse(bool) IsFalse(bool, string) Asserts that a bool value is false. IsNull(object) IsNull(object, string) Asserts that a variable is not assigned an object reference. IsNotNull(object) IsNotNull(object, string) Asserts that a variable is assigned an object reference. IsInstanceOfType(object, Type) IsInstanceOfType(object, Type, string) Asserts that an object is of the specified type or is derived from the specified type. IsNotInstanceOfType(object, Type) IsNotInstanceOfType(object, Type, string) Asserts that an object is not of the specified type. 148 Each of the static methods in the Assert class allows you to check some aspect of your unit test and the methods throw an exception if the check fails. All of the assertions have to succeed for the unit test to pass. Each of the methods in the table has an overloaded version that takes a string parameter. The string is included as the message element of the exception if the assertion fails. The AreEqual and AreNotEqual methods have a number of overloads that cater to comparing specific types. For example, there is a version that allows strings to be compared without taking case into account. Tip One noteworthy member of the Microsoft.VisualStudio.TestTools.UnitTesting namespace is the ExpectedException attribute. This is an assertion that succeeds only if the unit test throws an exception of the type specified by the ExceptionType parameter. This is a neat way of ensuring that exceptions are thrown without needing to mess around with try...catch blocks in your unit test. Now that I have shown you how to put together one unit test, I have added further tests to the test project to validate the other behaviors I want for my MinimumDiscountHelper class. You can see the additions in Listing 6-28, but these unit tests are so short and simple (which is generally a characteristic of unit tests) that I am not going to explain them in detail. Listing 6-28. Defining the Remaining Tests in the UnitTest1.cs File using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using EssentialTools.Models; namespace EssentialTools.Tests { [TestClass] public class UnitTest1 { private IDiscountHelper getTestObject() { return new MinimumDiscountHelper(); } [TestMethod] public void Discount_Above_100() { // arrange IDiscountHelper target = getTestObject(); decimal total = 200; // act var discountedTotal = target.ApplyDiscount(total); // assert Assert.AreEqual(total * 0.9M, discountedTotal); } [TestMethod] public void Discount_Between_10_And_100() { //arrange IDiscountHelper target = getTestObject(); // act decimal TenDollarDiscount = target.ApplyDiscount(10); decimal HundredDollarDiscount = target.ApplyDiscount(100); decimal FiftyDollarDiscount = target.ApplyDiscount(50); // assert 149 Assert.AreEqual(5, TenDollarDiscount, "$10 discount is wrong"); Assert.AreEqual(95, HundredDollarDiscount, "$100 discount is wrong"); Assert.AreEqual(45, FiftyDollarDiscount, "$50 discount is wrong"); } [TestMethod] public void Discount_Less_Than_10() { //arrange IDiscountHelper target = getTestObject(); // act decimal discount5 = target.ApplyDiscount(5); decimal discount0 = target.ApplyDiscount(0); // assert Assert.AreEqual(5, discount5); Assert.AreEqual(0, discount0); } [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Discount_Negative_Total() { //arrange IDiscountHelper target = getTestObject(); // act target.ApplyDiscount(-1); } } } Running the Unit Tests (and Failing) Visual Studio provides the Test Explorer window for managing and running tests. Select Windows Test Explorer from the Visual Studio Test menu to see the window and click the Run All button near the top-left corner. You will see results similar to the ones shown in Figure 6-6. 150 Figure 6-6. Running the tests in the project You can see the list of tests I defined in the left-hand panel of the Test Explorer window. All of the tests have failed, of course, because I have yet to implement the method I am testing. You can click any of the tests in the window to see details of why it has failed. The Test Explorer window provides a range of different ways to select and filter unit tests and to choose which tests to run. For my simple example project, however, I will just run all of the tests by clicking Run All. Implementing the Feature I have reached the point where I can implement the feature, safe in the knowledge that I will be able to check that the code works as expected when I am finished. For all of my preparation, the implementation of the MinimumDiscountHelper class is simple, as shown by Listing 6-29. Listing 6-29. The Contents of the MinimumDiscountHelper.cs File using System; namespace EssentialTools.Models { public class MinimumDiscountHelper : IDiscountHelper { public decimal ApplyDiscount(decimal totalParam) { if (totalParam < 0) { throw new ArgumentOutOfRangeException(); } else if (totalParam > 100) { return totalParam * 0.9M; } else if (totalParam > 10 && totalParam <= 100) { return totalParam -5; } else { return totalParam; } 151 } } } Testing and Fixing the Code I have left a deliberate error in the code to demonstrate how iterative unit testing with Visual Studio works and you can see the effect of the error if you click the Run All button in the Test Explorer window. You can see the test results in Figure 6-7. Figure 6-7. The effect of implementing the feature with a bug Visual Studio always tries to promote the most useful information to the top of the Test Explorer window. In this situation, this means that it displays failed tests before passed tests. You can see that I passed three of the unit tests, but I have a problem that the Discount_Between_10_And_100 test method detected. When I click the failed test, I can see that my test expected a result of 5, but actually got a value of 10. At this point, I return to my code and see that I have not implemented my expected behaviors properly. Specifically, I do not handle the discounts for totals that are 10 or 100 properly. The problem is in this statement from the MinumumDiscountHelper class: ... } else if (totalParam > 10 && totalParam < 100) { ... The specification that I am working to implement sets out the behavior for values which are between $10 and $100 inclusive, but my implementation excludes those values and only checks for values which are greater than $10, excluding totals which are exactly $10. The solution is simple and is shown in Listing 6-30. Only a single character needs to be added to change the effect of the if statement. Listing 6-30. Fixing the Feature Code in the MinimumDiscountHelper.cs File using System; namespace EssentialTools.Models { public class MinimumDiscountHelper : IDiscountHelper { 152 public decimal ApplyDiscount(decimal totalParam) { if (totalParam < 0) { throw new ArgumentOutOfRangeException(); } else if (totalParam > 100) { return totalParam * 0.9M; } else if (totalParam >= 10 && totalParam <= 100) { return totalParam -5; } else { return totalParam; } } } } When I click the Run All button in the Test Explorer window, the results show that I have fixed the problem and that my code passed all of the tests (see Figure 6-8). Figure 6-8. Passing all of the unit tests This is just a quick introduction to unit testing, and I will further demonstrate unit tests in later chapters as well. The unit test support in Visual Studio is pretty good and I recommend you explore the unit testing documentation on MSDN, which you can find at http://msdn.microsoft.com/en-us/library/dd264975.aspx. Using Moq One of the reasons that I am able to keep my tests so simple in the previous section was because I am testing a single class that depends on no other class to function. Such objects exist in real projects, of course, but you will also need to test objects that cannot function in isolation. In these situations, you need to be able to focus on the class or method you are interested in, so that you are not implicitly testing the dependencies as well. One useful approach is to use mock objects, which simulate the functionality of real objects from your project, but in a specific and controlled way. Mock objects let you narrow the focus of your tests so that you only examine the functionality in which you are interested. The paid-for versions of Visual Studio include support for creating mock objects through a feature called fakes, but I prefer to use a library called Moq, which is simple, easy-to-use and you can use with all Visual Studio editions, including the free ones. Understanding the Problem Before I get into the using Moq, I want to demonstrate the problem that I am trying to fix. In this section, I am going to unit test the LinqValueCalculator class, which I defined in the Models folder of the example project. As a reminder, Listing 6-31 shows the definition of the LinqValueCalculator class. 153 Listing 6-31. The Contents of the LinqValueCalculator.cs File using System.Collections.Generic; using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator : IValueCalculator { private IDiscountHelper discounter; private static int counter = 0; public LinqValueCalculator(IDiscountHelper discountParam) { discounter = discountParam; System.Diagnostics.Debug.WriteLine( string.Format("Instance {0} created", ++counter)); } public decimal ValueProducts(IEnumerable products) { return discounter.ApplyDiscount(products.Sum(p => p.Price)); } } } To test this class, I added a new unit test class to the test project. You do this by right-clicking the test project in the Solution Explorer and selecting Add Unit Test from the pop-up menu. If your Add menu does not have a Unit Test item, select New Item instead and use the Basic Unit Test template. You can see the changes I made to the new file, which Visual Studio names UnitTest2.cs by default, in Listing 6-32. Listing 6-32. Adding a Unit Test for the ShoppingCart Class in the UnitTest2.cs File using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using EssentialTools.Models; using System.Linq; namespace EssentialTools.Tests { [TestClass] public class UnitTest2 { private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; [TestMethod] public void Sum_Products_Correctly() { // arrange var discounter = new MinimumDiscountHelper(); var target = new LinqValueCalculator(discounter); 154 var goalTotal = products.Sum(e => e.Price); // act var result = target.ValueProducts(products); // assert Assert.AreEqual(goalTotal, result); } } } The problem I face is that the LinqValueCalculator class depends on an implementation of the IDiscountHelper interface to operate. In the example, I used the MinimumDiscountHelper class and this presents two different issues. First, I have made my unit test complex and brittle. In order to create a unit test that works, I need to take into account the discount logic in the IDiscountHelper implementation to figure out the expected value from the ValueProducts method. The brittleness comes from the fact that my tests will fail if the discount logic in the implementation changes, even though the LinqValueCalculator class may well be working properly. Second, and most troubling, I have extended the scope of my unit test so that it implicitly includes the MinimumDiscountHelper class. When my unit test fails, I will not know if the problem is in the LinqValueCalculator or MinimumDiscountHelper class. The best unit tests are simple and focused, and my current setup does not allow for either of these characteristics. In the sections that follow, I show you how to add and apply Moq in your MVC project so that you can avoid these problems. Adding Moq to the Visual Studio Project Just like with Ninject earlier in the chapter, the easiest way to add Moq to an MVC project is to use the integrated Visual Studio support for NuGet. Open the NuGet console and enter the following command: Install-Package Moq -version 4.1.1309.1617 -projectname EssentialTools.Tests The projectname argument allows me to tell NuGet that I want the Moq package installed in my unit test project, rather than in the main application. Adding a Mock Object to a Unit Test Adding a mock object to a unit test means telling Moq what kind of object you want to work with, configuring its behavior and then applying the object to the test target. You can see how I added a mock object to my unit test for the LinqValueCalculator in Listing 6-33. Listing 6-33. Using a Mock Object in a Unit Test in the UnitTest2.cs File using EssentialTools.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; using Moq; namespace EssentialTools.Tests { [TestClass] public class UnitTest2 { private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", 155 Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; [TestMethod] public void Sum_Products_Correctly() { // arrange Mock mock = new Mock(); mock.Setup(m => m.ApplyDiscount(It.IsAny())) .Returns(total => total); var target = new LinqValueCalculator(mock.Object); // act var result = target.ValueProducts(products); // assert Assert.AreEqual(products.Sum(e => e.Price), result); } } } The syntax for using Moq is a little odd when you first see it, so I will walk through each stage of the process. Tip Bear in mind that there are a number of different mocking libraries available, so the chances are good that you can find an alternative to suit you if you do not like the way that Moq works, although Moq is actually an easy library to use. You can expect some of the other popular libraries to have manuals hundreds of pages long. Creating a Mock Object The first step is to tell Moq what kind of mock object you want to work with. Moq relies heavily on type parameters, and you can see this in the way that I tell Moq I want to create a mock IDiscountHelper implementation: ... Mock mock = new Mock(); ... I create a strongly typed Mock object, which tells the Moq library the type it will be handling. This is the IDiscountHelper interface for my unit test, but it can be any type that you want to isolate to improve the focus of your unit tests. Selecting a Method In addition to creating the strongly typed Mock object, I also need to specify the way that it behaves. This is at the heart of the mocking process and it allows you to ensure that you establish a baseline behavior in the mock object, which you can use to test the functionality of your target object in the unit test. Here is the statement from the unit test that sets up the behavior I want: ... mock.Setup (m => m.ApplyDiscount(It.IsAny())).Returns (total => total); ... I use the Setup method to add a method to my mock object. Moq works using LINQ and lambda expressions. When I call 156 the Setup method, Moq passes me the interface that I have asked it to implement, cleverly wrapped up in some LINQ magic that I am not going to get into here. This allows me to select the method I want to configure by using a lambda expression. For my unit test, I want to define the behavior of the ApplyDiscount method, which is the only method in the IDiscountHelper interface, and the method I need to test the LinqValueCalculator class. I also have to tell Moq what parameter values I am interested in, which I do using the It class, which I have highlighted as follows: ... mock.Setup(m => m.ApplyDiscount(It.IsAny())).Returns (total => total); ... The It class defines a number of methods that are used with generic type parameters. In this case, I have called the IsAny method using decimal as the generic type. This tells Moq to apply the behavior I am defining whenever I call the ApplyDiscount method any decimal value. Table 6-5 shows the methods that the It class provides, all of which are static. Table 6-5. The Methods of the It Class Method Description Is(predicate) Specifies values of type T for which the predicate will return true. See Listing 6-34 for an example). IsAny() Specifies any value of the type T. IsInRange(min, max, kind) Matches if the parameter is between the defined values and of type T. The final parameter is a value from the Range enumeration and can be either Inclusive or Exclusive. IsRegex(expr) Matches a string parameter if it matches the specified regular expression. I will show you a more complex example later that uses some other It methods, but for the moment I will stick with the IsAny method which allows me to respond to any decimal value. Defining the Result The Returns method allows me to specify the result that Moq will return when I call my mocked method. I specify the type of the result using a type parameter and specify the result itself using a lambda expression. You can see how I have done this for the example: ... mock.Setup(m => m.ApplyDiscount(It.IsAny())).Returns (total => total) ; ... By calling Returns method with a decimal type parameter (i.e., Returns), I tell Moq that I am going to return a decimal value. For the lambda expression, Moq passes me a value of the type I receive in the ApplyDiscount method. I create a pass-through method in the example, in which I return the value that is passed to the mock ApplyDiscount method without performing any operations on it. This is the simplest kind of mock method, but I will show you more sophisticated examples shortly. Using the Mock Object The last step is to use the mock object in the unit test, which I do by reading the value of the Object property of the Mock object: ... var target = new LinqValueCalculator(mock.Object); ... 157 To summarize the example, the Object property returns an implementation of the IDiscountHelper interface where the ApplyDiscount method returns the value of the decimal parameter it is passed. This makes it easy to perform my unit test because I can sum the prices of my test Product objects myself and check that I get the same value back from the LinqValueCalculator object: ... Assert.AreEqual(products.Sum(e => e.Price), result); ... The benefit of using Moq in this way is that my unit test only checks the behavior of the LinqValueCalculator object and does not depend on any of the real implementations of the IDiscountHelper interface in the Models folder. This means that when my tests fail, I know that the problem is either in the LinqValueCalculator implementation or in the way I set up mock object, and solving a problem from either of these sources is simpler and easier than dealing with a chain of real objects and the interactions between them. Creating a More Complex Mock Object I showed you a simple mock object in the last section, but part of the beauty of Moq is the ability to build up complex behaviors to test different situations. In Listing 6-34, I added a new unit test to the UnitTest2.cs file that mocks a more complex implementation of the IDiscountHelper interface. In fact, I used Moq to model the behavior of the MinimumDiscountHelper class. Listing 6-34. Mocking the Behavior of the MinimumDiscountHelper Class in the UnitTest2.cs File using EssentialTools.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System.Linq; namespace EssentialTools.Tests { [TestClass] public class UnitTest2 { private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} }; [TestMethod] public void Sum_Products_Correctly() { // arrange Mock mock = new Mock(); mock.Setup(m => m.ApplyDiscount(It.IsAny())) .Returns(total => total); var target = new LinqValueCalculator(mock.Object); // act var result = target.ValueProducts(products); // assert 158 Assert.AreEqual(products.Sum(e => e.Price), result); } private Product[] createProduct(decimal value) { return new[] { new Product { Price = value } }; } [TestMethod] [ExpectedException(typeof(System.ArgumentOutOfRangeException))] public void Pass_Through_Variable_Discounts() { // arrange Mock mock = new Mock(); mock.Setup(m => m.ApplyDiscount(It.IsAny())) .Returns(total => total); mock.Setup(m => m.ApplyDiscount(It.Is(v => v == 0))) .Throws(); mock.Setup(m => m.ApplyDiscount(It.Is(v => v > 100))) .Returns(total => (total * 0.9M)); mock.Setup(m => m.ApplyDiscount(It.IsInRange(10, 100, Range.Inclusive))).Returns(total => total - 5); var target = new LinqValueCalculator(mock.Object); // act decimal FiveDollarDiscount = target.ValueProducts(createProduct(5)); decimal TenDollarDiscount = target.ValueProducts(createProduct(10)); decimal FiftyDollarDiscount = target.ValueProducts(createProduct(50)); decimal HundredDollarDiscount = target.ValueProducts(createProduct(100)); decimal FiveHundredDollarDiscount = target.ValueProducts(createProduct(500)); // assert Assert.AreEqual(5, FiveDollarDiscount, "$5 Fail"); Assert.AreEqual(5, TenDollarDiscount, "$10 Fail"); Assert.AreEqual(45, FiftyDollarDiscount, "$50 Fail"); Assert.AreEqual(95, HundredDollarDiscount, "$100 Fail"); Assert.AreEqual(450, FiveHundredDollarDiscount, "$500 Fail"); target.ValueProducts(createProduct(0)); } } } In unit test terms, replicating the expected behavior of one of the other model classes would be an odd thing to do, but it is a perfect demonstration of some of the different Moq features. I have defined four different behaviors for the ApplyDiscount method based on the value of the parameter that I receive. The simplest is the catch-all, which returns a value for any decimal value, like this: ... mock.Setup(m => m.ApplyDiscount(It.IsAny())).Returns (total => total); ... 159 This is the same behavior used in the previous example, and I have included it here because the order in which you call the Setup method affects the behavior of the mock object. Moq evaluates the behaviors in reverse order, so that it considers the most recent calls to the Setup method first. This means that you have to take care to create your mock behaviors in order from the most general to the most specific. The It.IsAny condition is the most general condition I define in this example and so I apply it first. If I reversed the order of my Setup calls, this behavior would capture all of the calls I make to the ApplyDiscount method and generate the wrong mock results. Mocking For Specific Values (and Throwing an Exception) For the second call to the Setup method, I have used the It.Is method: ... mock.Setup(m => m.ApplyDiscount(It.Is(v => v == 0))) .Throws(); ... The predicate I have passed to the Is method returns true if the value passed to the ApplyDiscount method is 0. Rather than return a result, I used the Throws method, which causes Moq to throw a new instance of the exception I specify with the type parameter. I also use the Is method to capture values that are greater than 100, like this: ... mock.Setup(m => m.ApplyDiscount(It.Is(v => v > 100))) .Returns(total => (total * 0.9M)); ... The It.Is method is the most flexible way of setting up specific behaviors for different parameter values because you can use any predicate that returns true or false. This is the method I use most often when creating complex mock objects. Mocking For a Range of Values My final use of the It object is with the IsInRange method, which allows me to capture a range of parameter values: ... mock.Setup(m => m.ApplyDiscount(It.IsInRange(10, 100, Range.Inclusive))) .Returns(total => total - 5); ... I have included this for completeness, but in my own projects I tend to use the Is method and a predicate that does the same thing, like this: ... mock.Setup(m => m.ApplyDiscount(It.Is(v => v >= 10 && v <= 100))) .Returns(total => total - 5); ... The effect is the same, but I find the predicate approach more flexible. Moq has a range of extremely useful features and you can see how to apply them by reading the quick start provided at http://code.google.com/p/moq/wiki/QuickStart. Summary In this chapter, I looked at the three tools I find essential for effective MVC development: Ninject, the built-in Visual Studio support for unit testing, and Moq. There are many alternatives, both open source and commercial, for all three tools and you will 160 not lack alternatives if you do not get along with the tools I like and use. You may find that you do not like TDD or unit testing in general, or that you are happy performing DI and mocking manually. That, of course, is entirely your choice. However, I think there are some substantial benefits in using all three tools in the development cycle. If you are hesitant to adopt them because you have never tried them, I encourage you to suspend disbelief and give them a go, at least for the duration of this book. 161 CHAPTER 7 SportsStore: A Real Application In the previous chapters, I built quick and simple MVC applications. I described the MVC pattern, the essential C# features and the kinds of tools that good MVC developers require. Now it is time to put everything together and build a simple but realistic e- commerce application. My application, called SportsStore, will follow the classic approach taken by online stores everywhere. I will create an online product catalog that customers can browse by category and page, a shopping cart where users can add and remove products, and a checkout where customers can enter their shipping details. I will also create an administration area that includes create, read, update, and delete (CRUD) facilities for managing the catalog; and I will protect it so that only logged-in administrators can make changes. My goal in this chapter and those that follow is to give you a sense of what real MVC Framework development is like by creating as realistic an example as possible. I want to focus on the MVC Framework, of course, and so I have simplified the integration with external systems, such as the database, and omitted others entirely, such as payment processing. You might find the going a little slow as I build up the levels of infrastructure I need. Certainly, you would get the initial functionality built more quickly with Web Forms, just by dragging and dropping controls bound directly to a database. But the initial investment in an MVC application pays dividends, resulting in maintainable, extensible, well-structured code with excellent support for unit testing. UNIT TESTING I have made quite a big deal about the ease of unit testing in MVC, and about my belief that unit testing is an important part of the development process. You will see this demonstrated throughout this part of the book because I have included details of unit tests and techniques as they relate to key MVC features. I know this is not a universal opinion. If you do not want to unit test, that is fine with me. To that end, when I have something to say that is purely about testing, I put it in a sidebar like this one. If you are not interested in unit testing, you can skip right over these sections, and the SportsStore application will work just fine. You do not need to do any kind of unit testing to get the technology benefits of ASP.NET MVC, although, of course, support for testing is a key reason for adopting the MVC Framework. Most of the MVC features I use for the SportsStore application have their own chapters later in the book. Rather than duplicate everything here, I tell you just enough to make sense for the example application and point you to the other chapter for in-depth information. I will call out each step needed to build the application, so that you can see how the MVC features fit together. You should pay particular attention when I create views. You will get some odd results if you do not follow the examples closely. Getting Started You will need to install Visual Studio if you are planning to code the SportsStore application on your own computer as you read through this part of the book. You can also download the SportsStore project as part of the code archive that accompanies this book (available from Apress.com). You do not need to follow along, of course. I have tried to make the screenshots and code listings as easy to follow as possible, just in case you are reading this book on a train, in a coffee shop, or the like. Creating the Visual Studio Solution and Projects I am going to create a Visual Studio solution that contains three projects. One project will contain the domain model, one will be the MVC application, and the third will contain the unit tests. To get started, I created a new Visual Studio solution called 162 SportsStore using the Blank Solution template, which you can find in the Other Project Types/Visual Studio Solutions section of the New Project dialog, as shown in Figure 7-1. Click the OK button to create the solution. Figure 7-1. Creating a new Visual Studio solution A Visual Studio solution is a container for one or more projects. I require three projects for my example app, which I have described in Table 7-1. You add a project by right-clicking the Solution entry in the Solution Explorer and selecting Add New Project from the pop-up menus. Table 7-1. The Three SportsStore Projects Project Name Visual Studio Project Template Purpose SportsStore.Domain Class Library Holds the domain entities and logic; set up for persistence via a repository created with the Entity Framework. SportsStore.WebUI ASP.NET MVC Web Application (choose Empty when prompted to choose a project template and check the MVC option) Holds the controllers and views; acts as the UI for the SportsStore application. SportsStore.UnitTests Unit Test Project Holds the unit tests for the other two projects I always use the Empty option for the ASP.NET MVC Web Application template. The other options add an initial setup to the project that includes JavaScript libraries, CSS style sheets, and C# classes that configure application features like security and routing. None of this is inherently bad–and some of the open-source libraries that Microsoft has recently “blessed” to 163 be included in new projects are excellent–but you can manually set up all of the content and configuration and, in doing so, learn more about the workings of the MVC Framework. When you have created the three projects, the Solution Explorer should look like Figure 7-2. I have deleted the Class1.cs file that Visual Studio adds to the SportsStore.Domain project. I will not be using it. Figure 7-2. The projects shown in the Solution Explorer window To make debugging easier, right-click the SportsStore.WebUI project and select Set as Startup Project from the pop-up menu (you will see the name turn bold). This means that when you select Start Debugging or Start without Debugging from the Debug menu, it is this project that will be started. Visual Studio will try to navigate to individual view files if you are editing them when you start the debugger, so right-click the SportsStore.WebUI project in the Solution Explorer and select Properties from the pop-up menu. Click on Web to open the web-related properties and select the Specific Page option. There is no need to enter a value into the Specific Page text field. Just selecting the option is enough to stop Visual Studio from trying to guess the URL you want to view and ensure that the browser requests the root URL for the application when you start the debugger. Installing the Tool Packages I will be using Ninject and Moq in this chapter. Select Tools Library Package Manger Package Manager Console in Visual Studio to open the NuGet command line and enter the following commands: 164 Install-Package Ninject -version 3.0.1.10 -projectname SportsStore.WebUI Install-Package Ninject.Web.Common -version 3.0.0.7 -projectname SportsStore.WebUI Install-Package Ninject.MVC3 -Version 3.0.0.6 -projectname SportsStore.WebUI Install-Package Ninject -version 3.0.1.10 -projectname SportsStore.UnitTests Install-Package Ninject.Web.Common -version 3.0.0.7 -projectname SportsStore.UnitTests Install-Package Ninject.MVC3 -Version 3.0.0.6 -projectname SportsStore.UnitTests Install-Package Moq -version 4.1.1309.1617 -projectname SportsStore.WebUI Install-Package Moq -version 4.1.1309.1617 -projectname SportsStore.UnitTests Install-Package Microsoft.Aspnet.Mvc -version 5.0.0 -projectname SportsStore.Domain Install-Package Microsoft.Aspnet.Mvc -version 5.0.0 -projectname SportsStore.UnitTests There are many NuGet commands to enter because I am being selective about which packages NuGet installs into which projects and, as in previous chapters, I am specifying particular versions of the packages to download and install. Adding References Between Projects I need to set up dependencies between projects and to some of the Microsoft assemblies. Right-click each project in the Solution Explorer window, select Add Reference, and add the references shown in Table 7-2 from the Assemblies Framework, Assemblies Extensions or Solution sections. Table 7-2. Required Project Dependencies Project Name Solution Dependencies Assemblies References SportsStore.Domain None System.ComponentModel.DataAnnotations SportsStore.WebUI SportsStore.Domain None SportsStore.UnitTests SportsStore.Domain SportsStore.WebUI System.Web Microsoft.CSharp Caution Take the time to set these relationships up properly. If you do not have the right libraries and project references, you will get into trouble when trying to build the project. Setting Up the DI Container In Chapter 6, I showed you how to use Ninject to create a custom dependency resolver that the MVC Framework will use to instantiate objects across the application. I am going to repeat that process, starting with adding an Infrastructure folder to the SportsStore.WebUI project and adding a class file called NinjectDependencyResolver.cs to it. You can see the contents of the new file in Listing 7-1. Listing 7-1. The Contents of the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Web.Mvc; using Ninject; namespace SportsStore.WebUI.Infrastructure { 165 public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { // put bindings here } } } As you may recall from Chapter 6, the next step is to create a bridge between the NinjectDependencyResolver class and the MVC support for dependency injection in the App_Start/NinjectWebCommon.cs file, which one of the Ninject NuGet packages added to the project, as shown in Listing 7-2. Listing 7-2. Integrating Ninject in the NinjectWebCommon.cs File ... private static void RegisterServices(IKernel kernel) { System.Web.Mvc.DependencyResolver.SetResolver(new SportsStore.WebUI.Infrastructure.NinjectDependencyResolver(kernel)); } ... Running the Application If you select Start Debugging from the Debug menu, you will see an error page as shown in Figure 7-3. This is because you have requested a URL associated with a non-existent controller. 166 Figure 7-3. The error page Starting the Domain Model All MVC Framework projects start with the domain model because everything in an MVC Framework application revolves around it. Since this is an e-commerce application, the most obvious domain entity I need is a product. Create a new folder called Entities inside the SportsStore.Domain project and then a new C# class file called Product.cs within it. You can see the structure in Figure 7-4. 167 Figure 7-4. Creating the Product class You are already familiar with the definition of the Product class, as I am going to use the one you saw in the previous chapters. Edit the Product.cs class file so that it matches Listing 7-3. Listing 7-3. The Contents of the Product.cs File namespace SportsStore.Domain.Entities { public class Product { public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } } } I am following the technique of defining my domain model in a separate Visual Studio project, which means that the class must be marked as public. You do not need to follow this convention, but I find that it helps keep the model separate from the controllers, which is useful in large and complex projects. Creating an Abstract Repository I need some way of getting Product entities from a database. As I explained in Chapter 3, the model includes the persistence logic for storing and retrieving the data from the persistent data store, but even within the model, I want to keep a degree of separation between the data model entities and the storage and retrieval logic, which I achieve using the repository pattern. I will not worry about how I am going to implement data persistence for the moment, but I will start the process of defining an interface for it. Create a new top-level folder inside the SportsStore.Domain project called Abstract and, within the new folder, a new interface file called IProductsRepository.cs, the contents of which Listing 7-4 shows. You can add a new interface by right-clicking the Abstract folder, selecting Add New Item, and selecting the Interface template. Listing 7-4. The Contents of the IProductRepository.cs File using System.Collections.Generic; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IProductRepository { IEnumerable Products { get; } } } This interface uses IEnumerable to allow a caller to obtain a sequence of Product objects, without saying how or where the data is stored or retrieved. A class that depends on the IProductRepository interface can obtain Product objects without needing to know anything about where they are coming from or how the implementation class will deliver them. This is the essence of the repository pattern. I will revisit the IProductRepository interface throughout the development process to add features. Making a Mock Repository Now that I have defined an abstract interface, I could implement the persistence mechanism and hook it up to a database, but I want to add some of the other parts of the application first. In order to do this, I am going to create a mock implementation of the IProductRepository interface that will stand in until I return to the topic of data storage. 168 I define the mock implementation and bind it to the IProductRepository interface in the AddBindings method of the NinjectDependencyResolver class in the SportsStore.WebUI project, as illustrated by Listing 7-5. Listing 7-5. Adding the Mock IProductRepository Implementation in the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using Moq; using Ninject; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { Mock mock = new Mock (); mock.Setup(m => m.Products).Returns(new List { new Product { Name = "Football", Price = 25 }, new Product { Name = "Surf board", Price = 179 }, new Product { Name = "Running shoes", Price = 95 } }); kernel.Bind().ToConstant(mock.Object); } } } I had to add a number of namespaces to the file for this addition, but the process I used to create the mock repository implementation uses the same Moq techniques I introduced in Chapter 6. I want Ninject to return the same mock object whenever it gets a request for an implementation of the IProductRepository interface, which is why I used the ToConstant method to set the Ninject scope, like this: ... kernel.Bind().ToConstant(mock.Object) ; ... Rather than create a new instance of the implementation object each time, Ninject will always satisfy requests for the 169 IProductRepository interface with the same mock object. Displaying a List of Products I could spend the rest of this chapter building out the domain model and the repository, and not touch the UI project at all. I think you would find that boring, though, so I am going to switch tracks and start using the MVC Framework in earnest. I will add model and repository features as I need them. In this section, I am going to create a controller and an action method that can display details of the products in the repository. For the moment, this will be for only the data in the mock repository, but I will sort that out later. I will also set up an initial routing configuration so that MVC knows how to map requests for the application to the controller I create. Adding a Controller Right-click the Controllers folder in the SportsStore.WebUI project and select Add Controller from the pop-up menu. Select the MVC 5 Controller – Empty option, click the Add button and set the name to ProductController. Click the Add button and Visual Studio will create a new class file called ProductController.cs, which you should edit to match Listing 7-6. Listing 7-6. The Initial Contents of the Product Controller.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public ProductController(IProductRepository productRepository) { this.repository = productRepository; } } } In addition to removing the Index action method, I added a constructor that declares a dependency on the IProductRepository interface, which will lead Ninject to inject the dependency for the product repository when it instantiates the controller class. I also imported the SportsStore.Domain namespaces so that I can refer to the repository and model classes without having to qualify their names. Next, I have added an action method, called List, which will render a view showing the complete list of products, as shown in Listing 7-7. Listing 7-7. Adding an Action Method to the ProductController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; 170 namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public ProductController(IProductRepository productRepository) { this.repository = productRepository; } public ViewResult List() { return View(repository.Products); } } } Calling the View method like this (without specifying a view name) tells the framework to render the default view for the action method. Passing a List of Product objects to the View method, provides the framework with the data with which to populate the Model object in a strongly typed view. Adding the Layout, View Start File and View Now I need to add the default view for the List action method. Right-click on the List action method in the HomeController class and select Add View from the pop-up menu. Set View Name to List, set Template to Empty, and select Product for the Model Class, as shown in Figure 7-5. Ensure that the Use A Layout Page box is checked and click the Add button to create the view. 171 Figure 7-5. Adding the Views/Product/List.cshtml view When you click the Add button, Visual Studio will create the List.cshtml file, but it will also create a _ViewStart.cshtml file and a Shared/_Layout.cshtml file. This is a helpful feature, but in keeping with the Microsoft approach to default content, the _Layout.cshtml file contains template content that I do not want or need. Edit the layout so that it matches the content shown in Listing 7-8. Listing 7-8. Editing the _Layout.cshtml File @ViewBag.Title
@RenderBody()
Rendering the View Data Although I set the model type of the view to be the Product class, I actually want to work with an IEnumerable, which is what the Product controller obtains from the repository and passes to the view. In Listing 7-9, you can see that I have edited the @model expression and added some HTML and Razor expressions to display details of the products. Listing 7-9. Editing the List.cshtml File @using SportsStore.Domain.Entities @model IEnumerable @{ ViewBag.Title = "Products"; } @foreach (var p in Model) {

@p.Name

@p.Description

@p.Price.ToString("c")

} I also changed the title of the page. Notice that I do not need to use the Razor @: expression to display the view data. This is because each of the content lines in the code body either is a Razor directive or starts with an HTML element. Tip I converted the Price property to a string using the ToString("c") method, which renders numerical values as currency according to the culture settings that are in effect on your server. For example, if the server is set up as en-US, then (1002.3).ToString("c") will return $1,002.30, but if the server is set to en-GB, then the same method will return £1,002.30. You can change the culture setting for your server by adding a section to the node in the Web.config file like this: . 172 Setting the Default Route I need to tell the MVC Framework that it should send requests that arrive for the root URL of my application (http://mysite/) to the List action method in the ProductController class. I do this by editing the statement in the RegisterRoutes method in the App_Start/RouteConfig.cs file, as shown in Listing 7-10. Listing 7-10. Adding the Default Route in the RouteConfig.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SportsStore.WebUI { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional } ); } } } You can see the changes in bold. Change Home to Product and Index to List, as shown in the listing. I cover the ASP.NET routing feature in detail in Chapters 15 and 16. For now, it is enough to know that this change directs requests for the default URL to the List action method in the Product controller. Tip Notice that I have set the value of the controller in Listing 7-10 to be Product and not ProductController, which is the name of the class. This is part of the ASP.NET MVC naming scheme, in which controller classes always end in Controller but you omit this part of the name when referring to the class. Running the Application I have all the basics in place. I have a controller with an action method that the MVC Framework will call when the default URL is requested. That action method relies on a mock implementation of the repository interface, which generates some simple test data. The controller passes the test data to the view that I associated with the action method, and the view displays a simple list of the details for each product. You can see the result by running the application, as shown in Figure 7-6. If you don’t get the result in the figure, check that you have navigated to the root URL and not one that targets another action. 173 Figure 7-6. Viewing the basic application functionality This is the typical pattern of development for the ASP.NET MVC Framework. An initial investment of time setting everything up is necessary, and then the basic features of the application snap together quickly. EASIER DEBUGGING When you run the project from the Debug menu, Visual Studio will create a new browser window to display the application, which can take a few seconds. There are some tricks that you can use to speed the process up. If you are editing view files and not classes, then you can make changes in Visual Studio while the debugger is running. Reload the browser window when you want to see the effect of your changes. ASP.NET will recompile your views into classes and display the changes immediately. Visual Studio won’t let you edit class files when the debugger is running or make some kinds of change to the project in the Solution Explorer, so this technique is most useful when you are tweaking the fit and finish of the HTML that your application generates. Visual Studio 2013 includes a new feature called browser link that lets you have multiple browser windows open and to reload them from the Visual Studio menu bar. I demonstrate this feature in Chapter 14. As a final alternative, you can keep your application open in a stand-alone browser window. To do this (assuming you have launched the debugger at least once already), right-click the IIS Express icon in the system tray and select the URL for your app from the pop-up menu. After you have made your changes, compile the solution in Visual Studio by pressing F6 or choosing Build Build Solution, and then switch to your browser window and reload the Web page. Preparing a Database I can already display simple views that contain details of the products, but I am displaying the test data that the mock IproductRepository returns. Before I can implement a real repository, I need to set up a database and populate it with some data. I am going to use SQL Server as the database, and I will access the database using the Entity Framework (EF), which is the Microsoft.NET ORM framework. An ORM framework presents the tables, columns, and rows of a relational database through regular C# objects. I mentioned in Chapter 6 that LINQ can work with different sources of data, and one of these is the Entity Framework. You will see how this simplifies things in a little while. 174 Note This is an area where you can choose from a wide range of tools and technologies. Not only are there different relational databases available, but you can also work with object repositories, document stores, and some esoteric alternatives. There are other .NET ORM frameworks as well, each of which takes a slightly different approach: variations that may give you a better fit for your projects. I am using the Entity Framework for a several reasons: it is simple and easy to get it up and working; the integration with LINQ is first rate (and I like using LINQ); and it is good. The earlier releases were a bit hit-and-miss, but the current versions are elegant and feature-rich. Creating the Database A nice feature of Visual Studio and SQL Server is the LocalDB feature, which is an administration-free implementation of the core SQL Server features specifically designed for developers. Using this feature, I can skip the process of setting up a database while I build my project and then deploy to a full SQL Server instance. Most MVC applications are deployed to hosted environments that are run by professional administrators and so the LocalDB feature means that database configuration can be left in the hands of DBAs and developers can get on with coding. The LocalDB feature is installed automatically with Visual Studio Express 2013 for Web, but you can download it directly from www.microsoft.com/sqlserver if you prefer. The first step is to create the database connection in Visual Studio. Open the Server Explorer window from the View menu and click the Connect to Database button (it looks like a power cable with a green plus sign). You will see the Choose Data Source dialog. Select the Microsoft SQL Server option, as shown in Figure 7-7, and click the Continue button. (Visual Studio remembers the selection you make, so you will not see this window if you already created a database connection in another project). Figure 7-7. Selecting the Data Source Next, you will see the Add Connection dialog. Set the server name to (localdb)\v11.0. This is a special name that indicates that you want to use the LocalDB feature. Check the Use Windows Authentication option and set the database name to SportsStore, as shown by Figure 7-8. 175 Figure 7-8. Setting up the SportsStore database Tip If you did not see the Choose Data Source dialog, you can click the Change button in the top right of the Add Connection dialog. Click the OK button and Visual Studio will prompt you to create the new database: click the Yes to go ahead. A new item will appear in the Data Connections section of the Server Explorer window, which you can expand to see the different facets of the database, as shown in Figure 7-9. You should see something similar, but the name of the database connection will be different because it will include the local PC name (the name of my workstation is tiny). 176 Figure 7-9. The LocalDB database as shown in the Server Explorer window Defining the Database Schema As I explained at the start of the chapter, my focus with the SportsStore application is to focus on the MVC Framework development process, and that means keeping the other components that the application relies on as simple as possible. I do not want to get into the topics of database design and the in-depth details of the Entity Framework, beyond what I need to demonstrate how to get data in and out of an application. These are topics in their own right and they are not part of ASP.NET or the MVC Framework. With this in mind, I am going to use a database that contains only one table. This is not how real e-commerce sites would structure their data, of course, but the important lesson in this section is about the repository pattern and how I use it to store and retrieve data, not the structure of the database. To create the database table, right-click the Tables item for the new SportsStore database in the Server Explorer window and select Add New Table, as shown in Figure 7-10. Figure 7-10. Adding a new table 177 Visual Studio will display a designer window for creating a new table. You can create new database tables using the visual part of the designer, but I am going to use the T-SQL section because it is a more concise and accurate way of describing the table specification I require in a book. Enter the SQL statement shown in Listing 7-11 and click the Update button in the top-left corner of the table design window. Listing 7-11. The SQL Statement to Create the Table in the SportsStore Database CREATE TABLE Products ( [ProductID] INT NOT NULL PRIMARY KEY IDENTITY, [Name] NVARCHAR(100) NOT NULL, [Description] NVARCHAR(500) NOT NULL, [Category] NVARCHAR(50) NOT NULL, [Price] DECIMAL(16, 2) NOT NULL ) This statement creates a table called Products, which has columns for the different properties I defined in the Product model class earlier in the chapter. Tip Setting the IDENTITY property for the ProductID column means that SQL Server will generate a unique primary key value when I add data to this table. When using a database in a Web application, it can be difficult to generate unique primary keys because requests from users arrive concurrently. By enabling this feature, I can store new table rows and rely on SQL Server to sort out unique values. When you click the Update button, Visual Studio will show a summary of the effect of the statement, as shown in Figure 7- 11. Figure 7-11. The summary of the effect of the SQL statement Click the Update Database button to execute the SQL and create the Products table in the database. You will be able to see the effect the update has if you click the Refresh button in the Server Explorer window. The Tables section shows the new Product table and details of each of the rows. Tip After you have updated the database, you can close the dbo.Products window. Visual Studio will offer you the chance to save the SQL script used to create the database. You do not need to save the script for this chapter, but it can be useful in real projects if you need to configure multiple databases. 178 Adding Data to the Database I am going to add data to the database so that I have something to work with until I add the catalog administration features in Chapter 11. In the Server Explorer window, expand the Tables item of the SportsStore database, right-click the Products table, and select Show Table Data. Enter the data shown in Figure 7-12. You can move from row to row by using the Tab key. At the end of each row, pressing tab will move to the next row and update the data in the database. Figure 7-12. Adding data to the Products table Note You must leave the ProductID column empty. It is an identity column, so SQL Server will generate a unique value when you tab to the next row. I listed the product details in Table 7-3 in case you cannot make out the detail from the figure. It doesn’t matter if you don’t enter all of the details exactly as I have, although you’ll see different results from the ones I show as you work through the process of creating the rest of the SportsStore application. Table 7-3. The Data for the Products Table 179 Creating the Entity Framework Context Recent versions of the Entity Framework include a nice feature called code-first. The idea is that I can define classes in my model and then generate a database from those classes. This is great for green-field development projects, but these are few and far between. Instead, I am going to show you a variation on code-first, where I associate the model classes with an existing database. Select Tools Library Package Manager Package Manager Console in Visual Studio to open the NuGet command line and enter the following command: Install-Package EntityFramework -projectname SportsStore.Domain Install-Package EntityFramework -projectname SportsStore.WebUI Tip You may see errors in the Package Manager Console telling you that binding redirects cannot be created. You can safely ignore these warnings. This command adds the Entity Framework package to the solution. I need to install the same package in the Domain and WebUI projects so that I create the classes that will access the database in the Domain project and access the database in the WebUI project. The next step is to create a context class that will associate the model with the database. Create a new folder in the SportsStore.Domain project called Concrete and add a new class file called EFDbContext.cs within it. Edit the contents of the class file so they match Listing 7-12. Listing 7-12. The Content of the EFDbContext.cs File using SportsStore.Domain.Entities; using System.Data.Entity; namespace SportsStore.Domain.Concrete { public class EFDbContext : DbContext { public DbSet Products { get; set; } } } To take advantage of the code-first feature, I need to create a class that is derived from System.Data.Entity.DbContext. This class then automatically defines a property for each table in the database that I want to work with. The name of the property specifies the table, and the type parameter of the DbSet result specifies the model type that the 180 Entity Framework should use to represent rows in that table. In this case, the property name is Products and the type parameter is Product, meaning that the Entity Framework should use the Product model type to represent rows in the Products table. Next, I need to tell the Entity Framework how to connect to the database, which I do by adding a database connection string to the Web.config file in the SportsStore.WebUI project with the same name as the context class, as shown in Listing 7-13. Listing 7-13. Adding a Database Connection in the Web.config File Tip Notice that I have switched project here. I define the model and the repository logic in the SportsStore.Domain project, but the database connection information is put in the Web.config file in the SportsStore.WebUI project. Caution I have had to split the value of the connectionString attribute across multiple lines to fit it on the page, but it is important to put everything on a single line in the Web.config file. There will be another add element in the connectionsStrings section of the Web.config file. Visual Studio creates this element by default and you can either ignore it or, as I have, delete it from the Web.config file. Creating the Product Repository All that remains is to add a class file to the Concrete folder of the SportsStore.Domain project called EFProductRepository.cs. Edit your class file so it matches Listing 7-14. Listing 7-14. The Contents of the EFProductRepostory.cs File using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using System.Collections.Generic; namespace SportsStore.Domain.Concrete { public class EFProductRepository : IProductRepository { 181 private EFDbContext context = new EFDbContext(); public IEnumerable Products { get { return context.Products; } } } } This is the repository class. It implements the IProductRepository interface and uses an instance of EFDbContext to retrieve data from the database using the Entity Framework. You will see how I work with the Entity Framework (and how simple it is) as I add features to the repository. To use the new repository class, I need to edit the Ninject bindings and replace the mock repository with a binding for the real one. Edit the NinjectDependencyResolver.cs class file in the SportsStore.WebUI project so that the AddBindings method looks like Listing 7-15. Listing 7-15. Adding the Real Repository Binding in the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using Moq; using Ninject; using SportsStore.Domain.Abstract; using SportsStore.Domain.Concrete; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind().To(); } } } The new binding is in bold. It tells Ninject to create instances of the EFProductRepository class to service requests for the IProductRepository interface. All that remains now is to run the application again. Figure 7-13 shows the results, which demonstrate the application is getting its product data from the database, rather than the mock repository. 182 Figure 7-13. The result of implementing the real repository Tip If you get a System.ArgumentException when you start the project, then you have split the details of the database connection over two lines in the Web.config file. See the previous section for details. This approach to getting the Entity Framework to present a SQL Server database as a series of model objects is simple and easy to work with, and it allows me to keep my focus on the MVC Framework. Of course, I am skipping over a lot of the detail in how the Entity Framework operates and the huge number of configuration options that are available. I like the Entity Framework a lot and I recommend that you spend some time getting to know it in detail. A good place to start is the Microsoft site for the Entity Framework: http://msdn.microsoft.com/data/ef. Adding Pagination You can see from Figure 7-13 that the List.cshtml view displays all of the products in the database on a single page. In this section, I will add support for pagination so that the view displays a number of products on a page, and the user can move from page to page to view the overall catalog. To do this, I am going to add a parameter to the List method in the Product controller, as shown in Listing 7-16. Listing 7-16. Adding Pagination Support to the List Action Method in the ProductController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Controllers { 183 public class ProductController : Controller { private IProductRepository repository; public int PageSize = 4; public ProductController(IProductRepository productRepository) { this.repository = productRepository; } public ViewResult List(int page = 1) { return View(repository.Products .OrderBy(p => p.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize)); } } } The PageSize field specifies that I want four products per page. I will come back and replace this with a better mechanism later on. I have added an optional parameter to the List method. This means that if I call the method without a parameter (List()), my call is treated as though I had supplied the value specified in the parameter definition (List(1)). The effect is that the action method displays the first page of products when the MVC Framework invokes it without an argument. Within the body of the action method, I get the Product objects, order them by the primary key, skip over the products that occur before the start of the current page, and take the number of products specified by the PageSize field. UNIT TEST: PAGINATION I can unit test the pagination feature by creating a mock repository, injecting it into the constructor of the ProductController class, and then calling the List method to request a specific page. I can then compare the Product objects I get with what I would expect from the test data in the mock implementation. See Chapter 6 for details of how to set up unit tests. Here is the unit test I created for this purpose, in the UnitTest1.cs file of the SportsStore.UnitTests project: using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Controllers; namespace SportsStore.UnitTests { [TestClass] public class UnitTest1 { [TestMethod] public void Can_Paginate() { // Arrange Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, new Product {ProductID = 4, Name = "P4"}, new Product {ProductID = 5, Name = "P5"} }); ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Act 184 IEnumerable result = (IEnumerable)controller.List(2).Model; // Assert Product[] prodArray = result.ToArray(); Assert.IsTrue(prodArray.Length == 2); Assert.AreEqual(prodArray[0].Name, "P4"); Assert.AreEqual(prodArray[1].Name, "P5"); } } } Notice how easy it is to get the data that returned from a controller method. I call the Model property on the result to get the IEnumerable sequence generated in the List method. I then check that the data is what I expect. In this case, I converted the sequence to an array using the LINQ ToArray extension method and checked the length and the values of the individual objects. Displaying Page Links If you run the application, you will see that there are only four items shown on the page. If you want to view another page, you can append query string parameters to the end of the URL, like this: http://localhost:51280/?page=2 You will need to change the port part of the URL to match whatever port your ASP.NET development server is running on. Using these query strings, you can navigate through the catalog of products. Of course, there is no way for customers to figure out that these query string parameters exist, and even if there were, they are not going to want to navigate this way. Instead, I need to render some page links at the bottom of the each list of products so that customers can navigate between pages. To do this, I am going to implement a reusable HTML helper method, similar to the Html.TextBoxFor and Html.BeginForm methods I used in Chapter 2. The helper will generate the HTML markup for the navigation links I require. Adding the View Model To support the HTML helper, I am going to pass information to the view about the number of pages available, the current page, and the total number of products in the repository. The easiest way to do this is to create a view model, which I introduced briefly in Chapter 3. Add the class shown in Listing 7-17, called PagingInfo, to the Models folder in the SportsStore.WebUI project. Listing 7-17. The Contents of the PagingInfo.cs File using System; namespace SportsStore.WebUI.Models { public class PagingInfo { public int TotalItems { get; set; } public int ItemsPerPage { get; set; } public int CurrentPage { get; set; } public int TotalPages { get { return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); } } } } 185 A view model is not part of the domain model. It is just a convenient class for passing data between the controller and the view. To emphasize this, I put this class in the SportsStore.WebUI project to keep it separate from the domain model classes. Adding the HTML Helper Method Now that I have a view model, I can implement the HTML helper method, which I am going to call PageLinks. Create a new folder in the SportsStore.WebUI project called HtmlHelpers and add a new class file called PagingHelpers.cs, the contents of which Listing 7-18 shows. Listing 7-18. The Contents of the PagingHelpers.cs Class File using System; using System.Text; using System.Web.Mvc; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.HtmlHelpers { public static class PagingHelpers { public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfo pagingInfo, Func pageUrl) { StringBuilder result = new StringBuilder(); for (int i = 1; i <= pagingInfo.TotalPages; i++) { TagBuilder tag = new TagBuilder("a"); tag.MergeAttribute("href", pageUrl(i)); tag.InnerHtml = i.ToString(); if (i == pagingInfo.CurrentPage) { tag.AddCssClass("selected"); tag.AddCssClass("btn-primary"); } tag.AddCssClass("btn btn-default"); result.Append(tag.ToString()); } return MvcHtmlString.Create(result.ToString()); } } } The PageLinks extension method generates the HTML for a set of page links using the information provided in a PagingInfo object. The Func parameter accepts a delegate that it uses to generate the links to view other pages. UNIT TEST: CREATING PAGE LINKS To test the PageLinks helper method, I call the method with test data and compare the results to the expected HTML. The unit test method is as follows: using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; 186 using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Controllers; using SportsStore.WebUI.Models; using SportsStore.WebUI.HtmlHelpers; namespace SportsStore.UnitTests { [TestClass] public class UnitTest1 { [TestMethod] public void Can_Paginate() { // ...statements removed for brevity... } [TestMethod] public void Can_Generate_Page_Links() { // Arrange - define an HTML helper - we need to do this // in order to apply the extension method HtmlHelper myHelper = null; // Arrange - create PagingInfo data PagingInfo pagingInfo = new PagingInfo { CurrentPage = 2, TotalItems = 28, ItemsPerPage = 10 }; // Arrange - set up the delegate using a lambda expression Func pageUrlDelegate = i => "Page" + i; // Act MvcHtmlString result = myHelper.PageLinks(pagingInfo, pageUrlDelegate); // Assert Assert.AreEqual(@"1" + @"2" + @"3", result.ToString()); } } } This test verifies the helper method output by using a literal string value that contains double quotes. C# is perfectly capable of working with such strings, as long as the string is prefixed with @ and use two sets of double quotes ("") in place of one set of double quotes. You must remember not to break the literal string into separate lines, unless the string you are comparing to is similarly broken. For example, the literal I use in the test method has wrapped onto two lines because the width of a printed page is narrow. I have not added a newline character; if I did, the test would fail. An extension method is available for use only when the namespace that contains it is in scope. In a code file, this is done with a using statement; but for a Razor view, you must add a configuration entry to the Web.config file, or add a @using statement to the view itself. There are, confusingly, two Web.config files in a Razor MVC project: the main one, which resides in the root directory of the application project, and the view-specific one, which is in the Views folder. The change I want to make is to the Views/web.config file as shown in Listing 7-19. Listing 7-19. Adding the HTML Helper Method Namespace to the Views/web.config File 187 ... ... Every namespace that I refer to in a Razor view needs to be used explicitly, declared in the web.config file or applied with a @using expression. Adding the View Model Data I am not quite ready to use the HTML helper method. I have yet to provide an instance of the PagingInfo view model class to the view. I could do this using the view bag feature, but I would rather wrap all of the data I am going to send from the controller to the view in a single view model class. To do this, I added a class file called ProductsListViewModel.cs to the Models folder of the SportsStore.WebUI project. Listing 7-20 shows the contents of the new file. Listing 7-20. The Contents of the ProductsListViewModel.cs File using System.Collections.Generic; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Models { public class ProductsListViewModel { public IEnumerable Products { get; set; } public PagingInfo PagingInfo { get; set; } } } I can update the List action method in the ProductController class to use the ProductsListViewModel class to provide the view with details of the products to display on the page and details of the pagination, as shown in Listing 7-21. Listing 7-21. Updating the List Method in the ProductController.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; 188 using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public int PageSize = 4; public ProductController(IProductRepository productRepository) { this.repository = productRepository; } public ViewResult List(int page = 1) { ProductsListViewModel model = new ProductsListViewModel { Products = repository.Products .OrderBy(p => p.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize), PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize, TotalItems = repository.Products.Count() } }; return View(model); } } } These changes pass a ProductsListViewModel object as the model data to the view. UNIT TEST: PAGE MODEL VIEW DATA I need to ensure that the controller sends the correct pagination data to the view. Here is the unit test I added to the test project to test this: ... [TestMethod] public void Can_Send_Pagination_View_Model() { // Arrange Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, new Product {ProductID = 4, Name = "P4"}, new Product {ProductID = 5, Name = "P5"} }); // Arrange ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Act ProductsListViewModel result = 189 (ProductsListViewModel)controller.List(2).Model; // Assert PagingInfo pageInfo = result.PagingInfo; Assert.AreEqual(pageInfo.CurrentPage, 2); Assert.AreEqual(pageInfo.ItemsPerPage, 3); Assert.AreEqual(pageInfo.TotalItems, 5); Assert.AreEqual(pageInfo.TotalPages, 2); } ... I also need to modify the earlier pagination unit test, contained in the Can_Paginate method. It relies on the List action method returning a ViewResult whose Model property is a sequence of Product objects, but I have wrapped that data inside another view model type. Here is the revised test: ... [TestMethod] public void Can_Paginate() { // Arrange Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, new Product {ProductID = 4, Name = "P4"}, new Product {ProductID = 5, Name = "P5"} }); ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Act ProductsListViewModel result = (ProductsListViewModel)controller.List(2).Model; // Assert Product[] prodArray = result. Products .ToArray(); Assert.IsTrue(prodArray.Length == 2); Assert.AreEqual(prodArray[0].Name, "P4"); Assert.AreEqual(prodArray[1].Name, "P5"); } ... I would usually create a common setup method, given the degree of duplication between these two test methods. However, since I am delivering the unit tests in individual sidebars like this one, I am going to keep everything separate so you can see each test on its own. The view is currently expecting a sequence of Product objects, so I need to update the List.cshtml file, as shown in Listing 7-22, to deal with the new view model type. Listing 7-22. Updating the List.cshtml File @model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Products"; } 190 @foreach (var p in Model.Products) {

@p.Name

@p.Description

@p.Price.ToString("c")

} I have changed the @model directive to tell Razor that I am now working with a different data type. I updated the foreach loop so that the data source is the Products property of the model data. Displaying the Page Links I have everything in place to add the page links to the List view. I created the view model that contains the paging information, updated the controller so that it passes this information to the view, and changed the @model directive to match the new model view type. All that remains is to call the HTML helper method from the view, which you can see in Listing 7-23. Listing 7-23. Calling the HTML Helper Method in the List.cshtml File @model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Products"; } @foreach (var p in Model.Products) {

@p.Name

@p.Description

@p.Price.ToString("c")

}
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }))
If you run the application, you will see the new page links, as illustrated in Figure 7-14. The style is still basic, which I will fix later in the chapter. What is important for the moment is that the links take the user from page to page in the catalog and let him explore the products for sale. 191 Figure 7-14. Displaying page navigation links WHY NOT JUST USE A GRIDVIEW? If you have worked with ASP.NET before, you might think that was a lot of work for an unimpressive result. It has taken me pages and pages just to get a page list. If I were using Web Forms, I could have done the same thing using the ASP.NET Web Forms GridView or ListView controls, right out of the box, by hooking it up directly to the Products database table. What I have accomplished in this chapter may not look like much, but it is different from dragging a control onto a design surface. First, I am building an application with a sound and maintainable architecture that involves proper separation of concerns. Unlike the simplest use of the ListView control, I have not directly coupled the UI and the database: an approach that gives quick results but that causes pain and misery over time. Second, I have been creating unit tests as I go, and these allow me to validate the behavior of the application in a natural way that is nearly impossible with a complex Web Forms control. Finally, bear in mind that I have given over a lot of this chapter to creating the underlying infrastructure on which I am building the application. I need to define and implement the repository only once, for example, and now that I have, I will be able to build and test new features quickly and easily, as the following chapters will demonstrate. None of this detracts from the immediate results that Web Forms can deliver, of course, but as I explained in Chapter 3, that immediacy comes with a cost that can be expensive and painful in large and complex projects. Improving the URLs 192 I have the page links working, but they still use the query string to pass page information to the server, like this: http://localhost/?page=2 I create URLs that are more appealing by creating a scheme that follows the pattern of composable URLs. A composable URL is one that makes sense to the user, like this one: http://localhost/Page2 MVC makes it easy to change the URL scheme in application because it uses the ASP.NET routing feature. All I need do is add a new route to the RegisterRoutes method in the RouteConfig.cs file, which you will find in the App_Start folder of the SportsStore.WebUI project. You can see the change I made to this file in Listing 7-24. Listing 7-24. Adding a New Route to the RouteConfig.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SportsStore.WebUI { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: null, url: "Page{page}", defaults: new { Controller = "Product", action = "List" } ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Product", action = "List", id = UrlParameter.Optional } ); } } } It is important that you add this route before the Default one that is already in the file. As you will see in Chapter 15, the routing system processes routes in the order they are listed, and I need the new route to take precedence over the existing one. This is the only alteration required to change the URL scheme for product pagination. The MVC Framework and the routing function are tightly integrated, and so the application automatically reflects a change like this in the result produced by the Url.Action method (which is what I used in the List.cshtml view to generate the page links). Do not worry if routing does not make sense to you now. I explain it in detail in Chapters 15 and 16. If you run the application and navigate to a page, you will see the new URL scheme in action, as illustrated in Figure 7-15. 193 Figure 7-15. The new URL scheme displayed in the browser Styling the Content I have built a great deal of infrastructure and the application is really starting to come together, but I have not paid any attention to its appearance. Even though this book is not about design or CSS, the SportsStore application design is so miserably plain that it undermines its technical strengths. In this section, I will put some of that right. I am going to implement a classic two-column layout with a header, as shown in Figure 7-16. Figure 7-16. The design goal for the SportsStore application Installing the Bootstrap Package I am going to use the Bootstrap package to provide the CSS styles I will apply to the application. To install the Bootstrap package, select Library Package Manager Package Manager Console from the Visual Studio Tools menu. Visual Studio will open the NuGet command line. Enter the following command and hit return: Install-Package -version 3.0.0 bootstrap –projectname SportsStore.WebUI This is the same basic NuGet command I used in Chapter 2, with the addition of the projectname argument to ensure NuGet adds the files to the right project. Note Once again, I am going to use Bootstrap without going into the details of the features that the package provides. For full details of Bootstrap and the other client-side libraries that Microsoft has blessed for use with the MVC Framework, see my Pro ASP.NET MVC 5 Client book, published by Apress in 2014. 194 Applying Bootstrap Styles to the Layout In Chapter 5, I explained how Razor layouts work and how you apply them. When I created the List.cshtml view for the Product controller, I asked you to check the option to use a layout, but leave the box that specifies a layout blank. This has the effect of using the layout specified by the Views/_ViewStart.cshtml file, which Visual Studio created automatically along the view. You can see the contents of the view start file in Listing 7-25. Listing 7-25. The Contents of the _ViewStart.cshtml File @{ Layout = "∼/Views/Shared/_Layout.cshtml"; } The value of the Layout property specifies that views will use the Views/Shared/_Layout.cshtml file as a layout, unless they explicitly specify an alternative. I reset the content of the _Layout.cshtml file earlier in the chapter to remove the template content that Visual Studio adds and in Listing 7-26 you can see how I have returned to this file to add the Bootstrap CSS file and apply some of the CSS styles it defines. Listing 7-26. Applying Bootstrap CSS to the _Layout.cshtml File @ViewBag.Title
Put something useful here later
@RenderBody()
I have added the bootstrap.css and bootstrap-theme.css files to the layout using link elements and applied various Bootstrap classes to create a simple layout. I also need to change the List.cshtml file, as shown in Listing 7-27. Listing 7-27. Applying Bootstrap to Style the List.cshtml File @model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Products"; 195 } @foreach (var p in Model.Products) {

@p.Name @p.Price.ToString("c")

@p.Description
}
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x }))
THE PROBLEM WITH STYLING ELEMENTS The HTML elements generated by an MVC application come from a variety of sources (static content, Razor expressions, HTML helper methods, etc.), so the style classes become diffused throughout the project. If this makes you feel slightly uncomfortable, then you are not alone. Mixing the CSS styles in with element generation is not a great idea and runs counter to the idea of separating out unrelated functionality that pervades MVC. You can improve on this situation by assigning non- Bootstrap classes to elements based on their role in the application and then use a library like jQuery or LESS to map between your custom classes and the Bootstrap ones. I am going to keep things simple for this application and accept that I have embedded the Bootstrap classes throughout the application, even though it complicates the process of changing styles in the future. I would not do this in a real project, but I know this example application is not going to enter into a maintenance phase. If you run the application, you will see that I have improved the appearance—at least a little, anyway—as illustrated by Figure 7- 17. 196 Figure 7-17. The design-enhanced SportsStore application Creating a Partial View As a finishing trick for this chapter, I am going to refactor the application to simplify the List.cshtml view. I am going to create a partial view, which is a fragment of content that you can embed into another view, rather like a template. Partial views are contained within their own files and are reusable across multiple views, which can help reduce duplication if you need to render the same kind of data in several places in your application. To add the partial view, right-click the /Views/Shared folder in the SportsStore.WebUI project and select Add View from the pop-up menu. Set View Name to ProductSummary, set Template to Empty, select Product from the Model Class drop-down list and check the Create As A Partial View box, as shown in Figure 7-18. 197 Figure 7-18. Creating a partial view Click the Add button, and Visual Studio will create a partial view file called Views/Shared/ProductSummary.cshtml. A partial view is similar to a regular view, except that it produces a fragment of HTML, rather than a full HTML document. If you open the ProductSummary view, you will see that it contains only the model view directive, which is set to the Product domain model class. Apply the changes shown in Listing 7-28. Listing 7-28. Adding Markup to the ProductSummary.cshtml File @model SportsStore.Domain.Entities.Product

@Model.Name @Model.Price.ToString("c")

@Model.Description
Now I need to update Views/Products/List.cshtml so that it uses the partial view. You can see the change in Listing 7-29. 198 Listing 7-29. Using a Partial View in the List.cshtml File @model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Products"; } @foreach (var p in Model.Products) { @Html.Partial("ProductSummary", p) }
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x}))
I have taken the markup that was previously in the foreach loop in the List.cshtml view and moved it to the new partial view. I call the partial view using the Html.Partial helper method. The parameters are the name of the view and the view model object. Switching to a partial view like this is good practice, but it does not change the appearance of the application, as Figure 7-19 shows. Figure 7-19. Applying a partial view 199 Summary In this chapter, I built most of the core infrastructure for the SportsStore application. It does not have many features that you could demonstrate to a client at this point, but behind the scenes, there are the beginnings of a domain model with a product repository backed by SQL Server and the Entity Framework. There is a single controller, ProductController, that can produce paginated lists of products, and I have set up DI and defined a clean and friendly URL scheme. If this chapter felt like a lot of setup for little benefit, then the next chapter will balance the equation. Now that the fundamental structure is in place, we can forge ahead and add all of the customer-facing features: navigation by category, a shopping cart, and a checkout process. 200 CHAPTER 8 SportsStore: Navigation In the previous chapter, I set up the core infrastructure of the SportsStore application. Now I will use that infrastructure to add features to the application and you will start to see how the investment in the basic plumbing pays off. I will be able to add important customer-facing features simply and easily and, along the way, you will see some additional functionality that the MVC Framework provides. Adding Navigation Controls The SportsStore application will be more usable if customers can navigate products by category. I will do this in three phases: Enhance the List action model in the ProductController class so that it is able to filter the Product objects in the repository. Revisit and enhance the URL scheme and revise the routing strategy. Create a category list that will go into the sidebar of the site, highlighting the current category and linking to others. Filtering the Product List I am going to start by enhancing the view model class, ProductsListViewModel, which I added to the SportsStore.WebUI project in the last chapter. I need to communicate the current category to the view in order to render the sidebar, and this is as good a place to start as any. Listing 8-1 shows the changes I made to the ProductsListView.cs file. Listing 8-1. Enhancing the ProductsListView .cs File using System.Collections.Generic; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Models { public class ProductsListViewModel { public IEnumerable Products { get; set; } public PagingInfo PagingInfo { get; set; } public string CurrentCategory { get; set; } } } I added a property called CurrentCategory. The next step is to update the Product controller so that the List action method will filter Product objects by category and use the new property I added to the view model to indicate which category has been selected. The changes are shown in Listing 8-2. Listing 8-2. Adding Category Support to the List Action Method in the ProductController.cs File using System; 201 using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class ProductController : Controller { private IProductRepository repository; public int PageSize = 4; public ProductController(IProductRepository productRepository) { this.repository = productRepository; } public ViewResult List(string category , int page = 1) { ProductsListViewModel model = new ProductsListViewModel { Products = repository.Products .Where(p => category == null || p.Category == category) .OrderBy(p => p.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize), PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize, TotalItems = repository.Products.Count() }, CurrentCategory = category }; return View(model); } } } I made three changes to the action method. First, I added a parameter called category. This category parameter is used by the second change in the listing, which is an enhancement to the LINQ query. If category is not null, only those Product objects with a matching Category property are selected. The last change is to set the value of the CurrentCategory property I added to the ProductsListViewModel class. However, these changes mean that the value of PagingInfo.TotalItems is incorrectly calculated. I will fix this in a while. UNIT TEST: UPDATING EXISTING UNIT TESTS I changed the signature of the List action method, which will prevent some of the existing unit test methods from compiling. To address this, I need to pass null as the first parameter to the List method in those unit tests that work with the controller. For example, in the Can_Paginate test, the action section of the unit test becomes as follows: ... [TestMethod] public void Can_Paginate() { // Arrange 202 Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, new Product {ProductID = 4, Name = "P4"}, new Product {ProductID = 5, Name = "P5"} }); // create a controller and make the page size 3 items ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Act ProductsListViewModel result = (ProductsListViewModel)controller.List(null, 2).Model; // Assert Product[] prodArray = result.Products.ToArray(); Assert.IsTrue(prodArray.Length == 2); Assert.AreEqual(prodArray[0].Name, "P4"); Assert.AreEqual(prodArray[1].Name, "P5"); } ... By using null, I receive all of the Product objects that the controller gets from the repository, which is the same situation I had before adding the new parameter. I need to make the same kind of change to the Can_Send_Pagination_View_Model test as well: ... [TestMethod] public void Can_Send_Pagination_View_Model() { // Arrange Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, new Product {ProductID = 4, Name = "P4"}, new Product {ProductID = 5, Name = "P5"} }); // Arrange ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Act ProductsListViewModel result = (ProductsListViewModel)controller.List(null, 2).Model; // Assert PagingInfo pageInfo = result.PagingInfo; Assert.AreEqual(pageInfo.CurrentPage, 2); Assert.AreEqual(pageInfo.ItemsPerPage, 3); Assert.AreEqual(pageInfo.TotalItems, 5); 203 Assert.AreEqual(pageInfo.TotalPages, 2); } ... Keeping your unit tests synchronized with your code changes quickly becomes second nature when you get into the testing mind-set. The effect of the category filtering is evident, even with these small changes. Start the application and select a category using the follow query string, changing the port to match the one that Visual Studio assigned for your project: http://localhost:51280/?category=Soccer You will see only the products in the Soccer category, as shown in Figure 8-1. Figure 8-1. Using the query string to filter by category Obviously, users won’t want to navigate to categories using URLs, but you can see how small changes can have a big impact in an MVC Framework application once the basic structure is in place. UNIT TEST: CATEGORY FILTERING I need a unit test to properly test the category filtering function, to ensure that the filter can correctly generate products in a specified category. Here is the test: ... 204 [TestMethod] public void Can_Filter_Products() { // Arrange // - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Cat1"}, new Product {ProductID = 2, Name = "P2", Category = "Cat2"}, new Product {ProductID = 3, Name = "P3", Category = "Cat1"}, new Product {ProductID = 4, Name = "P4", Category = "Cat2"}, new Product {ProductID = 5, Name = "P5", Category = "Cat3"} }); // Arrange - create a controller and make the page size 3 items ProductController controller = new ProductController(mock.Object); controller.PageSize = 3; // Action Product[] result = ((ProductsListViewModel)controller.List("Cat2", 1).Model) .Products.ToArray(); // Assert Assert.AreEqual(result.Length, 2); Assert.IsTrue(result[0].Name == "P2" && result[0].Category == "Cat2"); Assert.IsTrue(result[1].Name == "P4" && result[1].Category == "Cat2"); } ... This test creates a mock repository containing Product objects that belong to a range of categories. One specific category is requested using the Action method, and the results are checked to ensure that the results are the right objects in the right order. Refining the URL Scheme No one wants to see or use ugly URLs such as /?category=Soccer. To address this, I am going to revisit the routing scheme to create an approach to URLs that better suits me and my customers. To implement the new scheme, I changed the RegisterRoutes method in the App_Start/RouteConfig.cs file, as shown in Listing 8-3. Listing 8-3. The New URL Scheme in the RouteConfig.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SportsStore.WebUI { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(null, 205 "", new { controller = "Product", action = "List", category = (string)null, page = 1 } ); routes.MapRoute(null, "Page{page}", new { controller = "Product", action = "List", category = (string)null }, new { page = @"\d+" } ); routes.MapRoute(null, "{category}", new { controller = "Product", action = "List", page = 1 } ); routes.MapRoute(null, "{category}/Page{page}", new { controller = "Product", action = "List" }, new { page = @"\d+" } ); routes.MapRoute(null, "{controller}/{action}"); } } } Caution It is important to add the new routes in Listing 8-3 in the order they are shown. Routes are applied in the order in which they are defined, and you will get some odd effects if you change the order. Table 8-1 describes the URL scheme that these routes represent. I explain the routing system in detail in Chapters 15 and 16. Table 8-1. Route Summary URL Leads To / Lists the first page of products from all categories /Page2 Lists the specified page (in this case, page 2), showing items from all categories /Soccer Shows the first page of items from a specific category (in this case, the Soccer category) /Soccer/Page2 Shows the specified page (in this case, page 2) of items from the specified category (in this case, Soccer) The ASP.NET routing system is used by MVC to handle incoming requests from clients, but it also generates outgoing URLs that conform to the URL scheme and that can be embedded in Web pages. By using the routing system to handle incoming requests and generate outgoing URLs, I can ensure that all of the URLs in the application are consistent. Note I show you how to unit test routing configurations in Chapter 15. The Url.Action method is the most convenient way of generating outgoing links. In the previous chapter, I used this helper method in the List view in order to display the page links. Now that I have added support for category filtering, I need to go back and pass this information to the helper method, as shown in Listing 8-4. Listing 8-4. Adding Category Information to the Pagination Links in the List.cshtml File 206 @model SportsStore.WebUI.Models.ProductsListViewModel @{ ViewBag.Title = "Products"; } @foreach (var p in Model.Products) { @Html.Partial("ProductSummary", p) }
@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new { page = x, category = Model.CurrentCategory }))
Prior to this change, the links generated for the pagination links were like this: http://:/Page1 If the user clicked a page link like this, the category filter he applied would be lost, and he would be presented with a page containing products from all categories. By adding the current category, taken from the view model, I generate URLs like this instead: http:// : /Chess/Page1 When the user clicks this kind of link, the current category will be passed to the List action method, and the filtering will be preserved. After you have made this change, you can visit a URL such as /Chess or /Soccer, and you will see that the page link at the bottom of the page correctly includes the category. Building a Category Navigation Menu I need to provide customers with a way to select a category that does not involve typing in URLs. This means presenting them with a list of the categories available and indicating which, if any, is currently selected. As I build out the application, I will use this list of categories in more than one controller, so I need something that is self-contained and reusable. The ASP.NET MVC Framework has the concept of child actions, which are perfect for creating items such as a reusable navigation control. A child action relies on the HTML helper method called Html.Action, which lets you include the output from an arbitrary action method in the current view. In this case, I can create a new controller (I will call it NavController) with an action method (which I will call Menu) that renders a navigation menu. I will then use the Html.Action helper method to inject the output from that method into the layout. This approach gives me a real controller that can contain whatever application logic I need and that can be unit tested like any other controller. It is a nice way of creating smaller segments of an application while preserving the overall MVC Framework approach. Creating the Navigation Controller Right-click the Controllers folder in the SportsStore.WebUI project and select Add Controller from the pop-up menu. Select MVC 5 Controller – Empty from the list, click the Add button, set the controller name to NavController and click the Add button to create the NavController.cs class file. Remove the Index method that Visual Studio adds to new controllers by default and add a new action method called Menu, as shown in Listing 8-5. Listing 8-5. Adding The Menu Action Method to the NavController.cs File using System.Web.Mvc; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { 207 public string Menu() { return "Hello from NavController"; } } } This method returns a static message string but it is enough to get me started while I integrate the child action into the rest of the application. I want the category list to appear on all pages, so I am going to render the child action in the layout, rather than in a specific view. Edit the Views/Shared/_Layout.cshtml file so that it calls the Html.Action helper method, as shown in Listing 8-6. Listing 8-6. Adding the RenderAction Call to the _Layout.cshtml File @ViewBag.Title
@Html.Action("Menu", "Nav")
@RenderBody()
I removed the placeholder text and replaced it with a call to the Html.Action method. The parameters to this method are the name of the action method I want to call (Menu) and the controller that contains it (Nav). If you run the application, you will see that the output of the Menu action method is included in the response sent to the browser, as shown in Figure 8-2. 208 Figure 8-2. Displaying the output from the Menu action method Generating Category Lists I can now return to the Nav controller and generate a real set of categories. I do not want to generate the category URLs in the controller. Instead, I am going to use a helper method in the view to do that. All I am going to do in the Menu action method is create the list of categories, which I have done in Listing 8-7. Listing 8-7. Implementing the Menu Method in the NavController.cs File using System.Collections.Generic; using System.Web.Mvc; using SportsStore.Domain.Abstract; using System.Linq; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { private IProductRepository repository; public NavController(IProductRepository repo) { repository = repo; } public PartialViewResult Menu() { IEnumerable categories = repository.Products .Select(x => x.Category) .Distinct() .OrderBy(x => x); return PartialView(categories); } } } The first change is to add a constructor that accepts an IProductRepository implementation as its argument. This 209 has the effect of declaring a dependency that Ninject will resolve when it creates instances of the NavController class. The second change is to the Menu action method, which now uses a LINQ query to obtain a list of categories from the repository and passes them to the view. Notice that, since I am working with a partial view in this controller, I call the PartialView method in the action method and that the result is a PartialViewResult object. UNIT TEST: GENERATING THE CATEGORY LIST The unit test for my ability to produce a category list is relatively simple. My goal is to create a list that is sorted in alphabetical order and contains no duplicates. The simplest way to do this is to supply some test data that does have duplicate categories and that is not in order, pass this to the NavController, and assert that the data has been properly cleaned up. Here is the unit test: ... [TestMethod] public void Can_Create_Categories() { // Arrange // - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, new Product {ProductID = 2, Name = "P2", Category = "Apples"}, new Product {ProductID = 3, Name = "P3", Category = "Plums"}, new Product {ProductID = 4, Name = "P4", Category = "Oranges"}, }); // Arrange - create the controller NavController target = new NavController(mock.Object); // Act = get the set of categories string[] results = ((IEnumerable)target.Menu().Model).ToArray(); // Assert Assert.AreEqual(results.Length, 3); Assert.AreEqual(results[0], "Apples"); Assert.AreEqual(results[1], "Oranges"); Assert.AreEqual(results[2], "Plums"); } ... I created a mock repository implementation that contains repeating categories and categories that are not in order. I assert that the duplicates are removed and that alphabetical ordering is imposed. Creating the View To create the view for the Menu action method, right-click on the Views/Nav folder and select Add MVC 5 View Page (Razor) from the pop-up menu. Set the name to Menu and click the OK button to create the Menu.cshtml file. Remove the contents that Visual Studio adds to new views and set the content to match Listing 8-8. Listing 8-8. The Contents of the Menu.cshtml File @model IEnumerable @Html.ActionLink("Home", "List", "Product", null, new { @class = "btn btn-block btn-default btn-lg" }) 210 @foreach (var link in Model) { @Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 }, new { @class = "btn btn-block btn-default btn-lg" }) } I added a link called Home that will appear at the top of the category list and will list all of the products with no category filter. I did this using the ActionLink helper method, which generates an HTML anchor element using the routing information configured earlier. I then enumerated the category names and created links for each of them using the RouteLink method. This is similar to ActionLink, but it lets me supply a set of name/value pairs that are taken into account when generating the URL from the routing configuration. Do not worry if all this talk of routing does not make sense yet. I explain everything in depth in Chapters 15 and 16. The links I generate will look pretty ugly by default, so I have supplied an object to both the ActionLink and RouteLink helper methods that specifies values for attributes on the elements that are created. The objects I created define the class attribute (prefixed with a @ because class is a reserved C# keyword) and apply Bootstrap classes to style the links as large buttons. You can see the category links if you run the application, as shown in Figure 8-3. If you click a category, the list of items is updated to show only items from the selected category. 211 Figure 8-3. The category links Highlighting the Current Category At present, I do not indicate to users which category they are viewing. It might be something that the customer could infer from the items in the list, but I would prefer to provide solid visual feedback. I could do this by creating a view model that contains the list of categories and the selected category, and in fact, this is exactly what I would usually do. But for variety I am going to use the view bag feature I introduced in Chapter 2. This feature allows me to pass data from the controller to the view without using a view model. Listing 8-9 shows the changes to the Menu action method in the Nav controller. Listing 8-9. Using the View Bag Feature in the NavController.cs File using System.Collections.Generic; using System.Web.Mvc; using SportsStore.Domain.Abstract; using System.Linq; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { private IProductRepository repository; public NavController(IProductRepository repo) { 212 repository = repo; } public PartialViewResult Menu(string category = null) { ViewBag.SelectedCategory = category; IEnumerable categories = repository.Products .Select(x => x.Category) .Distinct() .OrderBy(x => x); return PartialView(categories); } } } I added a parameter to the Menu action method called category. The value for this parameter will be provided automatically by the routing configuration. Inside the method body, I have dynamically assigned a SelectedCategory property to the ViewBag object and set its value to be the current category. As I explained in Chapter 2, the ViewBag is a dynamic object and I create new properties simply by setting values for them. UNIT TEST: REPORTING THE SELECTED CATEGORY I can test that the Menu action method correctly adds details of the selected category by reading the value of the ViewBag property in a unit test, which is available through the ViewResult class. Here is the test: ... [TestMethod] public void Indicates_Selected_Category() { // Arrange // - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, new Product {ProductID = 4, Name = "P2", Category = "Oranges"}, }); // Arrange - create the controller NavController target = new NavController(mock.Object); // Arrange - define the category to selected string categoryToSelect = "Apples"; // Action string result = target.Menu(categoryToSelect).ViewBag.SelectedCategory; // Assert Assert.AreEqual(categoryToSelect, result); } ... This unit test will not compile unless you add a reference to the Microsoft.CSharp assembly, as described in the previous chapter. 213 Now that I am providing information about which category is selected, I can update the view to take advantage of this, and add a CSS class to the HTML anchor element that represents the selected category. Listing 8-10 shows the changes to the Menu.cshtml file. Listing 8-10. Highlighting the Selected Category in the Menu.cshtml File @model IEnumerable @Html.ActionLink("Home", "List", "Product", null, new { @class = "btn btn-block btn-default btn-lg" }) @foreach (var link in Model) { @Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 }, new { @class = "btn btn-block btn-default btn-lg" + (link == ViewBag.SelectedCategory ? " btn-primary" : "") }) } The change is simple. If the current link value matches the SelectedCategory value, then I add the element I am creating to another Bootstrap class, which will cause the button to be highlighted. Running the application shows the effect of the category highlighting, which you can also see in Figure 8-4. 214 Figure 8-4. Highlighting the selected category Correcting the Page Count I need to correct the page links so that they work correctly when a category is selected. Currently, the number of page links is determined by the total number of products in the repository and not the number of products in the selected category. This means that the customer can click the link for page 2 of the Chess category and end up with an empty page because there are not enough chess products to fill two pages. You can see the problem in Figure 8-5. Figure 8-5. Displaying the wrong page links when a category is selected I can fix this by updating the List action method in the Product controller so that the pagination information takes the categories into account. You can see the required changes in Listing 8-11. Listing 8-11. Creating Category-Aware Pagination Data in the ProductController.cs File ... public ViewResult List(string category, int page = 1) { ProductsListViewModel viewModel = new ProductsListViewModel { Products = repository.Products .Where(p => category == null || p.Category == category) .OrderBy(p => p.ProductID) .Skip((page - 1) * PageSize) .Take(PageSize), PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize, TotalItems = category == null ? repository.Products.Count() : repository.Products.Where(e => e.Category == category).Count() }, 215 CurrentCategory = category }; return View(viewModel); } ... If a category has been selected, I return the number of items in that category; if not, I return the total number of products. Now when I view a category, the links at the bottom of the page correctly reflect the number of products in the category, as shown in Figure 8-6. Figure 8-6. Displaying category-specific page counts UNIT TEST: CATEGORY-SPECIFIC PRODUCT COUNTS Testing that I am able to generate the current product count for different categories is simple. I create a mock repository that contains known data in a range of categories and then call the List action method requesting each category in turn. Here is the unit test: ... [TestMethod] public void Generate_Category_Specific_Product_Count() { 216 // Arrange // - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Cat1"}, new Product {ProductID = 2, Name = "P2", Category = "Cat2"}, new Product {ProductID = 3, Name = "P3", Category = "Cat1"}, new Product {ProductID = 4, Name = "P4", Category = "Cat2"}, new Product {ProductID = 5, Name = "P5", Category = "Cat3"} }); // Arrange - create a controller and make the page size 3 items ProductController target = new ProductController(mock.Object); target.PageSize = 3; // Action - test the product counts for different categories int res1 = ((ProductsListViewModel)target .List("Cat1").Model).PagingInfo.TotalItems; int res2 = ((ProductsListViewModel)target .List("Cat2").Model).PagingInfo.TotalItems; int res3 = ((ProductsListViewModel)target .List("Cat3").Model).PagingInfo.TotalItems; int resAll = ((ProductsListViewModel)target .List(null).Model).PagingInfo.TotalItems; // Assert Assert.AreEqual(res1, 2); Assert.AreEqual(res2, 2); Assert.AreEqual(res3, 1); Assert.AreEqual(resAll, 5); } ... Notice that I also call the List method, specifying no category, to make sure I get the right total count as well. Building the Shopping Cart The application is progressing nicely, but I cannot sell any products until I implement a shopping cart. In this section, I will create the shopping cart experience shown in Figure 8-7. This will be familiar to anyone who has ever made a purchase online. Figure 8-7. The basic shopping cart flow An Add to Cart button will be displayed alongside each of the products in the catalog. Clicking this button will show a summary of the products the customer has selected so far, including the total cost. At this point, the user can click the 217 Continue Shopping button to return to the product catalog, or click the Checkout Now button to complete the order and finish the shopping session. Defining the Cart Entity A shopping cart is part of the business domain, so it makes sense to represent a cart by creating an entity in the domain model. Add a class file called Cart.cs to the Entities folder in the SportsStore.Domain project and use it to define the classes shown in Listing 8-12. Listing 8-12. The Cart and CartLine Classes in the Cart.cs File using System.Collections.Generic; using System.Linq; namespace SportsStore.Domain.Entities { public class Cart { private List lineCollection = new List(); public void AddItem(Product product, int quantity) { CartLine line = lineCollection .Where(p => p.Product.ProductID == product.ProductID) .FirstOrDefault(); if (line == null) { lineCollection.Add(new CartLine { Product = product, Quantity = quantity }); } else { line.Quantity += quantity; } } public void RemoveLine(Product product) { lineCollection.RemoveAll(l => l.Product.ProductID == product.ProductID); } public decimal ComputeTotalValue() { return lineCollection.Sum(e => e.Product.Price * e.Quantity); } public void Clear() { lineCollection.Clear(); } public IEnumerable Lines { get { return lineCollection; } } } public class CartLine { public Product Product { get; set; } public int Quantity { get; set; } } } 218 The Cart class uses the CartLine class, defined in the same file, to represent a product selected by the customer and the quantity the user wants to buy. I defined methods to add an item to the cart, remove a previously added item from the cart, calculate the total cost of the items in the cart, and reset the cart by removing all of the items. I also provided a property that gives access to the contents of the cart using an IEnumerable. This is all straightforward stuff, easily implemented in C# with the help of a little LINQ. UNIT TEST: TESTING THE CART The Cart class is relatively simple, but it has a range of important behaviors that must work properly. A poorly functioning cart would undermine the entire SportsStore application. I have broken down the features and tested them individually. I created a new unit test file in the SportsStore.UnitTests project called CartTests.cs to contain these tests. The first behavior relates to when I add an item to the cart. If this is the first time that a given Product has been added to the cart, I want a new CartLine to be added. Here is the test, including the unit test class definition: using System. Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using SportsStore.Domain.Entities; namespace SportsStore.UnitTests { [TestClass] public class CartTests { [TestMethod] public void Can_Add_New_Lines() { // Arrange - create some test products Product p1 = new Product { ProductID = 1, Name = "P1" }; Product p2 = new Product { ProductID = 2, Name = "P2" }; // Arrange - create a new cart Cart target = new Cart(); // Act target.AddItem(p1, 1); target.AddItem(p2, 1); CartLine[] results = target.Lines.ToArray(); // Assert Assert.AreEqual(results.Length, 2); Assert.AreEqual(results[0].Product, p1); Assert.AreEqual(results[1].Product, p2); } } } However, if the customer has already added a Product to the cart, I want to increment the quantity of the corresponding CartLine and not create a new one. Here is the test: .. [TestMethod] public void Can_Add_Quantity_For_Existing_Lines() { // Arrange - create some test products Product p1 = new Product { ProductID = 1, Name = "P1" }; Product p2 = new Product { ProductID = 2, Name = "P2" }; 219 // Arrange - create a new cart Cart target = new Cart(); // Act target.AddItem(p1, 1); target.AddItem(p2, 1); target.AddItem(p1, 10); CartLine[] results = target.Lines.OrderBy(c => c.Product.ProductID).ToArray(); // Assert Assert.AreEqual(results.Length, 2); Assert.AreEqual(results[0].Quantity, 11); Assert.AreEqual(results[1].Quantity, 1); } ... I also need to check that users can change their mind and remove products from the cart. This feature is implemented by the RemoveLine method. Here is the test: ... [TestMethod] public void Can_Remove_Line() { // Arrange - create some test products Product p1 = new Product { ProductID = 1, Name = "P1" }; Product p2 = new Product { ProductID = 2, Name = "P2" }; Product p3 = new Product { ProductID = 3, Name = "P3" }; // Arrange - create a new cart Cart target = new Cart(); // Arrange - add some products to the cart target.AddItem(p1, 1); target.AddItem(p2, 3); target.AddItem(p3, 5); target.AddItem(p2, 1); // Act target.RemoveLine(p2); // Assert Assert.AreEqual(target.Lines.Where(c => c.Product == p2).Count(), 0); Assert.AreEqual(target.Lines.Count(), 2); } ... The next behavior I want to test is the ability to calculate the total cost of the items in the cart. Here’s the test for this behavior: ... [TestMethod] public void Calculate_Cart_Total() { // Arrange - create some test products Product p1 = new Product { ProductID = 1, Name = "P1", Price = 100M}; 220 Product p2 = new Product { ProductID = 2, Name = "P2" , Price = 50M}; // Arrange - create a new cart Cart target = new Cart(); // Act target.AddItem(p1, 1); target.AddItem(p2, 1); target.AddItem(p1, 3); decimal result = target.ComputeTotalValue(); // Assert Assert.AreEqual(result, 450M); } ... The final test is simple. I want to ensure that the contents of the cart are properly removed when reset. Here is the test: ... [TestMethod] public void Can_Clear_Contents() { // Arrange - create some test products Product p1 = new Product { ProductID = 1, Name = "P1", Price = 100M }; Product p2 = new Product { ProductID = 2, Name = "P2", Price = 50M }; // Arrange - create a new cart Cart target = new Cart(); // Arrange - add some items target.AddItem(p1, 1); target.AddItem(p2, 1); // Act - reset the cart target.Clear(); // Assert Assert.AreEqual(target.Lines.Count(), 0); } ... Sometimes, as in this case, the code required to test the functionality of a type is longer and more complex than the type itself. Do not let that put you off writing the unit tests. Defects in simple classes can have huge impacts, especially ones that play such an important role as Cart does in the example application. Adding the Add to Cart Buttons I need to edit the Views/Shared/ProductSummary.cshtml view to add the buttons to the product listings. The changes are shown in Listing 8-13. Listing 8-13. Adding the Buttons to the Product Summary.cshtml File View @model SportsStore.Domain.Entities.Product
221

@Model.Name @Model.Price.ToString("c")

@using (Html.BeginForm("AddToCart", "Cart")) {
@Html.HiddenFor(x => x.ProductID) @Html.Hidden("returnUrl", Request.Url.PathAndQuery)
} @Model.Description
I added a Razor block that creates a small HTML form for each product in the listing. When this form is submitted, it will invoke the AddToCart action method in the Cart controller (which I will implement in just a moment). Note By default, the BeginForm helper method creates a form that uses the HTTP POST method. You can change this so that forms use the GET method, but you should think carefully about doing so. The HTTP specification requires that GET requests must be idempotent, meaning that they must not cause changes, and adding a product to a cart is definitely a change. I have more to say on this topic in Chapter 16, including an explanation of what can happen if you ignore the need for idempotent GET requests. CREATING MULTIPLE HTML FORMS IN A PAGE Using the Html.BeginForm helper in each product listing means that every Add to cart button is rendered in its own separate HTML form element. This may be surprising if you have been developing with ASP.NET Web Forms, which imposes a limit of one form per page if you want to use the view state feature or complex controls (which tend to rely on view state). Since ASP.NET MVC does not use view state, there is no limit the number of forms you can create. Equally, there is no requirement to create a form for each button. However, since each form will post back to the same controller method, but with a different set of parameter values, it is a nice and simple way to deal with the button presses. Implementing the Cart Controller I need a controller to handle the Add to cart button presses. Create a new controller called CartController in the SportsStore.WebUI project and edit the content so that it matches Listing 8-14. Listing 8-14. The Contents of the CartController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; public CartController(IProductRepository repo) { repository = repo; 222 } public RedirectToRouteResult AddToCart(int productId, string returnUrl) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); if (product != null) { GetCart().AddItem(product, 1); } return RedirectToAction("Index", new { returnUrl }); } public RedirectToRouteResult RemoveFromCart(int productId, string returnUrl) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); if (product != null) { GetCart().RemoveLine(product); } return RedirectToAction("Index", new { returnUrl }); } private Cart GetCart() { Cart cart = (Cart)Session["Cart"]; if (cart == null) { cart = new Cart(); Session["Cart"] = cart; } return cart; } } } There are a few points to note about this controller. The first is that I use the ASP.NET session state feature to store and retrieve Cart objects. This is the purpose of the GetCart method. ASP.NET has a nice session feature that uses cookies or URL rewriting to associate multiple requests from a user together to form a single browsing session. A related feature is session state, which associates data with a session. This is an ideal fit for the Cart class. I want each user to have their own cart, and I want the cart to be persistent between requests. Data associated with a session is deleted when a session expires (typically because a user has not made a request for a while), which means that I do not need to manage the storage or life cycle of the Cart objects. To add an object to the session state, I set the value for a key on the Session object, like this: ... Session["Cart"] = cart; ... To retrieve an object again, I simply read the same key, like this: ... Cart cart = (Cart)Session["Cart"]; ... Tip Session state objects are stored in the memory of the ASP.NET server by default, but you can configure a range of different storage approaches, including using a SQL database. See my book, Pro ASP.NET MVC 5 Platform, published by Apress in 2014, for details. 223 For the AddToCart and RemoveFromCart methods, I have used parameter names that match the input elements in the HTML forms created in the ProductSummary.cshtml view. This allows the MVC Framework to associate incoming form POST variables with those parameters, meaning I do not need to process the form myself. Displaying the Contents of the Cart The final point to note about the Cart controller is that both the AddToCart and RemoveFromCart methods call the RedirectToAction method. This has the effect of sending an HTTP redirect instruction to the client browser, asking the browser to request a new URL. In this case, I have asked the browser to request a URL that will call the Index action method of the Cart controller. I am going to implement the Index method and use it to display the contents of the Cart. If you refer back to Figure 8-7, you will see that this is the workflow when the user clicks the Add to cart button. I need to pass two pieces of information to the view that will display the contents of the cart: the Cart object and the URL to display if the user clicks the Continue shopping button. I created a new class file called CartIndexViewModel.cs in the Models folder of the SportsStore.WebUI project. The contents of this file are shown in Listing 8-15. Listing 8-15. The Contents of the CartIndexViewModel.cs File using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Models { public class CartIndexViewModel { public Cart Cart { get; set; } public string ReturnUrl { get; set; } } } Now that I have the view model, I can implement the Index action method in the Cart controller class, as shown in Listing 8-16. Listing 8-16. The Index Action Method in the CartController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; public CartController(IProductRepository repo) { repository = repo; } public ViewResult Index(string returnUrl) { return View(new CartIndexViewModel { Cart = GetCart(), ReturnUrl = returnUrl }); } 224 // ...other action methods omitted for brevity... } } The last step to display the contents of the cart is to create the new view. Right-click on the Index action method and select Add View from the pop-up menu. Set the name to Index and click the OK button to create the Index.cshtml view file. Edit the view to match the contents shown in Listing 8-17. Listing 8-17. The Contents of the Index.cshtml File @model SportsStore.WebUI.Models.CartIndexViewModel @{ ViewBag.Title = "Sports Store: Your Cart"; }

Your cart

@foreach (var line in Model.Cart.Lines) { }
Quantity Item Price Subtotal
@line.Quantity @line.Product.Name @line.Product.Price.ToString("c") @((line.Quantity * line.Product.Price).ToString("c"))
Total: @Model.Cart.ComputeTotalValue().ToString("c")
The view enumerates the lines in the cart and adds rows for each of them to an HTML table, along with the total cost per line 225 and the total cost for the cart. The classes I have assigned the elements to correspond to Bootstrap styles for tables and text alignment. I now have the basic functions of the shopping cart in place. First, products are listed along with a button to add them to the cart, as shown in Figure 8-8. Figure 8-8. The Add to cart button And second, when the user clicks the Add to cart button, the appropriate product is added to their cart and a summary of the cart is displayed, as shown in Figure 8-9. And clicking the Continue shopping button returns the user to the product page they came from. 226 Figure 8-9. Displaying the contents of the shopping cart Summary In this chapter, I started to flesh out the customer-facing parts of the SportsStore app. I provided the means by which the user can navigate by category and put the basic building blocks in place for adding items to a shopping cart. I have more work to do and I continue the development of the application in the next chapter. 227 CHAPTER 9 SportsStore: Completing the Cart In this chapter, I continue to build out the SportsStore example app. In the previous chapter, I added the basic support for a shopping cart and now I am going to improve on and complete that functionality. Using Model Binding The MVC Framework uses a system called model binding to create C# objects from HTTP requests in order to pass them as parameter values to action methods. This is how the MVC framework processes forms, for example: it looks at the parameters of the action method that has been targeted and uses a model binder to get the form values sent by the browser and convert them to the type of the parameter with the same name before passing them to the action method. Model binders can create C# types from any information that is available in the request. This is one of the central features of the MVC Framework. I am going to create a custom model binder to improve the CartController class. I like using the session state feature in the Cart controller to store and manage the Cart objects that I set up in Chapter 8, but I do not like the way I have to go about it. It does not fit the rest of the application model, which is based around action method parameters. I cannot properly unit test the CartController class unless I mock the Session parameter of the base class, and that means mocking a whole bunch of other stuff I would rather not deal with. To solve this problem, I am going to create a custom model binder that obtains the Cart object contained in the session data. The MVC Framework will then be able to create Cart objects and pass them as parameters to the action methods in the CartController class. The model binding feature is powerful and flexible. I go into a lot more depth about this feature in Chapter 24, but this is a nice example to get started with. Creating a Custom Model Binder I create a custom model binder by implementing the System.Web.Mvc.IModelBinder interface. To create this implementation, I added a new folder in the SportsStore.WebUI project called Infrastructure/Binders and created a CartModelBinder.cs class file inside it. Listing 9-1 shows the contents of the new file. Listing 9-1. The Contents of the CartModelBinder.cs File using System.Web.Mvc; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure.Binders { public class CartModelBinder : IModelBinder { private const string sessionKey = "Cart"; public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { // get the Cart from the session Cart cart = null; if (controllerContext.HttpContext.Session != null) { 228 cart = (Cart)controllerContext.HttpContext.Session[sessionKey]; } // create the Cart if there wasn't one in the session data if (cart == null) { cart = new Cart(); if (controllerContext.HttpContext.Session != null) { controllerContext.HttpContext.Session[sessionKey] = cart; } } // return the cart return cart; } } } The IModelBinder interface defines one method: BindModel. The two parameters are provided to make creating the domain model object possible. The ControllerContext provides access to all the information that the controller class has, which includes details of the request from the client. The ModelBindingContext gives you information about the model object you are being asked to build and some tools for making the binding process easier. For my purposes, the ControllerContext class is the one I am interested in. It has an HttpContext property, which in turn has a Session property that lets me get and set session data. I can obtain the Cart object associated with the user's session by reading a value from the session data, and create a Cart if there is not one there already. I need to tell the MVC Framework that it can use the CartModelBinder class to create instances of Cart. I do this in the Application_Start method of Global.asax, as shown in Listing 9-2. Listing 9-2. Registering the CartModelBinder Class in the Global.asax.cs File using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using SportsStore.Domain.Entities; using SportsStore.WebUI.Infrastructure.Binders; namespace SportsStore.WebUI { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder()); } } } I can now update the Cart controller to remove the GetCart method and rely on the model binder to provide the controller with Cart objects. Listing 9-3 shows the changes. Listing 9-3. Relying on the Model Binder in the CartController.cs File 229 using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; public CartController(IProductRepository repo) { repository = repo; } public ViewResult Index(Cart cart, string returnUrl) { return View(new CartIndexViewModel { ReturnUrl = returnUrl, Cart = cart }); } public RedirectToRouteResult AddToCart(Cart cart, int productId, string returnUrl) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); if (product != null) { cart.AddItem(product, 1); } return RedirectToAction("Index", new { returnUrl }); } public RedirectToRouteResult RemoveFromCart(Cart cart, int productId, string returnUrl) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); if (product != null) { cart.RemoveLine(product); } return RedirectToAction("Index", new { returnUrl }); } } } I have removed the GetCart method and added a Cart parameter to each of the action methods. When the MVC Framework receives a request that requires, say, the AddToCart method to be invoked, it begins by looking at the parameters for the action method. It looks at the list of binders available and tries to find one that can create instances of each parameter type. The custom binder is asked to create a Cart object, and it does so by working with the session state feature. Between the custom binder and the default binder, the MVC Framework is able to create the set of parameters required to call the action method, allowing me to refactor the controller so that it has no knowledge of how Cart objects are created when requests are received. There are several benefits to using a custom model binder like this. The first is that I have separated the logic used to create a Cart from that of the controller, which allows me to change the way I store Cart objects without needing to change the 230 controller. The second benefit is that any controller class that works with Cart objects can simply declare them as action method parameters and take advantage of the custom model binder. The third benefit, and the one I think is most important, is that I can now unit test the Cart controller without needing to mock a lot of ASP.NET plumbing. UNIT TEST: THE CART CONTROLLER I can unit test the CartController class by creating Cart objects and passing them to the action methods. I want to test three different aspects of this controller: The AddToCart method should add the selected product to the customer’s cart. After adding a product to the cart, the user should be redirected to the Index view. The URL that the user can follow to return to the catalog should be correctly passed to the Index action method. Here are the unit tests I added to the CartTests.cs file in the SportsStore.UnitTests project: using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using SportsStore.Domain.Entities; using System.Linq; using Moq; using SportsStore.Domain.Abstract; using SportsStore.WebUI.Controllers; using System.Web.Mvc; using SportsStore.WebUI.Models; namespace SportsStore.UnitTests { [TestClass] public class CartTests { // . . . existing test methods omitted for brevity. . . [TestMethod] public void Can_Add_To_Cart() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, }.AsQueryable()); // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(mock.Object); // Act - add a product to the cart target.AddToCart(cart, 1, null); // Assert Assert.AreEqual(cart.Lines.Count(), 1); Assert.AreEqual(cart.Lines.ToArray()[0].Product.ProductID, 1); } [TestMethod] 231 public void Adding_Product_To_Cart_Goes_To_Cart_Screen() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1", Category = "Apples"}, }.AsQueryable()); // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(mock.Object); // Act - add a product to the cart RedirectToRouteResult result = target.AddToCart(cart, 2, "myUrl"); // Assert Assert.AreEqual(result.RouteValues["action"], "Index"); Assert.AreEqual(result.RouteValues["returnUrl"], "myUrl"); } [TestMethod] public void Can_View_Cart_Contents() { // Arrange - create a Cart Cart cart = new Cart(); // Arrange - create the controller CartController target = new CartController(null); // Act - call the Index action method CartIndexViewModel result = (CartIndexViewModel)target.Index(cart, "myUrl").ViewData.Model; // Assert Assert.AreSame(result.Cart, cart); Assert.AreEqual(result.ReturnUrl, "myUrl"); } } } Completing the Cart Now that I have introduced the custom model binder, it is time to complete the cart functionality by adding two new features. The first will allow the customer to remove an item from the cart. The second feature will display a summary of the cart at the top of the page. Removing Items from the Cart I already defined and tested the RemoveFromCart action method in the controller, so letting the customer remove items is just a matter of exposing this method in a view, which I are going to do by adding a Remove button in each row of the cart summary. The changes to Views/Cart/Index.cshtml are shown in Listing 9-4. 232 Listing 9-4. Introducing a Remove Button to the Index.cshtml File @model SportsStore.WebUI.Models.CartIndexViewModel @{ ViewBag.Title = "Sports Store: Your Cart"; }

Your cart

@foreach (var line in Model.Cart.Lines) { }
Quantity Item Price Subtotal
@line.Quantity @line.Product.Name @line.Product.Price.ToString("c") @((line.Quantity * line.Product.Price).ToString("c")) @using (Html.BeginForm("RemoveFromCart", "Cart")) { @Html.Hidden("ProductId", line.Product.ProductID) @Html.HiddenFor(x => x.ReturnUrl) }
Total: @Model.Cart.ComputeTotalValue().ToString("c")
I added a new column to each row of the table that contains a form with an input element. I styled the input element as a button with Bootstrap and added a style element and an id to the table element to ensure that the button and the content of the other columns are properly aligned. Note I used the strongly typed Html.HiddenFor helper method to create a hidden field for the ReturnUrl model property, but I had to use the string-based Html.Hidden helper to do the same for the ProductId field. If I had written Html.HiddenFor(x => line.Product.ProductID), the helper would render a hidden field with the name line.Product.ProductID. The name of the field would not match the names of the parameters for the CartController.RemoveFromCart action method, which would prevent the default model binders from working, so the MVC Framework would not be able to call the method. You can see the Remove buttons at work by running the application and adding items to the shopping cart. Remember that the cart already contains the functionality to remove it, which you can test by clicking one of the new buttons, as shown in Figure 9-1. Figure 9-1. Removing an item from the shopping cart Adding the Cart Summary I may have a functioning cart, but there is an issue with the way it is integrated into the interface. Customers can tell what is in their cart only by viewing the cart summary screen. And they can view the cart summary screen only by adding a new a new item to the cart. To solve this problem, I am going to add a widget that summarizes the contents of the cart and that can be clicked to display the cart contents throughout the application. I will do this in much the same way that I added the navigation widget—as an action whose output I will inject into the Razor layout. To start, I need to add the simple method, shown in Listing 9-5, to the CartController class. 234 Listing 9-5. Adding the Summary Method to the CartController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; public CartController(IProductRepository repo) { repository = repo; } // . . . other action methods omitted for brevity . . . public PartialViewResult Summary(Cart cart) { return PartialView(cart); } } } This simple method needs to render a view, supplying the current Cart (which will be obtained using the custom model binder) as view data. To create the view, right-click the Summary action method and select Add View from the pop-up menu. Set the name of the view to Summary and click the OK button to create the Views/Cart/Summary.cshtml file. Edit the view so that it matches Listing 9-6. Listing 9-6. The Contents of the Summary.cshtml File @model SportsStore.Domain.Entities.Cart The view displays the number of items in the cart, the total cost of those items, and a link that shows the details of the cart to the user (and, as you will have expected by now, I have assigned the elements that the view contains to classes defined by Bootstrap). Now that I have created the view that is returned by the Summary action method, I can call the Summary action method in the _Layout.cshtml file to display the cart summary, as shown in Listing 9-7. Listing 9-7. Adding the Summary Partial View to the _Layout.cshtml File 235 @ViewBag.Title
@Html.Action("Menu", "Nav")
@RenderBody()
You can see the cart summary by running the application. The item count and total increase as you add items to the cart, as shown by Figure 9-2. Figure 9-2. The cart summary widget With this addition, customers know what is in their cart and have an obvious way to check out from the store. You can see, once again, how easy it is to use the Html.Action helper method to incorporate the output from an action method in another view. This is a nice technique for breaking down the functionality of an application into distinct, reusable blocks. Submitting Orders I have now reached the final customer feature in SportsStore: the ability to check out and complete an order. In the following sections, I will extend the domain model to provide support for capturing the shipping details from a user and add the application support to process those details. 236 Extending the Domain Model Add a class file called ShippingDetails.cs to the Entities folder of the SportsStore.Domain project and edit it to match the contents shown in Listing 9-8. This is the class I will use to represent the shipping details for a customer. Listing 9-8. The Contents of the ShippingDetails.cs File using System.ComponentModel.DataAnnotations; namespace SportsStore.Domain.Entities { public class ShippingDetails { [Required(ErrorMessage = "Please enter a name")] public string Name { get; set; } [Required(ErrorMessage = "Please enter the first address line")] public string Line1 { get; set; } public string Line2 { get; set; } public string Line3 { get; set; } [Required(ErrorMessage = "Please enter a city name")] public string City { get; set; } [Required(ErrorMessage = "Please enter a state name")] public string State { get; set; } public string Zip { get; set; } [Required(ErrorMessage = "Please enter a country name")] public string Country { get; set; } public bool GiftWrap { get; set; } } } You can see that I am using the validation attributes from the System.ComponentModel.DataAnnotations namespace, just as I did in Chapter 2. I explore validation further in Chapter 25. Note The ShippingDetails class does not have any functionality, so there is nothing that that can be sensibly unit tested. Adding the Checkout Process The goal is to reach the point where users are able to enter their shipping details and submit their order. To start this off, I need to add a Checkout now button to the cart summary view. Listing 9-9 shows the change I applied to the Views/Cart/Index.cshtml file. Listing 9-9. Adding the Checkout Now Button to the Index.cshtml File . . .
Continue shopping @Html.ActionLink("Checkout now", "Checkout", null, new { @class = "btn btn-primary"}) 237
. . . This change generates a link that I have styled as a button and that, when clicked, calls the Checkout action method of the Cart controller. You can see how this button appears in Figure 9-3. Figure 9-3. The Checkout now button As you might expect, I now need to define the Checkout method in the CartController class, as shown in Listing 9-10. Listing 9-10. The Checkout Action Method in the CartController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; public CartController(IProductRepository repo) { repository = repo; } // . . . other action methods omitted for brevity . . . 238 public ViewResult Checkout() { return View(new ShippingDetails()); } } } The Checkout method returns the default view and passes a new ShippingDetails object as the view model. To create the view for the action method, right-click on the Checkout action method and select Add View from the pop-up menu. Set the name to Checkout and click the OK button. Visual Studio will create the Views/Cart/Checkout.cshtml file, which you should edit to match Listing 9-11. Listing 9-11. The Contents of the Checkout.cshtml File @model SportsStore.Domain.Entities.ShippingDetails @{ ViewBag.Title = "SportStore: Checkout"; }

Check out now

Please enter your details, and we'll ship your goods right away!

@using (Html.BeginForm()) {

Ship to

@Html.TextBoxFor(x => x.Name, new {@class = "form-control"})

Address

@Html.TextBoxFor(x => x.Line1, new {@class = "form-control"})
@Html.TextBoxFor(x => x.Line2, new {@class = "form-control"})
@Html.TextBoxFor(x => x.Line3, new {@class = "form-control"})
@Html.TextBoxFor(x => x.City, new {@class = "form-control"})
@Html.TextBoxFor(x => x.State, new {@class = "form-control"})
239 @Html.TextBoxFor(x => x.Zip, new {@class = "form-control"})
@Html.TextBoxFor(x => x.Country, new {@class = "form-control"})

Options

} For each of the properties in the model, I have created a label and input element formatted with Bootstrap to capture the user input. You can see the effect I have created by starting the application and clicking the Checkout button at the top of the page and then clicking Checkout now, as shown by Figure 9-4. (You can also reach this view by navigating to the /Cart/Checkout URL). 240 Figure 9-4. The shipping details form The problem with this view is that it contains a lot of repeated markup. There are MVC Framework HTML helpers that could reduce the duplication, but they make it hard to structure and style the content in the way that I want. Instead, I am going to use a handy feature to get metadata about the view model object and combine it with a mix of C# and Razor expressions. You can see what I have done in Listing 9-12. Listing 9-12. Reducing Duplication in the Checkout.cshtml File @model SportsStore.Domain.Entities.ShippingDetails @{ 241 ViewBag.Title = "SportStore: Checkout"; }

Check out now

Please enter your details, and we'll ship your goods right away!

@using (Html.BeginForm()) {

Ship to

@Html.TextBoxFor(x => x.Name, new {@class = "form-control"})

Address

foreach (var property in ViewData.ModelMetadata.Properties) { if (property.PropertyName != "Name" && property.PropertyName != "GiftWrap") {
@Html.TextBox(property.PropertyName, null, new {@class = "form-control"})
} }

Options

} The static ViewData.ModelMetadata property returns a System.Web.Mvc.ModelMetaData object that provides information about the model type for the view. The Properties property I use in the foreach loop returns a collection of ModelMetaData objects, each of which represents a property defined by the model type. I use the PropertyName property to ensure that I don't generate content for the Name or GiftWrap properties (which I deal with elsewhere in the view) and generate a set of elements, complete with Bootstrap classes, for all of the other properties. Tip The for and if keywords I have used are within the scope of a Razor expression (the @using expression that creates the form) and so I don’t need to prefix them with the @ character. In fact, were I to do so, Razor would report an error. It can take a little while to get used to when the @ character is required with Razor, but it becomes second nature for most programmers. For those that can't quite get it right first time (which includes me), the Razor error message displayed in the browser provides specific instructions to correct any mistakes. I am not quite done, however. If you run the example and look at the output generated by the view, you will see that some of the labels are not quite correct, as Figure 9-5 illustrates. 242 Figure 9-5. The problem with generating labels from property names The issue is that the property names don’t always make for good labels. This is why I check to see if there is a DisplayName value available when I generate the form elements, like this: . . . . . . To take advantage of the DisplayName property, I need to apply the Display attribute to the model class, as shown in Listing 9-13. Listing 9-13. Applying th e Display attribute to the ShippingDetails.cs File using System.ComponentModel.DataAnnotations; namespace SportsStore.Domain.Entities { public class ShippingDetails { [Required(ErrorMessage = "Please enter a name")] public string Name { get; set; } [Required(ErrorMessage = "Please enter the first address line")] [Display(Name="Line 1")] public string Line1 { get; set; } [Display(Name = "Line 2")] public string Line2 { get; set; } [Display(Name = "Line 3")] public string Line3 { get; set; } [Required(ErrorMessage = "Please enter a city name")] public string City { get; set; } [Required(ErrorMessage = "Please enter a state name")] public string State { get; set; } public string Zip { get; set; } [Required(ErrorMessage = "Please enter a country name")] 243 public string Country { get; set; } public bool GiftWrap { get; set; } } } Setting the Name value for the Display attribute allows me to set up a value that will be read by the DisplayName property in the view. You can see the effect by starting the application and viewing the checkout page, as shown in Figure 9-6. Figure 9-6. The effect of the Display attribute on the model type This example shows two different aspects of working with the MVC Framework. The first is that you can work around any situation to simplify your markup or code. The second is that even though the role that views play in the MVC pattern is restricted to displaying data and markup, the tools that Razor and C# provide for this task are rich and flexible, even to the extent of working with type metadata. Implementing the Order Processor I need a component in the application to which I can hand details of an order for processing. In keeping with the principles of the MVC model, I am going to define an interface for this functionality, write an implementation of the interface, and then associate the two using the DI container, Ninject. Defining the Interface Add a new interface called IOrderProcessor to the Abstract folder of the SportsStore.Domain project and edit the contents so that they match Listing 9-14. Listing 9-14. The Contents of the IOrderProcessor.cs File using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IOrderProcessor { 244 void ProcessOrder(Cart cart, ShippingDetails shippingDetails); } } Implementing the Interface The implementation of IOrderProcessor is going to deal with orders by e-mailing them to the site administrator. I am simplifying the sales process, of course. Most e-commerce sites would not simply e-mail an order, and I have not provided support for processing credit cards or other forms of payment. But I want to keep things focused on MVC, and so e-mail it is. Create a new class file called EmailOrderProcessor.cs in the Concrete folder of the SportsStore.Domain project and edit the contents so that they match Listing 9-15. This class uses the built-in SMTP support included in the .NET Framework library to send an e-mail. Listing 9-15. The Contents of the EmailOrderProcessor.cs File using System.Net; using System.Net.Mail; using System.Text; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Concrete { public class EmailSettings { public string MailToAddress = "orders@example.com"; public string MailFromAddress = "sportsstore@example.com"; public bool UseSsl = true; public string Username = "MySmtpUsername"; public string Password = "MySmtpPassword"; public string ServerName = "smtp.example.com"; public int ServerPort = 587; public bool WriteAsFile = false; public string FileLocation = @"c:\sports_store_emails"; } public class EmailOrderProcessor :IOrderProcessor { private EmailSettings emailSettings; public EmailOrderProcessor(EmailSettings settings) { emailSettings = settings; } public void ProcessOrder(Cart cart, ShippingDetails shippingInfo) { using (var smtpClient = new SmtpClient()) { smtpClient.EnableSsl = emailSettings.UseSsl; smtpClient.Host = emailSettings.ServerName; smtpClient.Port = emailSettings.ServerPort; smtpClient.UseDefaultCredentials = false; smtpClient.Credentials = new NetworkCredential(emailSettings.Username, emailSettings.Password); 245 if (emailSettings.WriteAsFile) { smtpClient.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; smtpClient.PickupDirectoryLocation = emailSettings.FileLocation; smtpClient.EnableSsl = false; } StringBuilder body = new StringBuilder() .AppendLine("A new order has been submitted") .AppendLine("---") .AppendLine("Items:"); foreach (var line in cart.Lines) { var subtotal = line.Product.Price * line.Quantity; body.AppendFormat("{0} x {1} (subtotal: {2:c}", line.Quantity, line.Product.Name, subtotal); } body.AppendFormat("Total order value: {0:c}", cart.ComputeTotalValue()) .AppendLine("---") .AppendLine("Ship to:") .AppendLine(shippingInfo.Name) .AppendLine(shippingInfo.Line1) .AppendLine(shippingInfo.Line2 ?? "") .AppendLine(shippingInfo.Line3 ?? "") .AppendLine(shippingInfo.City) .AppendLine(shippingInfo.State ?? "") .AppendLine(shippingInfo.Country) .AppendLine(shippingInfo.Zip) .AppendLine("---") .AppendFormat("Gift wrap: {0}", shippingInfo.GiftWrap ? "Yes" : "No"); MailMessage mailMessage = new MailMessage( emailSettings.MailFromAddress, // From emailSettings.MailToAddress, // To "New order submitted!", // Subject body.ToString()); // Body if (emailSettings.WriteAsFile) { mailMessage.BodyEncoding = Encoding.ASCII; } smtpClient.Send(mailMessage); } } } } 246 To make things simpler, I have defined the EmailSettings class in Listing 9-15 as well. An instance of this class is demanded by the EmailOrderProcessor constructor and contains all the settings that are required to configure the .NET e-mail classes. Tip Do not worry if you do not have an SMTP server available. If you set the EmailSettings.WriteAsFile property to true, the e-mail messages will be written as files to the directory specified by the FileLocation property. This directory must exist and be writable. The files will be written with the .eml extension, but they can be read with any text editor. The location I have set in the listing is c:\sports_store_emails. Registering the Implementation Now that I have an implementation of the IOrderProcessor interface and the means to configure it, I can use Ninject to create instances of it. Edit the NinjectDependencyResolver.cs file in the Infrastructure folder of the SportsStore.WebUI project and make the changes shown in Listing 9-16 to the AddBindings method. Listing 9-16. Adding Ninject Bindings for IOrderProcessor to the NinjectDependencyResolver.cs File using System; using System.Collections.Generic; using System.Configuration; using System.Web.Mvc; using Moq; using Ninject; using SportsStore.Domain.Abstract; using SportsStore.Domain.Concrete; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind().To(); EmailSettings emailSettings = new EmailSettings { WriteAsFile = bool.Parse(ConfigurationManager .AppSettings["Email.WriteAsFile"] ?? "false") }; kernel.Bind().To() .WithConstructorArgument("settings", emailSettings); 247 } } } I created an EmailSettings object, which I use with the Ninject WithConstructorArgument method so that I can inject it into the EmailOrderProcessor constructor when new instances are created to service requests for the IOrderProcessor interface. In Listing 9-16, I specified a value for only one of the EmailSettings properties: WriteAsFile. I read the value of this property using the ConfigurationManager.AppSettings property, which provides access to application settings defined in the Web.config file (the one in the root project folder), which are shown in Listing 9-17. Listing 9-17. Application Settings in the Web.config File . . . . . . Completing the Cart Controller To complete the CartController class, I need to modify the constructor so that it demands an implementation of the IOrderProcessor interface and add a new action method that will handle the HTTP form POST request when the user clicks the Complete order button. Listing 9-18 shows both changes. Listing 9-18. Completing the Controller in the CartController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers { public class CartController : Controller { private IProductRepository repository; private IOrderProcessor orderProcessor; public CartController(IProductRepository repo, IOrderProcessor proc) { repository = repo; orderProcessor = proc; } // . . . other action methods omitted for brevity . . . public ViewResult Checkout() { return View(new ShippingDetails()); } 248 [HttpPost] public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails) { if (cart.Lines.Count() == 0) { ModelState.AddModelError("", "Sorry, your cart is empty!"); } if (ModelState.IsValid) { orderProcessor.ProcessOrder(cart, shippingDetails); cart.Clear(); return View("Completed"); } else { return View(shippingDetails); } } } } You can see that the Checkout action method I added is decorated with the HttpPost attribute, which means that it will be invoked for a POST request—in this case, when the user submits the form. Once again, I am relying on the model binder system, both for the ShippingDetails parameter (which is created automatically using the HTTP form data) and the Cart parameter (which is created using the custom binder). Note The change in constructor forces me to update the unit tests I created for the CartController class. Passing null for the new constructor parameter will let the unit tests compile. The MVC Framework checks the validation constraints that I applied to ShippingDetails using the data annotation attributes, and any validation problems violations are passed to the action method through the ModelState property. I can see if there are any problems by checking the ModelState.IsValid property. Notice that I call the ModelState.AddModelError method to register an error message if there are no items in the cart. I will explain how to display such errors shortly, and I have much more to say about model binding and validation in Chapters 24 and 25. UNIT TEST: ORDER PROCESSING To complete the unit testing for the CartController class, I need to test the behavior of the new overloaded version of the Checkout method. Although the method looks short and simple, the use of MVC Framework model binding means that there is a lot going on behind the scenes that needs to be tested. I want to process an order only if there are items in the cart and the customer has provided valid shipping details. Under all other circumstances, the customer should be shown an error. Here is the first test method: . . . [TestMethod] public void Cannot_Checkout_Empty_Cart() { // Arrange - create a mock order processor Mock mock = new Mock(); // Arrange - create an empty cart Cart cart = new Cart(); // Arrange - create shipping details ShippingDetails shippingDetails = new ShippingDetails(); // Arrange - create an instance of the controller CartController target = new CartController(null, mock.Object); // Act 249 ViewResult result = target.Checkout(cart, shippingDetails); // Assert - check that the order hasn't been passed on to the processor mock.Verify(m => m.ProcessOrder(It.IsAny(), It.IsAny()), Times.Never()); // Assert - check that the method is returning the default view Assert.AreEqual("", result.ViewName); // Assert - check that I am passing an invalid model to the view Assert.AreEqual(false, result.ViewData.ModelState.IsValid); } . . . This test ensures that I cannot check out with an empty cart. I check this by ensuring that the ProcessOrder of the mock IOrderProcessor implementation is never called, that the view that the method returns is the default view (which will redisplay the data entered by customers and give them a chance to correct it), and that the model state being passed to the view has been marked as invalid. This may seem like a belt-and-braces set of assertions, but I need all three to be sure that I have the right behavior. The next test method works in much the same way, but injects an error into the view model to simulate a problem reported by the model binder (which would happen in production when the customer enters invalid shipping data): . . . [TestMethod] public void Cannot_Checkout_Invalid_ShippingDetails() { // Arrange - create a mock order processor Mock mock = new Mock(); // Arrange - create a cart with an item Cart cart = new Cart(); cart.AddItem(new Product(), 1); // Arrange - create an instance of the controller CartController target = new CartController(null, mock.Object); // Arrange - add an error to the model target.ModelState.AddModelError("error", "error"); // Act - try to checkout ViewResult result = target.Checkout(cart, new ShippingDetails()); // Assert - check that the order hasn't been passed on to the processor mock.Verify(m => m.ProcessOrder(It.IsAny(), It.IsAny()), Times.Never()); // Assert - check that the method is returning the default view Assert.AreEqual("", result.ViewName); // Assert - check that I am passing an invalid model to the view Assert.AreEqual(false, result.ViewData.ModelState.IsValid); } . . . Having established that an empty cart or invalid details will prevent an order from being processed, I need to ensure that I process orders when appropriate. Here is the test: . . . 250 [TestMethod] public void Can_Checkout_And_Submit_Order() { // Arrange - create a mock order processor Mock mock = new Mock(); // Arrange - create a cart with an item Cart cart = new Cart(); cart.AddItem(new Product(), 1); // Arrange - create an instance of the controller CartController target = new CartController(null, mock.Object); // Act - try to checkout ViewResult result = target.Checkout(cart, new ShippingDetails()); // Assert - check that the order has been passed on to the processor mock.Verify(m => m.ProcessOrder(It.IsAny(), It.IsAny()), Times.Once()); // Assert - check that the method is returning the Completed view Assert.AreEqual("Completed", result.ViewName); // Assert - check that I am passing a valid model to the view Assert.AreEqual(true, result.ViewData.ModelState.IsValid); } . . . Notice that I did not need to test that I can identify valid shipping details. This is handled for me automatically by the model binder using the attributes applied to the properties of the ShippingDetails class. Displaying Validation Errors The MVC Framework will use the validation attributes applied to the ShippingDetails class to validate user data. However, I need to make a couple of changes to display any problems to the user. The first issue is that I need to provide a summary of any problems to the user. This is especially important when dealing with problems that are not related to specific fields, such as when the user tries to check out with no items in the cart. To display a useful summary of the validation errors, I can use the Html.ValidationSummary helper method, just as I did in Chapter 2. Listing 9-19 shows the addition to Checkout.cshtml view. Listing 9-19. Adding a Validation Summary to the Checkout.cshtml File . . . @using (Html.BeginForm()) { @Html.ValidationSummary()

Ship to

@Html.TextBoxFor(x => x.Name, new {@class = "form-control"})

Address

. . . The next step is to create some CSS styles that target the classes used by the validation summary and those to which the MVC Framework adds invalid elements. I created a new Style Sheet called ErrorStyles.css in the Content folder 251 of the SportsStore.WebUI project and defined the styles shown in Listing 9-20. This is the same set of styles that I used in Chapter 2. Listing 9-20. The Contents of the ErrorStyles.css File .field-validation-error {color: #f00;} .field-validation-valid { display: none;} .input-validation-error { border: 1px solid #f00; background-color: #fee; } .validation-summary-errors { font-weight: bold; color: #f00;} .validation-summary-valid { display: none;} In order to use apply the styles, I updated the _Layout.cshtml file to add a link element for the ErrorStyles.css file, as shown in Listing 9-21. Listing 9-21. Adding a Link Element in the _Layout.cshtml File . . . @ViewBag.Title Thanks! Thanks for placing your order. We'll ship your goods as soon as possible. I don't need to make any code changes to integrate this view into the application because I already added the required statements when I defined the Checkout action method back in Listing 9-18. Now customers can go through the entire process, from selecting products to checking out. If they provide valid shipping details (and have items in their cart), they will see the summary page when they click the Complete order button, as shown in Figure 9-8. Figure 9-8. The thank-you page Summary I have completed all the major parts of the customer-facing portion of SportsStore. It might not be enough to worry Amazon, but I have a product catalog that can be browsed by category and page, a neat shopping cart, and a simple checkout process. The well-separated architecture means I can easily change the behavior of any piece of the application without worrying about causing problems or inconsistencies elsewhere. For example, I could process orders by storing them in a database, and it would not have any impact on the shopping cart, the product catalog, or any other area of the application. In the next chapter, I use two different techniques to create a mobile version of the SportsStore application. 254 CHAPTER 10 SportsStore: Mobile There is no escaping the popularity of devices such as smartphones and tablets, and if you want to deliver your application to the widest possible audience, you will have to embrace the world of mobile web browsers. If I sound less than enthusiastic, it is because the phrase mobile web browsers runs the gamut from fast, capable, and modern browsers that can rival a decent desktop browser right through to the slow, inconsistent, and outdated. The bottom line is that delivering a good experience to mobile users is hard—much harder than delivering content just to desktops. It takes careful planning, design and an enormous amount of testing—and even then it is easy to be caught out by a new smartphone or tablet. Putting Mobile Web Development in Context The MVC Framework does have some features that can help with mobile development. But the MVC Framework is a server-side framework that receives HTTP requests and generates HTML responses and there is a limited amount it can do to deal with the wide variation in capabilities that you will encounter when targeting mobile clients. The degree to which The MVC Framework can help depends on the mobile strategy that you have adopted. There are three basic mobile web strategies you can follow, which I describe the following sections. Note There is a fourth option, which is to create a native application, but I don’t discuss that here since it doesn’t directly involve the MVC Framework or, for that matter, web applications. Doing Nothing (Or As Little As Possible) It may seem like an odd idea to do nothing, but some mobile devices are capable of handling content that has been developed for desktop clients. Many—admittedly the most recent—have high-resolution, high-density displays with plenty of memory and browsers that can render HTML and run JavaScript quickly. If your app isn’t too demanding, you may find that many mobile devices won’t encounter any problems at all displaying your application content. As an example, Figure 10-1 shows how an iPad displays the SportsStore application without any modifications. 255 Figure 10-1. Displaying the SportsStore application on a tablet It does pretty well. The only issue is that the pagination links are off the bottom of the page, which is easily adjusted by changing the page layout or changing the number of products that are displayed on a page. Note The screenshots I show in this chapter are all obtained using browserstack.com, which is the cross-platform browser testing service I use for my own projects. It isn’t a perfect service. It is often slow to use at peak times, can be fragile from outside of the US, and the mobile devices are emulated. I use it mainly for the desktop browser support, which is more robust, but I get decent results and I don’t have to maintain my own set of emulators. You can get a free trial to follow the examples in this chapter and there are plenty of competitors out there if you want to go elsewhere. Note that I don’t have any relationship with Browser Stack other than as a regular customer, for which I pay full price and receive no special treatment. Using Responsive Design The next strategy is to create content that adapts to the capabilities of the device on which it is being displayed, known as responsive design. The CSS standard has features that let you change the styling applied to elements based on the capabilities of the device, a technique that is most frequently used to alter the layout of content based on screen width. Responsive design is something that is handled by the client using CSS and not directly managed by the MVC Framework. I go into the topic of responsive design in depth in my Pro ASP.NET MVC 5 Client book, but to give a demonstration how the 256 technique can be applied (and some considerations that do touch on the MVC Framework), I am going to use some responsive features that are included in the Bootstrap library, which I have been using to style the SportsStore application (and which has become one of the libraries Microsoft includes in the MVC 5 project templates for Visual Studio 2013). My goal will be to adjust the layout of the main part of the application so that it is visible on an iPhone. My “do nothing” strategy doesn’t pay off for this kind of device because it has a narrow screen, as Figure 10-2 shows. Figure 10-2. Displaying the SportsStore application on a smartphone I am going to tackle this problem in sections, focusing on different aspects of the layout. My goal is to preserve all of the functionality of the application, but presented in a different way. Note The MVC Framework isn’t an active participant in the responsive design. It sends all browsers the same content and lets them figure out which bits they need to display. This means that there is no sensible way to add unit tests for responsive design to a Visual Studio project. It is a technique that requires careful testing in the client and is difficult to automate. Creating a Responsive Header I am going to start with the page header, which contains the SportsStore name, the cart summary, and the Checkout button. Although the simplest solution would be to remove the SportsStore name and free up enough space for the rest of the content, I am going to keep it there (see the Accepting the Realities of Branding sidebar) and rearrange everything across two lines. ACCEPTING THE REALITIES OF BRANDING One of the easiest ways to free up screen real-estate is to remove your branding from the application. I am only displaying the SportsStore name as text, but you can see how much of the screen it occupies. What was a modest degree of branding on the desktop becomes a space hog on a smart phone. Removing branding is difficult, however. Not for technical reasons, but because most branding teams are obsessed with slathering branding on everything. It is the reason that there are company-branded pens in the boardroom, company-branded cups in the break room, and why you get business cards with new logos every 18 months. Companies rebrand so often because people who work in branding know, deep down, that they don’t have real jobs and the constant emphasis on logos and color schemes creates a frenzy of activity that distracts them from the creeping existential dread that haunts their every 257 waking moment. My advice is to accept that a certain amount of screen space will always be given over to branding, even on the smallest and least capable device. You might try to fight against the idea, but the branding team is usually part of the marketing department, marketing usually reports to the VP of Sales, and the VP of Sales has a hot line to the CEO because revenue is all that the board cares about. Some arguments just can’t be won. In Listing 10-1, you can see how I have adjusted the content of the header in the _Layout.cshtml file in the SportsStore.WebUI project. Listing 10-1. Adding Responsive Content to the _Layout.cshtml File @ViewBag.Title
@Html.Action("Menu", "Nav")
@RenderBody()
Bootstrap defines a set of classes that can be used to show or hide elements based on the width of the device screen. This is something you can do manually using CSS media queries, but the Bootstrap classes are integrated into the other styles. For the SportsStore branding, I have used the visible-xs and hidden-xs classes to switch to text on two lines that will be shown vertically when the window size is below 768 pixels. Bootstrap provides pairs of classes that show and hide elements at different browser window sizes, the names of which start with visible- or hidden-. The *–xs classes (i.e., visible-xs and hidden-xs) are the ones I used in the example. The *-sm classes work on windows wider than 768 pixels, the *-md classes work on windows wider than 992 pixels, and the *-lg classes work on windows wider than 258 1200 pixels. Caution Responsive CSS features like the ones that Bootstrap provide are based on the size of the browser window, not the device screen. Mobile device browsers are usually displayed full-screen, which means that the window size and the screen size are the same, but you can’t always rely on this being the case. As ever, you need to test against the devices you are targeting to ensure that you have not made assumptions that catch you out. You can see the effect of these changes by starting the application and viewing the product listing in a regular desktop browser, which has the advantage of letting you change the size of the window. Make the window smaller (less than 786 pixels), and you will see the SportsStore text break into two lines, as illustrated by Figure 10-3. Figure 10-3. Using Bootstrap responsive design features to adjust the application branding This may seem like a small change but it has a big impact on smaller screens, especially when combined with the changes I made to the Views/Cart/Summary.cshtml file, which is the view that provides the summary of the cart and its contents. You can see the changes I made in Listing 10-2. Listing 10-2. Adding Responsive Content to the Summary.cshtml File @model SportsStore.Domain.Entities.Cart This is the same technique that I applied to the _Layout.cshtml file, where I selectively show and hide content. In this 259 case, however, I hide the standard Checkout now button on small screens and replace it with an icon button, using one of the icons that is included in the Bootstrap package. The Bootstrap icons are applied through a span element, which means that I can’t use the Html.ActionLink helper method because it doesn’t present me with the ability to set the contents of the element it creates. Instead, I define the a element directly and use the Url.Action helper method (which I describe properly in Chapter 23) to generate a URL for the href attribute. The result is an a element with the same attributes that would be created by the Html.ActionLink method, but that contains a span element. You can see the effect of the changes I made to both files in Figure 10-4, which shows the header content displayed on the iPhone. Figure 10-4. The modified SportsStore header displayed on an iPhone simulator MOBILE FIRST VERSUS DESKTOP FIRST Most web application projects start with desktop clients and then add support for mobile clients, just as I am doing in this book. This is known as desktop first design/development and a common problem is that the server-side development has largely finished before work on the mobile client begins, resulting in an awkward mobile experience which is hacked out of features designed for more capable desktop clients. There is an alternative philosophy called mobile first design/development which, as the name suggests, starts with the mobile client as the foundation for the application and adds features to take advantage of more capable desktop browsers. Or to put it another way, desktop first tends to start with a full set of features and degrade gracefully for less capable devices, while mobile first tends to start with a smaller set of features that are gracefully enhanced for more capable devices. Both approaches have their merits and I tend to favor desktop first development because it is easy to get desktop browsers to load content from a local development workstation, something that can be surprisingly hard when working with real mobile hardware. I tend to work on a tight cycle of write-compile-check (which means reloading URLs into the browser frequently), and I get frustrated with the hoops one has to go through to get the same cycle going on a mobile device. The danger in putting any group of users first is that you create a substandard experience for another group, just moving the pain around. Proponents of mobile first design often argue that this can’t happen when you start with the basic features and then scale up, but that has not been my experience. It is important to have a solid plan for what functionality and layout you are going to deliver to all devices before you start developing for any of them. When you have such a plan, it doesn’t matter what kind of device you begin with and, critically, the server-side parts of the application will be built from the ground up to support a full range of clients. Creating a Responsive Product List To complete my responsive adaptations, I need a product list that will display on narrow devices. The biggest problem that I have is caused by the horizontal space that is taken up by the category buttons. I am going to move the buttons out of the way, giving individual product descriptions the whole width of the display. In Listing 10-3, you can see that I have further modified the _Layout.cshtml file. 260 Listing 10-3. Creating a Responsive Product List in the _Layout.cshtml File @ViewBag.Title
@RenderBody()
There can only be one call to the RenderBody method in a layout. I get into the details of layouts in Chapter 20, but the effect of this limit is that I can’t have duplicate sets of elements that I show and hide, each containing a RenderBody call. Instead, I need to change the layout of the grid that contains the RenderBody method call so that the elements in the layout adapt around the content from the view. One of the reasons that I used the Bootstrap grid to structure the content in the _Layout.cshtml file in Chapter 7 is that it includes some responsive design features that let me work around the RenderBody limitation. The Bootstrap grid layout supports 12 columns and you specify how many an element will occupy by applying a class, like this, which is how I applied the Bootstrap classes in Chapter 7: ...
@RenderBody()
... Much like the hidden-* and visible-* classes that I described earlier, Bootstrap provides a set of classes that set the number of columns that an element occupies in the grid based on the width of the window. The col-xs-* classes are fixed and don’t change based on the width of the screen. My use of the col-xs-8 class tells 261 Bootstrap that the div element should span 8 of the 12 available columns and that the visibility of the element should not change based on the width of the window. The col-sm-* classes set the columns when the window is 768 pixels or wider, the col-md-* classes work on windows that are 992 pixels or wider and, finally, the col-lg-* classes work on windows that are 1200 pixels or wider. With this in mind, here are the classes that I applied to the div element that surrounds the RenderBody method call from Listing 10-3: ...
@RenderBody()
... The effect of applying both classes is that the div element will occupy all 12 columns in the grid by default and 8 columns when the screen is 768 pixels or wider. The other columns in the grid contain the category buttons, as follows: ... ... This element will occupy 3 columns when the screen is wider than 768 pixels and be hidden otherwise. Combined with the other classes I applied, the effect is that the product descriptions fill small windows and share the available space with the category buttons for larger windows. You can see both layouts in Figure 10-5. I used a desktop browser to for this figure because I am able to easily vary the width of the window. Figure 10-5. Using a responsive grid in the product layout 262 Helping the Controller Select a View I don’t want to leave mobile users without the ability to filter products, which means that I need to present the categories in a different way. To do this, I created a new view called MenuHorizontal.cshtml in the Views/Nav folder with the content shown in Listing 10-4. Listing 10-4. The Contents of the MenuHorizontal.cshtml File @model IEnumerable
@Html.ActionLink("Home", "List", "Product", new { @class = "btn btn- default btn-sm" }) @foreach (var link in Model) { @Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 }, new { @class = "btn btn-default btn-sm" + (link == ViewBag.SelectedCategory ? " btn-primary" : "") }) }
This is a variation on the original Menu.cshtml layout, but with a container div element and some Bootstrap classes to create a horizontal layout of the buttons. The basic functionality, however, is the same. I generate a set of links which will filter the products by category. The set of category buttons is generated through the Menu action method of the Nav controller, which I need to update so that it selects the right view file based on the required orientation of the buttons, as shown in Listing 10-5. Listing 10-5. Updating the Menu Action Method in the NavController.cs File using System.Collections.Generic; using System.Web.Mvc; using SportsStore.Domain.Abstract; using System.Linq; namespace SportsStore.WebUI.Controllers { public class NavController : Controller { private IProductRepository repository; public NavController(IProductRepository repo) { repository = repo; } public PartialViewResult Menu(string category = null, bool horizontalLayout = false) { ViewBag.SelectedCategory = category; IEnumerable categories = repository.Products 263 .Select(x => x.Category) .Distinct() .OrderBy(x => x); string viewName = horizontalLayout ? "MenuHorizontal" : "Menu"; return PartialView(viewName, categories); } } } I have defined a new parameter for the action method that specifies the orientation, which I use to select the name of the view file passed to the PartialView method. To set the value of this parameter, I need to return to the _Layout.cshtml file, as shown in Listing 10-6. Listing 10-6. Updating the _Layout.cshtml File to Include the Horizontal Buttons ...
@Html.Action("Menu", "Nav", new { horizontalLayout = true })
@RenderBody()
... The optional third argument to the Html.Action method is an object that lets me set values for the routing system, which I explain in Chapters 15 and 16. I use this feature to signal which view the controller should select. The overall effect of these changes is shown in Figure 10-6. 264 Figure 10-6. The revised product listing for small screens You can see that moving the buttons to the top of the product listing creates enough space for each product to be displayed properly. I could continue improving the fit and finish of the views, but you get the idea. Aside from a brief demonstration of how responsive CSS classes can be used, I wanted to touch upon some of the limitations that the MVC Framework imposes (such as the RenderBody method limit) and some of the facilities it can provide to assist generating content in different ways (such as passing data from a view to a controller via the routing system and the Html.Action helper method). Note I have focused on one orientation for the iPhone, but don’t forget that most mobile devices allow for multiple orientations and that you will have to cater for them all in a real project. Removing View Duplication In the previous example, I wanted to show you how you can have a controller select a view based on routing information passed by a call to the Html.Action helper method. It is an important and useful feature, but I would not have used it in a real project because it leaves me with two views, Menu.cshtml and MenuHorizontal.cshtml, that contain largely similar markup and Razor expressions. This is a maintenance risk because any changes that I require to the category filter buttons will have to be applied in two places. To resolve this I am going to consolidate the views. I created a new view file called FlexMenu.cshtml in the Views/Nav folder with the content shown in Listing 10-7. Listing 10-7. The Contents of the FlexMenu.cshtml File @model IEnumerable @{ bool horizontal = ((bool)(ViewContext.RouteData.Values["horizontalLayout"] ?? false)); string wrapperClasses = horizontal ? "btn-group btn-group-sm btn-group- justified" : null; } 265
@Html.ActionLink("Home", "List", "Product", new { @class = horizontal ? "btn btn-default btn-sm" : "btn btn-block btn-default btn-lg" }) @foreach (var link in Model) { @Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 }, new { @class = (horizontal ? "btn btn-default btn-sm" : "btn btn-block btn-default btn-lg" ) + (link == ViewBag.SelectedCategory ? " btn-primary" : "") }) }
The cost of removing duplication is a more complex view that can generate both orientations of buttons and it is a matter of personal preference as to which approach you take. If you are like me and prefer to avoid duplication, then this listing shows several useful features you can apply to views. The first is the ability to access routing information directly from the view. The ViewContext property provides information about the current state of the request that is being processed, including details of the routing information, as follows: ... bool horizontal = ((bool)(ViewContext.RouteData.Values["horizontalLayout"] ?? false)); ... The second feature is the ability to create local variables within a view. This is possible because of the way that Razor views are compiled into classes (which I describe in Chapter 20), and I have created a local variable called horizontal that means I don’t have to check the route data throughout the listing to figure out which orientation the view is being used for. Caution Local variables should be used sparingly because it is the slippery slope into creating views that are hard to maintain and hard to test, but I sometimes use them in situations like this where I see them as an acceptable cost of simplifying a view. A related feature is the way that Razor will conditionally set attributes based on variables. I defined a string of class names as a local variable in the view like this: ... string wrapperClasses = horizontal ? "btn-group btn-group-sm btn-group- justified" : null; ... The value of the wrapperClasses variable is either the string of class names that I used for horizontal layouts or null. I apply this variable to the class attribute like this: ...
... When the variable is null, Razor is smart enough to remove the class attribute from the div element entirely, generating an element like this: 266
When the variable is not null, Razor will insert the value and leave the class attribute intact, producing a result like this:
This is a nice way of matching the characteristics of the C# with the semantics of HTML and is a feature that is endlessly useful when writing complex views because it won’t insert null values into attributes and it won’t generate empty attributes, which can cause problems with CSS selectors (and JavaScript libraries that use attributes to select elements, such as jQuery). Note Conditional attributes will work on any variable, not just the ones you have defined in the view. This means that you can apply this feature to model properties and the view bag. To use my consolidate view, I need to revise the Menu action method in the Nav controller, as shown in Listing 10-8. Listing 10-8. Updating the Menu Action in the NavController.cs File ... public PartialViewResult Menu(string category = null) { ViewBag.SelectedCategory = category; IEnumerable categories = repository.Products .Select(x => x.Category) .Distinct() .OrderBy(x => x); return PartialView("FlexMenu", categories); } ... I removed the parameter that receives the orientation and changed the call to the PartialView method so that the FlexMenu view is always selected. The result of these changes doesn’t alter the layout of the content or the effect of the responsive design, but it does remove the duplication in the views and means that you can delete the Menu.cshtml and MenuHorizontal.cshtml views from the Visual Studio project. Both orientations of category filter button are now produced by the FlexMenu.cshtml view. THE LIMITATIONS OF RESPONSIVE DESIGN There are some problems with responsive design as a way to support mobile clients. The first is that you end up duplicating a lot of content and sending it to the server so that it can be displayed in different scenarios. You saw this in the previous section when the HTML generated by the layout contained multiple sets of elements for the page header and the category filter buttons. The extra elements don’t amount to much on a per-request basis, but the overall effect for a busy application is a sharp increase in the amount of bandwidth you will need to provision, with the corresponding increase in running costs. The second problem is that responsive design can be fiddly and it requires endless testing to get right. Not all devices handle the underlying CSS features that enable responsive design (known as media queries) properly and, unless you are thorough and careful, you will end up with an application that delivers an adequate experience on every device without excelling on any of them, a kind of blandness that comes from averaging out all of the device quirks. Responsive design can be useful when applied thoughtfully, but it can easily result in an application that is riddled with compromises that don’t deliver a good experience for any of your target users. Creating Mobile Specific Content Responsive design delivers the same content to all devices and uses CSS to figure out how that content should be presented, a process that doesn’t involve the server-side part of the application and which assumes that you want to treat all devices as being 267 variations on the same basic theme. An alternative approach is to use the server to assess the capabilities of the client browser and send different HTML to different kinds of client. This works well if you want to present a completely different aspect of the application on the desktop to, say, a tablet. Note You don’t have to choose between responsive design and mobile-specific content and, in most projects, you’ll need to use both to get a good result on the devices you target. As an example, you may decide to create content specifically for tablets and use responsive design to create the horizontal and vertical orientations that most tablets support. The MVC Framework supports a feature called display modes, which allows you to create different views that are delivered based on the client that has made the request, a feature provided by the ASP.NET Framework. I explain how you can create and manage display modes in depth in my Pro ASP.NET MVC 5 Platform book, but for the SportsStore application, I am going to use the simplest form of display modes, which is to treat all mobile devices as being the same. My goal will be to deliver an experience to mobile devices using the popular jQuery Mobile library, while keeping the existing content for desktop devices. Note I am not going to go into any detail about jQuery Mobile in this book, other than to demonstrate how it can be used to deliver mobile-specific content. For full details of jQuery Mobile, see my Pro jQuery 2.0 book, published by Apress. Creating a Mobile Layout All I have to do to create mobile-specific content is to create views and layouts that have a .Mobile.cshtml suffix. I created a new layout called _Layout.Mobile.cshtml in the Views/Shared folder with the content shown in Listing 10-9. Note Because the name of the view contains an additional period, you will need to create the view by right-clicking the Shared folder and selecting Add MVC 5 Layout Page (Razor) from the pop-up menu. Listing 10-9. The Contents of the _Layout.Mobile.cshtml File @ViewBag.Title

SportsStore

@Html.Action("Menu", "Nav")
    @RenderBody()
268 This layout uses jQuery Mobile, which I obtain using a content delivery network (CDN) so that I don’t have to install a NuGet package for the JavaScript and CSS files I need. Note I am just scratching the surface by creating mobile-specific views, because I am using the same controllers and action methods that are used for desktop clients. Having separate views allows you to introduce different controllers, which are particular to a set of clients, and this can be used to create totally different features and functionality for different types of client. The MVC Framework will automatically identify mobile clients and use the _Layout.Mobile.cshtml file when it is rendering views, seamlessly replacing the _Layout.cshtml file which is used for other clients. You can see the impact of the change in Figure 10-7. Figure 10-7. The effect of creating a mobile layout in the SportsStore application You can see that the layout is different, but the overall effect is a mess, and that’s because I need to create mobile versions of the main view that is handling the request and the partial view that is used for the category filtering buttons. Creating the Mobile Views I am going to start with the category filtering, which means creating a view called FlexMenu.Mobile.cshtml in the Views/Nav folder with the content shown in Listing 10-10. Listing 10-10. The Contents of the FlexMenu.Mobile.html File @model IEnumerable
    @foreach (var link in Model) {
  • @Html.RouteLink(link, new { controller = "Product", action = "List", category = link, page = 1 }, new { data_transition = "fade", @class = (link == ViewBag.SelectedCategory ? "ui-btn-active" : null) }) 269
  • }
This view uses a Razor foreach expression to generate li elements for the product categories, producing elements that are organized in the way that jQuery Mobile expects for a navigation bar at the top of the page. You can see the effect in Figure 10-8. Figure 10-8. The effect of creating a mobile-specific view Note jQuery Mobile relies on the use of data attributes to format elements. Data attributes are prefixed with data- and were an unofficial way of defining custom attributes for years before becoming an official part of the HTML5 standard. In the listing, I needed to add a data-transition attribute to the li elements, but I can’t use data-transition as the property name for the anonymous object because this would be a C# expression. The problem is the hyphen and Razor works around this by translating underscores in the property names to hyphens in attribute names, such that I was able to use data_transition in the listing and get a data-transition attribute on the elements I generate. The product information is still a mess, but the category buttons are now being generated by the new mobile-specific view. It is worth taking a moment to reflect on what the MVC Framework is doing to render the content in Figure 10-8. The HTTP request from the browser targets the List action method in the Product controller, which tells the MVC Framework to render the List.cshtml view file. The MVC Framework knows that the request came from a mobile browser and so it starts looking for mobile-specific views. There is no List.Mobile.cshtml, and so the List.cshtml file is processed instead. This view relies on the _Layout.cshtml file, but the MVC Framework notices that there is a mobile-specific version available and so it uses _Layout.Mobile.cshtml instead. The layout requires the FlexMobile.cshtml file but there is a mobile version of that as well, and so on. The result is that the response to the browser is generated from a mix of mobile-specific and general views, with the MVC Framework using the most specific view file available, but seamlessly falling back when needed. THE TWO PROBLEMS IN THE EXAMPLE The example in this chapter is intended to demonstrate the way that the MVC Framework can deliver mobile-specific content, but I would be remiss if I didn’t point out two potentially serious problems that this example introduces to the SportsStore application. The first is that I have provided less functionality in my mobile views than in the desktop ones. There is no cart summary in the page header, for example. I left some features out to simplify the changes I had to make, but I recommend against delivering reduced functionality to any device unless there is a technical limitation that prevents the device from being able to support it. Mobile devices are increasingly capable and many users will only access your application with a mobile browser. The days when you could assume that mobile access was a supplement to desktop use have passed. 270 The second problem is that I have not offered the user the chance to switch back to the desktop layout. Don’t underestimate the number of users that would prefer to have the desktop layout on a mobile device, even though it might be a little awkward and require some zooming and scrolling on smaller screens. Some mobile devices allow larger monitors to be connected, for example, and this is rarely detected by the mechanism that the ASP.NET Framework uses to identify mobile devices. You should always give mobile users the choice about which layout they receive. Neither of these issues prevents me from deploying my application, but they are the kind of frustration that plagues mobile web application users. Mobile devices will be a big part of any modern web application and you should take every precaution to deliver a good user experience to this important category of users. My last change is to create a mobile-specific version of the view that generates the product summary. I created a view file called ProductSummary.Mobile.cshtml in the Views/Shared folder with the contents shown in Listing 10- 11. Listing 10-11. The Contents of the ProductSummary.Mobile.cshtml File @model SportsStore.Domain.Entities.Product

@Model.Name

@Model.Description
(@Model.Price.ToString("c"))
@using (Html.BeginForm("AddToCart", "Cart")) { @Html.HiddenFor(x => x.ProductID) @Html.Hidden("returnUrl", Request.Url.PathAndQuery) }
This view uses a jQuery Mobile widget that allows users to open and collapse regions of content. It isn’t an ideal way of presenting product information, but it is simple and my emphasis in this section is on mobile-specific content rather than the jQuery Mobile library. You can see the effect of this new view in Figure 10-9. 271 Figure 10-9. The effect of mobile-specific views In a real project, I would carry on, of course, and create mobile-specific versions of the views that display the pagination links, the shopping cart and the checkout form. I am not going to because you have already seen how the MVC Framework lets you target mobile devices. Summary In this chapter I have shown you two techniques for handling mobile devices: responsive design and mobile-specific content. Responsive design isn’t directly related to the MVC Framework, which sends the same content to all browsers and lets them figure out what to do with it. But as I demonstrated, there are some limitations in the way that views work that require careful thought and some nice Razor features that can ease the overall process. Creating mobile-specific content is something that the MVC Framework does actively participate in by automatically applying mobile-specific views and layouts if they are available and blending them seamlessly into the process that renders HTML for the clients. In the next chapter, I add the basic features required to administer the SportsStore product catalog. 272 CHAPTER 11 SportsStore: Administration In this chapter, I continue to build the SportsStore application in order to give the site administrator a way of managing the product catalog. I will add support for creating, editing, and removing items from the product repository, as well as for uploading and displaying images alongside products in the catalog. Adding Catalog Management The convention for managing collections of items is to present the user with two types of pages: a list page and an edit page, as shown in Figure 11-1. Figure 11-1. Sketch of a CRUD UI for the product catalog Together, these pages allow a user to create, read, update, and delete items in the collection. Collectively, these actions are known as CRUD. Developers need to implement CRUD so often that Visual Studio tries to help by offering to generate MVC controllers that have action methods for CRUD operations and view templates that support them. But like all of the Visual Studio templates, I think it is better to learn how to use the features of the MVC Framework directly. Creating a CRUD Controller I am going to create a new controller for the SportsStore administration features. Right-click on the Controllers folder for the SportsStore.WebUI project in the Solution Explorer and select Add Controller from the pop-up menu. Select MVC 5 Controller – Empty from the list of options, click the Add button, set the name to AdminController and click the Add button to create the Controllers/AdminController.cs file. Edit the contents of the controller class to match Listing 11-1. Listing 11-1. The Contents of the AdminController.cs File using System.Web.Mvc; using SportsStore.Domain.Abstract; namespace SportsStore.WebUI.Controllers { 273 public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { repository = repo; } public ViewResult Index() { return View(repository.Products); } } } The controller constructor declares a dependency on the IProductRepository interface, which Ninject will resolve when instances are created. The controller defines a single action method, Index, that calls the View method to select the default view for the action, passing the set of products in the database as the view model. UNIT TEST: THE INDEX ACTION The behavior that I care about for the Index method of the Admin controller is that it correctly returns the Product objects that are in the repository. I can test this by creating a mock repository implementation and comparing the test data with the data returned by the action method. Here is the unit test, which I placed into a new unit test file called AdminTests.cs in the SportsStore.UnitTests project: using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.WebUI.Controllers; using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace SportsStore.UnitTests { [TestClass] public class AdminTests { [TestMethod] public void Index_Contains_All_Products() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, }); // Arrange - create a controller AdminController target = new AdminController(mock.Object); // Action Product[] result = ((IEnumerable)target.Index(). ViewData.Model).ToArray(); // Assert 274 Assert.AreEqual(result.Length, 3); Assert.AreEqual("P1", result[0].Name); Assert.AreEqual("P2", result[1].Name); Assert.AreEqual("P3", result[2].Name); } } } Creating a New Layout I am going to create a new layout to use with the SportsStore administration views. It will be a simple layout that provides a single point where I can apply changes to all the administration views. Create the new layout, by right-clicking the Views/Shared folder in the SportsStore.WebUI project and select Add > MVC 5 Layout Page (Razor) from the pop-up menu. Set the name to _AdminLayout.cshtml (don’t forget the underscore) and click the OK button to create the Views/Shared/_AdminLayout.cshtml file. Set the contents of the new view to match Listing 11-2. Note As I explained previously, the convention is to start the layout name with an underscore (_). Razor is also used by another Microsoft technology called WebMatrix, which uses the underscore to prevent layout pages from being served to browsers. MVC does not need this protection, but the convention for naming layouts is carried over to MVC applications anyway. Listing 11-2. The Contents of the _AdminLayout.cshtml File @{ Layout = null; }
@RenderBody()
I have added a call to the RenderBody method, so that the contents of the view that the layout is being used for will be inserted into the response to the server. (I would not have had to do this if I had used the Add New Item menu option and used the Visual Studio layout template, but I took a shortcut to create the view directly, which meant I had to edit the new file to get the content I require.) I also added link elements for the Bootstrap files and for the CSS file that contains the styles I created to highlight validation errors to the user. Implementing the List View Now that I have the new layout, I can add a view to the project for the Index action method of the Admin controller. Even though I am not a fan of the Visual Studio scaffold and template features, I am going to create the view for the Index method using the scaffold system so you can see how it works. Just because I don’t like pre-cut code, doesn’t mean that you shouldn’t use it. 275 Right-click on the Views/Admin folder in the SportsStore.WebUI project and select Add View from the menu. Set View Name to Index, select the List for the Template option (this is where I usually select one of the Empty options), select Product as the Model Class, check the option to use a layout page, and select the _AdminLayout.cshtml file from the Views/Shared folder. You can see all of the configuration options in Figure 11-2. Figure 11-2. Configuring a scaffold view Note When using the List scaffold, Visual Studio assumes you are working with an IEnumerable sequence of the model view type, so you can just select the singular form of the class from the list. Click the Add button to create the new view, which will have the contents shown in Listing 11-3. (I have formatted the markup so that it doesn’t require so much space on the page.) Listing 11-3. The Contents of the Views/Admin/Index.cshtml File @model IEnumerable @{ ViewBag.Title = "Index"; Layout = "∼/Views/Shared/_AdminLayout.cshtml"; }

Index

@Html.ActionLink("Create New", "Create")

276 @foreach (var item in Model) { }
@Html.DisplayNameFor(model => model.Name)@Html.DisplayNameFor(model => model.Description) @Html.DisplayNameFor(model => model.Price) @Html.DisplayNameFor(model => model.Category)
@Html.DisplayFor(modelItem => item.Name) @Html.DisplayFor(modelItem => item.Description) @Html.DisplayFor(modelItem => item.Price) @Html.DisplayFor(modelItem => item.Category) @Html.ActionLink("Edit", "Edit", new { id=item.ProductID }) | @Html.ActionLink("Details", "Details", new { id=item.ProductID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ProductID })
Visual Studio looks at the type of view model object and generates elements in a table that correspond to the properties defined by the model type. You can see how this view is rendered by starting the application and navigating to the /Admin/Index URL. Figure 11-3 shows the results. 277 Figure 11-3. Rendering the scaffold List view The scaffold view makes a reasonable attempt at setting up a sensible baseline for the view. I have columns for each of the properties in the Product class and links for the other CRUD operations that target action methods in the Admin controller (although, since I created that controller without using scaffolding, the action methods do not exist). The scaffolding is clever, but the views that it generates are bland and so general as to be worthless in a project of any complexity. My advice is to start with empty controllers, views and layouts and add the functionality you need as and when you need it. Returning to the do-it-yourself approach, edit the Index.cshtml file so that it corresponds to Listing 11-4. Listing 11-4. Modifying the Index.cshtml View @model IEnumerable @{ ViewBag.Title = "Admin: All Products"; Layout = "∼/Views/Shared/_AdminLayout.cshtml"; 278 }

All Products

@foreach (var item in Model) { }
ID Name Price Actions
@item.ProductID @Html.ActionLink(item.Name, "Edit", new { item.ProductID }) @item.Price.ToString("c") @using (Html.BeginForm("Delete", "Admin")) { @Html.Hidden("ProductID", item.ProductID) }
This view presents the information in a more compact form, omitting some of the properties from the Product class and using Bootstrap to apply styling. You can see how this view renders in Figure 11-4. 279 Figure 11-4. Rendering the modified Index view Now I have a nice list page. The administrator can see the products in the catalog and there are links or buttons to add, delete, and inspect items. In the following sections, I will add the functionality to support each of these actions. Editing Products To provide create and update features, I will add a product-editing page similar to the one shown in Figure 11-1. There are two parts to this job: Display a page that will allow the administrator to change values for the properties of a product Add an action method that can process those changes when they are submitted Creating the Edit Action Method 280 Listing 11-5 shows the Edit method I added to the Admin controller. This is the action method I specified in the calls to the Html.ActionLink helper method in the Index view. Listing 11-5. Adding the Edit Action Method in the AdminController.cs File using System.Linq; using System.Web.Mvc; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.WebUI.Controllers { public class AdminController : Controller { private IProductRepository repository; public AdminController(IProductRepository repo) { repository = repo; } public ViewResult Index() { return View(repository.Products); } public ViewResult Edit(int productId) { Product product = repository.Products .FirstOrDefault(p => p.ProductID == productId); return View(product); } } } This simple method finds the product with the ID that corresponds to the productId parameter and passes it as a view model object to the View method. UNIT TEST: THE EDIT ACTION METHOD I want to test for two behaviors in the Edit action method. The first is that I get the product I ask for when I provide a valid ID value. Obviously, I want to make sure that I am editing the product I expected. The second behavior is that I do not get any product at all when I request an ID value that is not in the repository. Here are the test methods I added to the AdminTests.cs unit test file: ... [TestMethod] public void Can_Edit_Product() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, }); // Arrange - create the controller AdminController target = new AdminController(mock.Object); 281 // Act Product p1 = target.Edit(1).ViewData.Model as Product; Product p2 = target.Edit(2).ViewData.Model as Product; Product p3 = target.Edit(3).ViewData.Model as Product; // Assert Assert.AreEqual(1, p1.ProductID); Assert.AreEqual(2, p2.ProductID); Assert.AreEqual(3, p3.ProductID); } [TestMethod] public void Cannot_Edit_Nonexistent_Product() { // Arrange - create the mock repository Mock mock = new Mock(); mock.Setup(m => m.Products).Returns(new Product[] { new Product {ProductID = 1, Name = "P1"}, new Product {ProductID = 2, Name = "P2"}, new Product {ProductID = 3, Name = "P3"}, }); // Arrange - create the controller AdminController target = new AdminController(mock.Object); // Act Product result = (Product)target.Edit(4).ViewData.Model; // Assert Assert.IsNull(result); } ... Creating the Edit View Now that I have an action method, I can create a view for it to render. Right-click on the Views/Admin folder in the Solution Explorer and select Add MVC 5 View Page (Razor) from the menu. Set the name to Edit.cshtml, click the button to create the file and edit the contents to match Listing 11-6. Listing 11-6. The Contents of the Edit .cshtml File @model SportsStore.Domain.Entities.Product @{ ViewBag.Title = "Admin: Edit " + @Model.Name; Layout = "∼/Views/Shared/_AdminLayout.cshtml"; }

Edit @Model.Name

@using (Html.BeginForm()) { @Html.EditorForModel() @Html.ActionLink("Cancel and return to List", "Index") } 282 Instead of writing out markup for each of the labels and inputs by hand, I have called the Html.EditorForModel helper method. This method asks the MVC Framework to create the editing interface for me, which it does by inspecting the model type—in this case, the Product class. To see the page that is generated from the Edit view, run the application and navigate to /Admin/Index. Click one of the product name links, and you will see the page shown in Figure 11-5. Figure 11-5. The page generated using the EditorForModel helper method Let’s be honest: the EditorForModel method is convenient, but it does not produce the most attractive results. In addition, I do not want the administrator to be able to see or edit the ProductID attribute, and the text box for the Description property is far too small. I can give the MVC Framework directions about how to create editors for properties by using model metadata. This allows me to apply attributes to the properties of the new model class to influence the output of the Html.EditorForModel method. Listing 11-7 shows how to use metadata on the Product class in the SportsStore.Domain project. Listing 11-7. Using Model Metadata in the Product.cs File using System.ComponentModel.DataAnnotations; using System.Web.Mvc; namespace SportsStore.Domain.Entities { public class Product { [HiddenInput(DisplayValue = false)] public int ProductID { get; set; } public string Name { get; set; } [DataType(DataType.MultilineText)] public string Description { get; set; } public decimal Price { get; set; } 283 public string Category { get; set; } } } The HiddenInput attribute tells the MVC Framework to render the property as a hidden form element, and the DataType attribute allows me to specify how a value is presented and edited. In this case, I have selected the MultilineText option. The HiddenInput attribute is part of the System.Web.Mvc namespace and the DataType attribute is part of the System.ComponentModel.DataAnnotations namespace, which is why I had you add references to the assemblies for these namespace to the SportsStore.Domain project in Chapter 7. Figure 11-6 shows the Edit page once the metadata has been applied. You can no longer see or edit the ProductId property, and there is a multiline text box for entering the description. However, the overall appearance is poor. Figure 11-6. The effect of applying metadata The problem is that the Html.EditorForModel helper doesn’t know anything about the Product class and generates some basic and safe HTML. There are three ways we can deal with this. The first is to define CSS styles for the content that the helper generates, which is made easier by the classes that are automatically added to the HTML elements by the MVC Framework. If you look at the source for the page shown in Figure 11-6, you will see that the textarea element that has been created for the product description has been assigned to the text-box-multi-line CSS class: