SitePoint Sponsor

User Tag List

Page 2 of 2 FirstFirst 12
Results 26 to 40 of 40
  1. #26
    SitePoint Enthusiast
    Join Date
    Apr 2009
    Posts
    47
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Serenarules View Post
    For those that like to delve, want customization of underlying abstract classes, or just don't like the way any existing framework behaves, this tutorial is for you.

    Personally, I don't like the way the ASP.NET MVC framework lumps action handlers into a single controller. I prefer to isolate my actions as much as possible.

    You will not need to install ASP.NET MVC framework, or any other framework, other than that which comes with Visual Studio 2008.

    To begin, use VS to create a new web application on your development server, or in the file system if you prefer.

    The first thing you will want to do is edit your web.config file and edit it the following changes.

    Add the following to assemblies:

    Code XML:
    <!-- required for routing -->
    <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <!-- / required for routing -->

    Add the following to modules:

    Code XML:
    <!-- required for routing -->
    <remove name="UrlRoutingModule"/>
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <!-- / required for routing -->

    Add the following to handlers:

    Code XML:
    <!-- required for routing -->
    <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
    <!-- / required for routing -->

    Change the authentication type to Forms:

    Code XML:
    <authentication mode="Forms" />

    Add the following attribute to modules:

    Code XML:
    <!-- set runAllManagedModulesForAllRequests="true" to persist FormsAuthentication after routing -->
    <modules runAllManagedModulesForAllRequests="true">

    The second thing you will want to do is set up the aspnetdb database. Use the ASP.NET Configuration tool to do this. Enabled roles and add one. Then add a user, adding him to the role. Make note of the username and password. We will use this later.

    Now let's get on to the code!

    Add a global.asax file to your project and use the following code:

    Code VBNET:
    <%@ Application Language="VB"%>
    <%@ Import namespace="System.web.Routing" %>
    <script runat="server">
    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    RouteTable.Routes.Add("ResourceStopRoute", New Route("{resource}.axd/{*pathInfo}", New StopRoutingHandler))
    Dim routeConstraints As RouteValueDictionary = New RouteValueDictionary
    routeConstraints.Add("id", "\d+")
     
    Dim routeDefaults As RouteValueDictionary = New RouteValueDictionary
    routeDefaults.Add("command", "Home")
    routeDefaults.Add("action", "Page")
    routeDefaults.Add("id", 0)
     
    Dim routeDefinition As Route = New Route("{command}/{action}/{id}", routeDefaults, routeConstraints, New RequestController)
    RouteTable.Routes.Add("CommandActionRoute", routeDefinition)
    End Sub
     
    </script>

    This sets a decimal value as the constraint for the id parameter and defaults for all, then adds the route. In addition, we add a route to ignore requests for axd resource files. Why do we not add constraints for the command and action parameters? You'll see shortly.

    Now add a code file name RequestController.vb to your code base and use this code:

    Code VBNET:
    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Web.Routing
    Imports System.Reflection.Assembly
    Public Class RequestController : Implements Routing.IRouteHandler
    Public Function GetHttpHandler(ByVal requestContext As RequestContext) As IHttpHandler Implements IRouteHandler.GetHttpHandler
    Dim commandName As String = New String("{command}{action}Command")
    commandName = commandName.Replace("{command}", requestContext.RouteData.Values("command"))
    commandName = commandName.Replace("{action}", requestContext.RouteData.Values("action"))
    Dim desiredCommand As IRouteHandler = GetExecutingAssembly.CreateInstance(commandName)
    If desiredCommand Is Nothing Then desiredCommand = New HomePageCommand
    Return desiredCommand.GetHttpHandler(requestContext)
    End Function
    End Class

    Here is where we create our command object based off of the command and action parameters. If it cannot be created, we use a known command instead. In this case, the home page. This is why we didn't add constraints earlier. We want all typos in user typed urls to actually go somewhere.

    Now here are the two abstract classes in our sysem. The CommandAbstract and ViewAbstract. You can add as much as you want to these and persist just about anything you wish between command and view.

    Code VBNET:
    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Web.Routing
    Public MustInherit Class CommandAbstract : Implements Routing.IRouteHandler
    Protected RouteData As Dictionary(Of String, Object) = New Dictionary(Of String, Object)
    Protected ViewData As Dictionary(Of String, Object) = New Dictionary(Of String, Object)
    Public Function GetHttpHandler(ByVal requestContext As RequestContext) As IHttpHandler Implements IRouteHandler.GetHttpHandler
    For Each item In requestContext.RouteData.Values
    RouteData.Add(item.Key, item.Value)
    Next
    Dim desiredView As IHttpHandler = Execute(requestContext)
    requestContext.HttpContext.Items.Add("RouteData", RouteData)
    requestContext.HttpContext.Items.Add("ViewData", ViewData)
    Return desiredView
    End Function
    Protected MustOverride Function Execute(ByRef requestContext As RequestContext) As IHttpHandler
    End Class
    Public MustInherit Class ViewAbstract : Implements IHttpHandler
    Protected RouteData As Dictionary(Of String, Object)
    Protected ViewData As Dictionary(Of String, Object)
    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
    Get
    Return True
    End Get
    End Property
    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
    RouteData = context.Items("RouteData")
    ViewData = context.Items("ViewData")
    Render(context)
    End Sub
    Protected MustOverride Sub Render(ByRef context As HttpContext)
    End Class

    All commands and views derive from these. Now let's take a look at the commands and views.Be sure to change the username and password in UserLoginCommand using the values you supplied earlier!

    Code VBNET:
    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Web.Routing
    Public Class AboutPageCommand : Inherits CommandAbstract
    Protected Overrides Function Execute(ByRef requestContext As RequestContext) As IHttpHandler
    ViewData("message") = "This is the about page!"
    Return New AboutPageView
    End Function
    End Class
    Public Class HomePageCommand : Inherits CommandAbstract
    Protected Overrides Function Execute(ByRef requestContext As RequestContext) As IHttpHandler
    Return New HomePageView
    End Function
    End Class
    Public Class UserLoginCommand : Inherits CommandAbstract
    Protected Overrides Function Execute(ByRef requestContext As RequestContext) As IHttpHandler
    If requestContext.HttpContext.Request.RequestType = "POST" Then
    If Membership.ValidateUser("Admin", "admin--") Then
    FormsAuthentication.SetAuthCookie("Admin", True)
    requestContext.HttpContext.Response.Redirect("~/")
    End If
    End If
    Return New UserLoginView
    End Function
    End Class
    Public Class UserLogoutCommand : Inherits CommandAbstract
    Protected Overrides Function Execute(ByRef requestContext As RequestContext) As IHttpHandler
    If requestContext.HttpContext.Request.RequestType = "POST" Then
    FormsAuthentication.SignOut()
    requestContext.HttpContext.Response.Redirect("~/")
    End If
    Return New UserLogoutView
    End Function
    End Class

    Code VBNET:
    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Web.Routing
    Public Class AboutPageView : Inherits ViewAbstract
    Protected Overrides Sub Render(ByRef context As System.Web.HttpContext)
    context.Response.Write(ViewData("message"))
     
    End Sub
    End Class
    Public Class HomePageView : Inherits ViewAbstract
    Protected Overrides Sub Render(ByRef context As System.Web.HttpContext)
    If context.User.Identity.IsAuthenticated Then
    context.Response.Write("You are currently logged in as: " + context.User.Identity.Name + "!")
    Else
    context.Response.Write("You are currently logged in as: Guest!")
    End If
    End Sub
    End Class
    Public Class UserLoginView : Inherits ViewAbstract
    Protected Overrides Sub Render(ByRef context As System.Web.HttpContext)
    context.Response.Write("<html><head><title></title></head><body><form method=""post"" action=""Login""><input type=""submit"" id=""submit"" name=""submit"" value=""Login Now"" /></form></body></html>")
    End Sub
    End Class
    Public Class UserLogoutView : Inherits ViewAbstract
    Protected Overrides Sub Render(ByRef context As System.Web.HttpContext)
    context.Response.Write("<html><head><title></title></head><body><form method=""post"" action=""Logout""><input type=""submit"" id=""submit"" name=""submit"" value=""Logout Now"" /></form></body></html>")
    End Sub
    End Class

    You should now be ready to compile and run.If you try this sample and have problems, let me know.

    Ideally, you will want to store your html templates externally and read them in, whether they be in static files, xml, or database. This leave a very clean system, in that there are NO aspx files whatsoever in the application (yes, this means you can delete the Default.aspx now). I store my localized phrases and templates in a db as bits and pieces are pull them in when needed, but that's beyond the scope of this little tutorial.

    I hope this was useful, or at least, insightful, to somebody.
    Thanks for your contribution and you have added a huge code and i want to salute you for this effort and your tips are very helpful, i have saved all of you codes in my note pad.
    Thanks.
    Submit Articles

  2. #27
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,653
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    While we are talking MVC, I might as well kick a question to the crowd: What is the best practice vis-a-vis WebFormViews. Being a bit of an old webform head, I start thinking about how I can subclass MvcView and stuff all sorts of neat stuff in there, but I suspect that isn't the Mvc way. So, questions are:

    1) How should one model shared functionality across views.
    2) On viewdata--I can see this having some application-specific stuff as well as some view management stuff. How should one attack this?

    For the record, the app in question is a completely custom aspnetmvc CMS system. Well, half-mvc at least; admin tools will likely be webforms.

  3. #28
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    My first suggestion is to go to the link below and download the file I listed. This is the source for MVC. You can learn a lot just by looking it over.

    I would say this though. Subsclassing ViewPage is a bit more involved than some of the classes. There is also the ViewPage(Of TModel) to worry about, and subsclassing that causes problems with 'Context not a member of...' errors on pages that inherit from that, like the Error.aspx page that comes with the package.

    You can of course extend mvc.controller if you'd like, since that's where most of your active code will be. You really want to keep as much code out of the view as possible, save for some simple collection looping and things like that.

    http://www.microsoft.com/downloads/d...displaylang=en

    AspNetMvc1.0.Ms-PL.source.zip

  4. #29
    SitePoint Guru pufa's Avatar
    Join Date
    Oct 2004
    Location
    Portugal, Lisboa
    Posts
    947
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One of the things I've saw related to MVC and liked a lot was the "Html Helpers" when combined with lambda expression, and to be able to define your form from the model.

    Code Csharp:
    Html.InputFor(m => m.Name) // Name being a string outputs regular input field with length restrictions and so on. 
    Html.InputFor(m => m.DataOfBirth) // DataOfBirth being a date outputs a Calendar UI wiget.

    What I'm looking at now is how to keep the controller action clean, with focus on the action it self and move all the other suff you have to show in the view out of the controller action.

    dhtmlgod... you mentioned action filters... aren't these used has attributes? Is there another way to attatch them?

    How do you test this behavior?

    Last thing... Resharper rocks (no affiliation... just happy customer)

    cheers,
    Rui
    Last edited by pufa; May 16, 2009 at 05:27.
    Ciao, Rui...

  5. #30
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,653
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Thanks. Not sure how reviewing the source is going to help with a more philosophical architecture question; I'm not trying to rewrite the controller system, just avoid repetition and centralize functionality in the view engine.

    The HTML helpers look neat. Still not a huge fan of extension methods, really feels like monkeypatching to me.

    The big drawback I see with the reliance upon attributes for alot of things is that you must know many things at compile time. So stuff like making these filters configuration driven isn't easy without a little voodoo.

    IE:

    Code:
    [OutputCacheFilter(Duration = ConfigRepository.GetConfig().CacheTime)]
    public ActionResult PenguinsSuck()
    {
    //Prove how the Pittsburg Penguins do suck, and that Sidney Crosby tortures small furry animals for fun
    }
    Won't work because the compiler can't really resolve the configuration driven bit at compile time. And I really don't think adjusting the cache times on an app should be something that requires a developer, a recompile and a redeploy.

  6. #31
    SitePoint Guru pufa's Avatar
    Join Date
    Oct 2004
    Location
    Portugal, Lisboa
    Posts
    947
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Moved this to a new post ... feels better


    Something like this
    Code Csharp:
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    IEnumerable<ViewContentFilterAttribute> contentFilterAttributes = ServiceLocator.GetAllInstances<ViewContentFilterAttribute>();
    contentFilterAttributes.ForEach(cf => cf.OnActionExecuting(filterContext));
    base.OnActionExecuting(filterContext);
    }

    ViewContentFilterAttribute is just a "marker" class to help in Windsor registration

    Code Csharp:
    Container.Register(
    AllTypes.Of<ViewContentFilterAttribute>()
    .FromAssembly(typeof(MvcApplication).Assembly)
    .Configure(c => c.Named(c.Implementation.Name)));

    An example of an action filter that load a list of "Categories" into ViewData

    Code Csharp:
    public class CategoriesViewContentFilterAttribute : ViewContentFilterAttribute
    {
    public ICategoryRepository CategoryRepository { get; set; }
     
    public CategoriesViewContentFilterAttribute(ICategoryRepository categoryRepository)
    {
    CategoryRepository = categoryRepository;
    }
     
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    var categories = CategoryRepository.GetAllCategories();
    ControllerBase controller = filterContext.Controller;
    controller.ViewData.Add(categories);
    base.OnActionExecuting(filterContext);
    }
    }
    Ciao, Rui...

  7. #32
    ALT.NET - because we need it silver trophybronze trophy dhtmlgod's Avatar
    Join Date
    Jul 2001
    Location
    Scotland
    Posts
    4,836
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wwb_99 View Post
    1) How should one model shared functionality across views.
    2) On viewdata--I can see this having some application-specific stuff as well as some view management stuff. How should one attack this?
    1) We use IPresentationModel<ViewModel> and anything that is shared across views are properties on IPresentationModel. We attach anything we need to that using ActionFlters

    2) I would strongly recommend not using ViewDat["string"]. Magic strings can quickly become a pain and have poor refactoring support. Using a combination of IPresentation and ViewMode, we always send everything to the view strongly typed.

    Basically every view has a ViewModel, and we never use magic strings

  8. #33
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,653
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Oh, I definitely agree on magic strings. I'm in the lunatic fringe that uses constants for nearly anything possible. And that is if I'm too lazy to write up a strongly-typed config section <g>.

    That approach is awesome. I was looking at Nerd Dinner when you wrote it, and I hit upon the concepts of making ViewModel wrapper classes. But wrapping the whole thing in an interface makes a whole lotta sense.

    Next question--how would one go about dynamically changing MasterPages? We have some scenarios where this would make alot of sense. Or at least this *sort* of capability would make alot of sense.

  9. #34
    ALT.NET - because we need it silver trophybronze trophy dhtmlgod's Avatar
    Join Date
    Jul 2001
    Location
    Scotland
    Posts
    4,836
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Cool

    Quote Originally Posted by pufa View Post
    One of the things I've saw related to MVC and liked a lot was the "Html Helpers" when combined with lambda expression, and to be able to define your form from the model.

    Code Csharp:
    Html.InputFor(m => m.Name) // Name being a string outputs regular input field with length restrictions and so on. 
    Html.InputFor(m => m.DataOfBirth) // DataOfBirth being a date outputs a Calendar UI wiget.

    What I'm looking at now is how to keep the controller action clean, with focus on the action it self and move all the other suff you have to show in the view out of the controller action.

    dhtmlgod... you mentioned action filters... aren't these used has attributes? Is there another way to attatch them?

    How do you test this behavior?

    Last thing... Resharper rocks (no affiliation... just happy customer)

    cheers,
    Rui
    Extension methods are the way to go with UI stuff. We use and have extended the FluentHtml that comes with MvcContrib.

    We don't have any if statements or foreach loops in our views. We remove if statements bu having one view model for each view, with the view model type deciding the view. UserRegsterModel will always result in a UserRegisterModelView.

    To get around foreach loops, we do this:

    Code Csharp:
    <% this.RenderEach(m => m.Navigation); %>

    RenderEach will use the Navigation type (say NavigationModel) to render a user controler called NavigationModelView for each item.

    We also don't use ActionFilters as attributes. We use an internal Dsl to configure out application and resolve the filters through our container transparently. To do this, I have started an open source project called FluentMvc that allows use to do this:

    Code Csharp:
    FluentMvcConfiguration.Configure =
                    x =>
                        {
                            x.ResolveWith(objectFactory);
     
                            x.UsingControllerFactory(windsorControllerFactory);
                            x.WithResultFactory<ActionResultFactory>();
                            x.WithResultFactory<JsonResultFactory>();
                            x.WithResultFactory<ViewResultFactory>(Is.Default);
     
                            x.WithFilter<NavigationFilter>()
                                .WithFilter<HandleErrorAttribute>()
                                .WithFilter<CurrentUserDetailsFilter>()
                                .WithFilter<SecurityFilter>(Except.For<AccountController>(ac => ac.Register())
                                                                .AndFor<AccountController>(ac => ac.Register(null))
                                                                .AndFor<AccountController>(ac => ac.LogOn(null, null, false, null))
                                                                .AndFor<AccountController>(ac => ac.LogOn())
                                                                .AndFor<HomeController>())
     
                                .WithFilter<SecurityFilter>(Apply.For<ImportController>())
                                .WithFilter<SecurityFilter>(Apply.For<BrokerController>())
     
                                .WithFilter<PostOnlyFilter>(Apply.For<ImportController>(ic => ic.UploadFile(null)));
                        };

    This allows us to centralise our filter configuration and use dependency injection in our filters and develop them using TDD like any other class. We also overcome the other limitations of attributes.

    This with a combination of an external Dsl for Castle in the form of Binsor means we can change injected dependencies (like cache duration, etc.) without having to recompile and deal with Xml hell

    Although we use FluentMvc in production, I wouldn't say it was production ready, but you can get the source here: http://code.google.com/p/fluentmvc/ It needs a large refactoring, but it will deliver the above no problem, but you need to add a class to your project to work with Windsor. I am considering using the CommonServiceLocator to abstract the IoC container out instead of our own interface.

  10. #35
    ALT.NET - because we need it silver trophybronze trophy dhtmlgod's Avatar
    Join Date
    Jul 2001
    Location
    Scotland
    Posts
    4,836
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wwb_99 View Post
    That approach is awesome. I was looking at Nerd Dinner when you wrote it, and I hit upon the concepts of making ViewModel wrapper classes. But wrapping the whole thing in an interface makes a whole lotta sense.
    I highly recommend checking out AutoMapper, it really helps with the pain of mapping between your entity and view model.

    Quote Originally Posted by wwb_99 View Post
    Next question--how would one go about dynamically changing MasterPages? We have some scenarios where this would make alot of sense. Or at least this *sort* of capability would make alot of sense.
    Your own viewengine would be the way to go for this. You could prolly take the quick and dirty viewengine I posted and add it there.

    Check out Oxite for a good example of how to write an Mvx application, it blows Nerd Dinner out of the water and they have taken a lot of input from the community about the direction they should take. And use alot of th stuff I've mentioned

    I just want to make the point that to a hardcore WebForms guy, Mvc is in some ways going to feel like a step backwards. No user controllers, less in the box, more code, etc. But from a SoC and testibility POV, it's amazing, and as an old ASP head, I love having 100% control of the UI again

  11. #36
    SitePoint Member gavin.williams's Avatar
    Join Date
    May 2009
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Really some informative tutorial you got here. Useful materials and great value

  12. #37
    SitePoint Evangelist
    Join Date
    Apr 2008
    Location
    Dublin, Ireland
    Posts
    461
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dhtmlgod View Post
    as an old ASP head, I love having 100&#37; control of the UI again
    While I like MVC I have to say I took 100% control of the UI a long time ago. Repeater instead of gridview, not using other controls like the link button or any control that outputs bad html/inline javascript, avoiding asp.net ajax and the control toolkit at all costs. Building a site that that works without script and adding unobtrusive script on top. Aside from MVC I like the routing stuff a lot which allows you to do the same friendly urls in a non-mvc app. Now if hosting companies could all just move to IIS7.

  13. #38
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    wwb, master pages can be changes using the View() function as is, as far as I know. It has several overloads, one of which takes two params, the view and the master to load with it. It works.

    One thing I'm having a problem with at the moment is localization at the web.config level. I have my own view engine that works locale and theme into the url ({locale}/{theme}/{controller}/{action}/{id}) and restructured my View folder to suit. I then stuck a web.config with <globalization uiCulture="? culture="?" /> in it within the theme folder. It's supposed to set the culture for the aspx pages under it so I don't have to update each and every aspx files @Page directives. It's still falling back to the locale.resx rather than using the locale specific ones (locale.en.resx, locale.en-US.resx, locale.en-CA.resx etc).

    Ideas?

  14. #39
    SitePoint Author silver trophybronze trophy
    wwb_99's Avatar
    Join Date
    May 2003
    Location
    Washington, DC
    Posts
    10,653
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dhtmlgod View Post
    I highly recommend checking out AutoMapper, it really helps with the pain of mapping between your entity and view model.


    Your own viewengine would be the way to go for this. You could prolly take the quick and dirty viewengine I posted and add it there.
    Just tried AutoMapper. I'd be willing to argue that it actually is the best thing since sliced bread at the moment.

    Check out Oxite for a good example of how to write an Mvx application, it blows Nerd Dinner out of the water and they have taken a lot of input from the community about the direction they should take. And use alot of th stuff I've mentioned
    Definitely a step beyond NerdDinner, and lots of good stuff. Really a bit much for a first serious dive into MVC--so much going on, and the downloadable source package won't even build. Would be really awesome if someone [who doesn't work for MSFT] could put together a NerdDinner-scale demo app showing the "community" ways, etc.

    I just want to make the point that to a hardcore WebForms guy, Mvc is in some ways going to feel like a step backwards. No user controllers, less in the box, more code, etc. But from a SoC and testibility POV, it's amazing, and as an old ASP head, I love having 100% control of the UI again
    I, like Brian, am a naked databinding and literals type of guy. That and lots of user controls. If you knew what you were doing, you could keep pretty effective control of the html in web forms land.

    And Url Routing solves another major issue. Really feels like a sideways step to me to some extent. There are definitely things I miss about web forms, especially since I had so much seat time with them. In fact, on the current project, I'm really still debating using web forms for the admin half of the app and then MVC for the public facing bits. You loose a bit of testability at the top of the stack, but if you keep discipline in said forms, you can still have a pretty solid coverage. That said, doing it all in MVC makes alot of sense as well from a patterns perspective.

    The big problems I'm trying to solve via MVC basically revolve around giving the html types a bit more leeway to do more interactive stuff. No way they could hang with web forms, but all the dinky little "this is how you post an ajax form using jquery" tutorials work well with MVC.

    wwb, master pages can be changes using the View() function as is, as far as I know. It has several overloads, one of which takes two params, the view and the master to load with it. It works.

    One thing I'm having a problem with at the moment is localization at the web.config level. I have my own view engine that works locale and theme into the url ({locale}/{theme}/{controller}/{action}/{id}) and restructured my View folder to suit. I then stuck a web.config with <globalization uiCulture="? culture="?" /> in it within the theme folder. It's supposed to set the culture for the aspx pages under it so I don't have to update each and every aspx files @Page directives. It's still falling back to the locale.resx rather than using the locale specific ones (locale.en.resx, locale.en-US.resx, locale.en-CA.resx etc).

    Ideas?
    Thanks, I missed that overload. Could come in handy for some stuff. Otoh, architecturally, pushing such functions into a custom view engine probably is a better long-term solution. Keep controllers lean and services fat. As for your question, not sure and honestly I rarely have to deal with localization here. I'd guess that the Url Routing and general funkschwea of ASP.NET Mvc breaks alot of the <location> based bits of ASP.NET. Also, I think it might make more sense to incorporate that key bit into your presentation model data rather than relying upon configuration. It's much more testable that way.

    Now for the dumb questions portion of this program:

    1) Query string parameters: how should one deal with them, especially optional stuff. I did a bit of searching and didn't find any good nor extant examples on the intertron. For the simple "one optional parameter" scenario, I landed upon using controllers with method signatures like:

    Code:
    public ActionResult List(int? groupingConstructId)
    But what about fun scenarios where you have a half-dozen or more potential optional parameters?

    2) Are there any good guides to writing tests for controllers that test the HTTP bits. For example, I'd really like to assure myself that all my controller actions are responding to the right HTTP verbs and requesting authorization as appropriate and such.

    3) Kind of 2a here, but are there any guides to make the model data go "through the lifecycle." IE--my tests now call the ModelState.IsValid property, but it is pretty apparent that ModelState isn't getting built so it is always valid.

    4) How are the cool kids doing validation these days anyhow?

  15. #40
    ALT.NET - because we need it silver trophybronze trophy dhtmlgod's Avatar
    Join Date
    Jul 2001
    Location
    Scotland
    Posts
    4,836
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    1) Query string parameters: how should one deal with them, especially optional stuff. I did a bit of searching and didn't find any good nor extant examples on the intertron. For the simple "one optional parameter" scenario, I landed upon using controllers with method signatures like:
    If I have an action that takes more than 2 parameters, I will change that to a parameter object, which makes dealing with optionals alot easier
    2) Are there any good guides to writing tests for controllers that test the HTTP bits. For example, I'd really like to assure myself that all my controller actions are responding to the right HTTP verbs and requesting authorization as appropriate and such.
    My guideline is that a controller should not know about Http. The abstractions on System.Web.Abstractions, IMO, don't go far enough. I prefer to add abstractions over any http stuff. I find this easier to work with and test. While it does mean a more classes, they are usually thin delegating wrappers. Things like authorization and HTTP verbs are applied with ActionFilters and as attributes with an OOB version. From a testing perpective, you just want to ensure that the attribute is present. When we used attribute based filter, we used a little extension method that uses from reflection thats tests a certain attribute is present.
    3) Kind of 2a here, but are there any guides to make the model data go "through the lifecycle." IE--my tests now call the ModelState.IsValid property, but it is pretty apparent that ModelState isn't getting built so it is always valid.
    Adding a errror using controllerinstance.ModelState.AddModelError() will cause the ModelState to become invalid (lets just ignore that poor design choice here) and causees the IsValid property to become false.
    4) How are the cool kids doing validation these days anyhow?
    We use Castle validators with xVal. We attribute our view model like this:

    Code Csharp:
    using Castle.Components.Validator;
     
    public class UserCreateModel
    {
        [ValidateNonEmpty]
        public string UserName { get; set; }
     
        [ValidateNonEmpty, ValidateEmail]
        public string Email { get; set; }
     
        [ValidateNonEmpty]
        public string Password { get; set; }
     
        [ValidateNonEmpty, ValidateSameAs("Password")]
        public string PasswordConfirm { get; set; }
    }

    And on the client we use jQuery validation:

    Code HTML4Strict:
    <script type="text/javascript" src="/Static/Scripts/jquery.validate.pack.js"></script>
    <script type="text/javascript" src="/Static/Scripts/xVal.jquery.validate.js"></script>
     
    <%= Html.ClientSideValidation<UserCreateModel>() %>

    The end result is that jQuery validation is automatically added on the client, and the ModelState is automatically filled by the time you hit your controller action. It also works with Data Annotations that was added with .NET 3.5, but I've never used them.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •