Showing posts with label MVC3. Show all posts
Showing posts with label MVC3. Show all posts

Sunday, 18 December 2011

MVC3 EntityFramework POCO StructureMap

The purpose of this post is to explain how to setup an MVC3 solution which uses Entity Framework POCO as an Object-Relational Mapping (ORM) tool and StructureMap as an Inversion of Control (IoC) tool.

Step 1 - New MVC 3 Project

There are plenty of resources available to show you how to start a new MVC 3 Project in Visual Studio 2010 so i won't go into that...

Step 2 - Add Entity Framework 4.1 as your ORM

There are a few different ways to do this but i use the NuGet package which is pretty straight forward.

  1. Open your MVC3 project in VS2010
  2. Tools > Library Package Manager > Manage NuGet packages
    • Click 'Online' in the left hand menu so you can search for new packages
    • Search for 'Entity Framework' - at the time of writing, version 4.2 was available but you can also go direct to the nuget site and some previous versions may be available, for instance, i'm using version 4.1.10331.0
    • See http://nuget.org/packages/entityframework
  3. Simply click 'Install' button and the framework will be installed your machine for future use
  4. Now you have EF installed, right-click on the 'Models' folder (or where-ever you want your .edmx file to end up) in your solution and choose Add > New item
    • Under the 'Data' heading, choose 'ADO.Net Entity Data Model'
    • Follow the prompts accordingly...use Google for more info ;-)
Step 3 - Install and configure StructureMap

  1. You can get the latest version of StructureMap from here: http://sourceforge.net/projects/structuremap/ 
  2. The easiest way to install and configure Structure Map though, is to use the NuGet package manager again
  3. Similarly to step 2.2 above, search for 'structure' within the Online NuGet packages and you should find one (at the time of writing) called 'StructureMap.MVC3'
    • Be sure not to confuse this with another very similar package called 'StructureMap-MVC3' which is now deprecated
to be continued...

Wednesday, 19 October 2011

MVC 3 Html.DropDownList - How to Post Data back to an Action Method

To assign a Hazard to a Worksite Location, we need a drop down list (DDL) of Hazard Types to choose from.

By default, when you create the Controller/Views for the WORK_SITE_HAZARD type, ASP.Net MVC will define a SelectList of HAZARD_TYPE objects and throw it in the ViewBag so the Create.cshtml view can access it and assign it to a Html.DropDownFor …

            ViewBag.HAZARD_TYPE_ID = new SelectList(db.HAZARD_TYPES, "HAZARD_TYPE_ID", "HAZARD_TYPE_DESC");

Because I’m coming at ASP.Net MVC with fresh eyes, I couldn’t help but think: “this is stupid, this name implies that it is putting the ID of a single HAZARD_TYPE in the ViewBag”

So I renamed it to HAZARD_TYPES and updated the view so that the Html.DropDownFor would reference a collection called “HAZARD_TYPES”…makes much more sense!

However, when I then go to post the data back to the Create(WORK_SITE_HAZARD wsh) action method (which expects an instantiated WORK_SITE_HAZARD object) the HAZARD_TYPE_ID is not populated.

This is because the internals that instantiate the WORK_SITE_HAZARD object for me is, by convention, looking for an element on the form with id=”HAZARD_TYPE_ID”.

It doesn’t find this though because the Html.DropDownFor helper sets the ID of the resulting <select> element to be the string you give it in the constructor, the “Name” property – which I have changed to “HAZARD_TYPES” because it is more semantic.

The second convention is, when rendering the Html.DropDownFor, the framework will look in the ViewBag for any object with key equal to the string you pass as the “Name” parameter into the Html.DropDownFor constructor.

So if you were to change the name you pass into the Html.DropDownFor constructor, to say  ‘ddlHazardTypes’, you’ll get:

”There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'ddlHazardTypes'”

So, just before I was going to give into their, in my opinion, stupid convention: I found an overloaded version of the Html.DropDownFor constructor:

@Html.DropDownList("HAZARD_TYPE_ID", (SelectList)ViewBag.HAZARD_TYPES ,String.Empty, null)

This allows me to give the <select> element the an ‘id’ attribute of “HAZARD_TYPE_ID”, which the framework needs to instantiate the WORK_SITE_HAZARD object, populate it with the selected HAZARD_TYPE and pass it back to the Create action method

AND

ensure the list to which the DropDownList is bound, is called something meaningful, eg “HAZARD_TYPES”

Monday, 17 October 2011

HTML.DropDownList Partial View selectedItem WebGrid

I have a partial view which renders a list of child entities for the main view/entity.

EG A Student View, bound to a StudentModel. Each student has a bunch of courses. Each course has a Score attribute.

So the Student page should have a Grid of Courses. In each row of the grid, which represent a course, there should be a drop down list allowing the user to change the Score for the course, for that Student (don't let the Students get to this page!).

Getting this working in MVC proved a little challenging.

I used a Partial View within the StudentView. The Partial View was bound to an ICollection<COURSE> object - this is done via the call

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>


Now the Model for the Partial View is a collection of Courses.

So we want to render a table, with each row having a drop down list bound to a collection of SCORE objects (eg HighDistinction, Distinction, Credit, Pass, Fail) and make sure that when the page loads, each COURSE has the correct SCORE pre-selected, if any.

There are a few tricks to this:
  1.  Define the collection of objects that you want to be displayed in the drop down list within the Controller of the main entity, eg the StudentController, and put it in the ViewBag so that the partial view can access it:

    ViewBag.SCORE_TYPES = GetScoreTypes(); // this returns IEnumerable<SCORE_TYPE>
  2. Define a "SelectList" object within the Partial View, passing into it the collection of SCORE_TYPES you defined in the Controller. Make sure you declare the forth parameter, this is what tells .NET which field of your Model is to be used to determine the 'SelectedValue' upon initial page load:

    new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID);
    
  3. Then assign "SelectList" to the HTML.DropDownList helper. 
You can do all this with a WebGrid object or manually marking up the table yourself.
Using a WebGrid (this is the code for the Partial View)

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>

@{
    var grid = new WebGrid(Model, canPage: false, canSort: true, defaultSort: "", selectionFieldName: "editCourse");
}
@grid.GetHtml(
tableStyle: "FullWidth",
columns:
grid.Columns(
    grid.Column(format: (item) => Html.DropDownList("nameOfDDL",new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID), "Pick a score...")),
    grid.Column("STUDENT_COURSE_DESC", header: "Course Description")    
    ))

Using a Manual Markup (this is the code for the Partial View)

@model ICollection<MYUNI_BUSINESS_DOMAIN.COURSE>
 
<table class="FullWidth">
    <tr>
        <th>Score Type</th>
        <th>Course Description</th>
        <th></th>
    </tr>
@{               
    foreach (var item in Model)
    {
        var selectList = new SelectList(ViewBag.SCORE_TYPES, "SCORE_TYPE_ID", "SCORE_TYPE_DESC", item.SCORE_TYPE_ID);
        <tr>
            <td>
            @Html.DropDownList("nameOfDDL", selectList, "Pick a score...")
            </td>
            <td>@item.STUDENT_COURSE_DESC</td>
        </tr>        
    }      
}
</table>

Friday, 14 October 2011

Using Data Annotations and Validators with the Entity Framework

After a lot of searching around, i finally found the answer.

Because when you use an ORM like Entity Framework, your Model objects will often get clobbered whenever you change the Model in the Designer and it re-generates your classes for you.

As a result of this, you cannot apply any Data Annoation directly to the generated classes.

This post and this one by Scott Gu explains that the technique you need to employ is 'buddy classes'.

What i didn't understand is: what was the point of applying the MetadataType attribute to the generated class??? It would just get over-written as well!

What i think Scott fails to make clear is: you need to add a new, seperate, partial class and apply the MetadataType to that (the new class is 'partial to' the generated Entity class) - DO NOT apply the MetadataType to the generated class!

All that's mentioned is:

"...apply a “MetadataType” attribute to a partial class that is compiled with the tool-generated class"

Example speaks a thousand words:

Lets say we're using Database First approach (or Model first) and we have a Person entity which is generated automatically for us every time we update the *.EDMX file in the Visual Studio Data Model Designer.

So we need to add a new partial class called 'Person' to the solution (making sure it is in the same namespace as the generated 'Person' class)

Right click on the Project > Add > Class

Call it 'Person.cs'

Change the declaration of the class to have the 'partial' keyword.

Add another class to the same file (or a new physical file, doesn't matter) - lets call it Person_Metadata.

Then add the MetadataType attribute to the new partial Person class.

My Person.cs file now looks like:

namespace BusinessDomain
{
    [MetadataType(typeof(Person_Metadata))] 
    partial class Person
    {
        // You can put custom business logic in here too!
    }
    class Person_Metadata
    {
        [Required]
        public object Name
    }
}


VIOLIA! The Name property of the Person object now has the 'Required' attribute applied to it and this WILL NOT be over-written when you update your Model via the Designer.

NOTE: The return type of the 'Name' Property is 'object' because it allows you to change the Type of the Property within the Model without having to update the Person_Metadata class eg if you wanted to change Nullable Boolean to Boolean, etc. You should retain this strategy for all properties you put in the Metadata class.

You can continue to add various Properties to the Person_Metadata class and assign them various Data Annotations.

Customising MVC View Rendering EditorTemplates | Display Templates | UIHint

There are a myriad of ways to customise the HTML output of an MVC View.

A simple example is the rendering of a Model.DateTime property (eg Person.DateOfBirth) - such fields are often persisted within the Data Source (a SQL/Oracle database or XML file for example) as a DateTime type.

But sometimes you don't want the Time component to display on the UI, as is the the default behaviour when you have View markup (with Razor syntax) like this:

<div class="editor-label">
            @Html.LabelFor(model => model.DATE_REVISED)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.DATE_REVISED, "_DateTime")
            @Html.ValidationMessageFor(model => model.DATE_REVISED)
        </div>

This post, under the heading 'Specifying Model Display Format' explains some of the various ways you can change this behaviour with MVC3.

One of the ways i was looking into was the use of the UIHint attribute.

What i didn't realise is that you can't just put a Partial View in the View/Shared folder, it needs to be in the Views/Shared/EditorTemplates for your 'Edit' views and in Views/Shared/DisplayTemplates for your 'Details' views.

You gota know the Convention man - it's all Convention over Configuration - thats the name of the ASP.Net MVC game (and MVC in general)!

The other Convention which is good to know:

If you name the View with the same name as the Type which comprises the View's Model

eg
@model System.DateTime
 
// More View markup which determines the how the Model will be rendered 

needs a View named 'dateTime', 'DateTime' or '_DateTime' (appears Convention is that the View name does not have to match the case formatting of the type name and it can have an underscore at the beginning - any exceptions to this and .Net will not match the Partial View to the type).

and then .Net will pick it up automatically, even without the [UIHint] annotation on the Model Property.

This is good news if you're Models are being generated automatically via Entity Framework!

NOTE: Having a Partial View over-ride the rendering of type System.DateTime will make EVERY DateTime property, application wide, render using the mark-up contained in that Partial View! 

[ie if you have to add heaps of Annotations, and then need to re-generate, all your customising will be over-ridden!]

Tuesday, 11 October 2011

HTML Helper: Html.BeginForm

@using (Html.BeginForm())

This generates a URL for the Form to send the request to and it will refer to the CURRENT action = that is, the one that processed the current request.

Useful where you want the same URL to handle GET and POST requests.

To do this you need two action methods with the same name, one decorated with the [HttpPost] attribute (an action method selector) and that takes an instance of the View Model:

public class SomeController : Controller {

    public ViewResult MyAction() {
        /* Displays the form */
    }
    [HttpPost]
    public ActionResult MyAction(MyModel incomingData) {
        /* Handles the POST */
    }
}

Friday, 7 October 2011

MVC3 Routing and Segment Variable Quirks

Imagine our application has a single route, as follows:

routes.MapRoute("MyRoute", "{controller}/{action}/{color}/{page}");

Now imagine that a user is currently at the URL /Catalog/List/Purple/123, and we render a link as follows:

@Html.ActionLink("Click me", "List", "Catalog", new {page=789}, null)


You might expect that the routing system would be unable to match the route, because we have not supplied a value for the color segment variable, and there is no default value defined. You would, however, be wrong. The routing system will match against the route we have defined. It will generate the following HTML:

<a href="http://www.blogger.com/Catalog/List/Purple/789">Click me</a>


The routing system is keen to make a match against a route, to the extent that it will reuse segment variable values from the incoming URL. In this case, we end up with the value Purple for the color variable, because of the URL from which our imaginary user started.

This is not a behavior of last resort. The routing system will apply this technique as part of its regular assessment of routes, even if there is a subsequent route that would match without requiring values from the current request to be reused. The routing system will reuse values only for segment variables that occur earlier in the URL pattern than any parameters that are supplied to the Html.ActionLink method. Suppose we tried to create a link like this:

@Html.ActionLink("Click me", "List", "Catalog", new {color="Aqua"}, null)


We have supplied a value for color, but not for page. But color appears before page in the URL pattern, and so the routing system won't reuse the values from the incoming URL, and the route will not match.

The best way to deal with this behavior is to prevent it from happening

Where Does MVC3 Look For the Entity.ID within the View

Working with Razor Engine Views created by default after clicking 'Add View' from with a Controller Action Method.

Found that the 'Edit' views included the Entity ID in the markup allowing users to directly edit the primary key of the field - not cool.

So i deleted them - also, not cool.

Doing so resulted in this Exception:

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

Which upon debugging the Action Method, which was expecting an instance of my entity to be passed in, i found the entity was NULL and then this post alerted me to the fact that .Net couldn't find an ID and so didn't know which entity to pass in.

So, i need to be able to ensure that the ID is available for the runtime to instanitate the correct Model Entity and pass it into the Controller Action Method > hence I added the original markup back in:

<div class="editor-label">
    @Html.LabelFor(model => model.EMERGENCY_SERVICE_TYPE_ID)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.EMERGENCY_SERVICE_TYPE_ID)
    @Html.ValidationMessageFor(model => model.EMERGENCY_SERVICE_TYPE_ID)
</div>

BUT: where, exactly is .Net getting the ID from ???

It appears that .Net is ripping the ID out of this HTML Helper:

@Html.EditorFor(model => model.EMERGENCY_SERVICE_TYPE_ID)

When you inspect the HTML at the client, you'll find that the above Helper will render the HTML as:

<input id="EMERGENCY_SERVICE_TYPE_ID" class="text-box single-line" type="text" 
 value="23" name="EMERGENCY_SERVICE_TYPE_ID"  
 data-val-required="The EMERGENCY_SERVICE_TYPE_ID field is required." 
 data-val-number="The field EMERGENCY_SERVICE_TYPE_ID must be a number." 
 data-val="true">

After some playing around eliminating various attributes i found that in order for .Net to automatically take the form values (hence the ID needs to be in an <input> tag) and use them to instanitate a Model Entity for you, use the following markup:

<input type="hidden" 
name="EMERGENCY_SERVICE_TYPE_ID" 
value="@this.Model.EMERGENCY_SERVICE_TYPE_ID" 
id="estId" />

Note the Razor Syntax (@this.Model) could be replaced with ye olde Server Side code syntax (<%=).

You could do it using a HTML Helper (eg HTML.HiddenFor(x => x.ID)) but that would also include a bunch of unnecessary stuff in the markup.

The mandatory requirements in order for .Net to instantiate your Entity:
  • The ID needs to be wrapped up in an <input>  tag
  • The 'name' attribute of the <input> tag needs to exactly the same as the Entity Property which represents the Primary Key

Thursday, 6 October 2011

StructureMap + MVC3 + Generic Repository Pattern

I had this exact same problem:

Have a Generic Repository:

public interface IRepository<TEntity> : IDisposable where TEntity : class
     { }
and a Concrete Implementation:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
     { }
which i wanted injected into the constructor of the Controllers at runtime where the TEntity would be the Model relevant to that Controller:
    public FooBarController(IRepository<FOO_BAR_TYPE> repository)
            {
                _repo = repository;
            }
the Controller would then use Repository "_repo" to update the Model:
    //
    // POST: /EmergencyServiceType/Create
    [HttpPost]
    public ActionResult Create(FOO_BAR_TYPE foobar)
    {
        if (ModelState.IsValid)
        {            
            // GetNextSequenceValue() deals with Oracle+EF issue with auto-increment IDs
            foobar.FOO_BAR_ID = _repo.GetNextSequenceValue(); 
            _repo.Add(foobar);
            _repo.SaveChanges();
            return RedirectToAction("Index");  
        }
    
        return View(foobar); // display the updated Model
    }
simonjreid elluded to the answer for me: had to add the ObjectContext to the StructureMap configuration (the Repository's purpose was to wrap up the Context generated by EntityFramework, which i called MyContextWrapper. Therefore because the Repository depended on MyContextWrapper, which in turn depends on ObjectContext):
    // This avoids 'No Default Instance for ...DbConnection' exception
    x.For<System.Data.Objects.ObjectContext>().Use<MyContextWrapper>();
    x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); // note that Named is required and is Case Sensitive
However, now i get the StructureMap runtime Exception:

StructureMap Exception Code: 205
Missing requested Instance property "connectionString"

After reading a post by Jeremy Miller [A Gentle Quickstart][1] (right at the bottom) i found that you can define what arguments to pass into the constructor of your registered types ie i needed to pass in the Connection String to the Constructor of the MyCustomContext class (here is the full listing of how i am initializing the ObjectFactory:
    string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyContextWrapper"].ConnectionString;
    ObjectFactory.Initialize(x =>
                {
                    x.Scan(scan =>
                            {
                                // Make sure BUSINESS_DOMAIN assembly is scanned
                                scan.AssemblyContainingType<BUSINESS_DOMAIN.MyContextWrapper>(); 
                                scan.TheCallingAssembly();
                                scan.WithDefaultConventions();
                            });
                    // 'connStr' below is a local variable defined above
                    x.For<System.Data.Objects.ObjectContext>()
                        .Use<MyContextWrapper>()
                        .Ctor<string>().Is(connStr);
                    x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); 
                });
And BOOM! Can now have my Controller instaniated at runtime by StructureMap and get it to inject an instance of IRepository...happy days.

[1]: http://structuremap.net/structuremap/QuickStart.htm

StructureMap MVC3 Inject Repository Into a Controller

Been working on this for a while and had to pull together information from various sources.

The way i finally got it to work (and this will need to be refactored further) was to ensure that the Type you put into the Use() method is named - otherwise you'll get the exception: "No Parameterless Constructor...".

I am not using the StructureMap configuration file, but instead the Fluent Interface, Registry DSL to configure StructureMap programmatically in the Application_Start event of Global.asax.cs:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    StructureMap.ObjectFactory.Initialize(x =>
        {
            x.UseDefaultStructureMapConfigFile = false;
            x.For<System.Web.Mvc.IController>().Use<MvcSMTest.Controllers.BlogController>().Named("blog");
            x.For<MvcSMTest.Models.IPostRepository>().Use<MvcSMTest.Models.InMemoryPostRepository>();
        });
    ControllerBuilder.Current.SetControllerFactory(new MvcSMTest.Controllers.MyControllerFactory());
}