Best asp.net-mvc questions in January 2011

Using MVC HtmlHelper extensions from Razor declarative views

13 votes

I was trying to create a Razor declarative helper in my App_Code folder for an MVC 3 RTM project.

The problem I ran into was that the MVC HtmlHelper extensions, like ActionLink, aren't available. This is because the compiled helpers derive from System.Web.WebPages.HelperPage, and though it exposes an Html property, its of type System.Web.WebPages.HtmlHelper rather than System.Web.Mvc.HtmlHelper.

An example of the kind of error I was getting is:

'System.Web.Mvc.HtmlHelper' does not contain a definition for 'ActionLink' and no extension method 'ActionLink' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)

My only solution has been to create my own HelperPage and override the Html property:

public class HelperPage : System.Web.WebPages.HelperPage 
{
    // Workaround - exposes the MVC HtmlHelper instead of the normal helper
    public static new HtmlHelper Html
    {
        get { return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html; }
    }
}

I then have to write the following at the top of every helper:

@inherits FunnelWeb.Web.App_Code.HelperPage
@using System.Web.Mvc
@using System.Web.Mvc.Html

@helper DoSomething()
{
    @Html.ActionLink("Index", "Home")
}

Is it meant to be this hard in MVC 3, or am I doing something wrong?

Take a look at Marcind's answer to this question. What you're experiencing is a limitation of putting declarative views in the App_Code folder.

Putting your helpers in App_Code works but has certain limitations that impact certain MVC scenarios (for example: no access to standard MVC Html. helpers)

Microsoft Charting, MVC 3 and Razor

12 votes

Related to This topic I wonder if anyone has made the Microsoft Charting library working with Asp MVC 3 and Razor.

I know about the new chart helper introduced, but since that is very limited that is not really an option.

To create an action method that returns an image is also easy enough, but since all interactivity breaks down (even just simple tooltips for the bars in a bar chart) this method has several limitations.

This example is probably the most helpful article I have found, but I still cant get a single easy chart working even though it does work when rendering the image only in an action method. Also I have got the samples working fine under .net 4, but obviously those arent MVC samples.

SO - has anyone got Microsoft charting working fully in Asp MVC 3 with Razor and could post a link to a complete solution?

If it is tool tip and drill down you are looking for then here is a sample. I tried and worked as a charm for me. You need to have ImageMap linked with your image to have interactivity.

MVC Charts with Interactivity

how to render a razor view, get the html of a rendered view inside an action

9 votes

anybody knows how to get the generated html of a view inside an action ?

something like this:

public ActionResult Do()
{
    var html = RenderView("hello", model);
...
}

I use a static method in a class I called Utilities.Common I pass views back to the client as properties of JSON objects constantly so I had a need to render them to a string. Here ya go:

        public static string RenderPartialViewToString(Controller controller, string viewName, object model)
        {
            controller.ViewData.Model = model;
            try
            {
                using (StringWriter sw = new StringWriter())
                {
                    ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
                    ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                    viewResult.View.Render(viewContext, sw);

                    return sw.GetStringBuilder().ToString();
                }
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }

This will work for full views as well as partial views, just change ViewEngines.Engines.FindPartialView to ViewEngines.Engines.FindView.

ASP.NET MVC 3 client-side validation with parameters

9 votes

Following on from this post http://stackoverflow.com/questions/4747184/perform-client-side-validation-for-custom-attribute/4782235#4782235

I am trying to get my head around how to do this, passing additional parameters to the client-side script

As I understand it so far to implement custom validation with MVC 3 the following is required

Create a custom validation attribute

Based on ValidationAttribute and implementing IClientValidatable. I have also see some examples deriving from ModelValidator, which seems to implement the functionality of both ValidationAttribute and IClientValidatable. So this is my first point of confusion as to what the diffirences are or whether ModelValidator was used in MVC 2 but is now depricated or what ?

An instance of ModelClientValidationRule must be returned from GetClientValidationRules() to specify details such as the error message, ValidationType (which I understand to be the name of the Javascript function that will perform the client-side validation) and any additional custom parameters that the attribute may have, and that need to be passed to the Javascript validation.

I assume that the runtime (not sure which part of it) then use the ModelClientValidationRule to generate html attribute in the tag elements as follows. data-val="true" to indicate that the element requires validation data-val-[ValidationType]=[ErrorMessage] data-val-[ValidationType].[ValidationParameters(n).Key]=[ValidationParameters(n).Value]

Implement the client-side validation logic

A Javascript function must be created and added to jQuery.validators with jQuery.validators.addmethod() so that JQuery is aware of it when it need to be executed. Something like:

jQuery.validator.addMethod(
    'greaterThan', 
    function (value, element, params) {
        /.../
       return /* true or false   */ ; 
    },
    ''
); 

My question here is whether the signature 'function (value, element, params)' is standard for methods that will handle validation and I assume it will be called by some jQuery functionality at the appropriate time such as before a form is submitted or when an element looses fuces or on keyUp events. I just don't undertand how you can controll this i.e. choose which event is appropriete for yout custom validation.

Implement an unobtrusive adapter

This translates unobtrusive attributes to; something I am not very clear on, but assume it to be a jQuery Rule, but I am not clear on how those work. Something like

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    { },
    function (options) {
        options.rules['greaterThan'] = true;
        options.messages['greaterThan'] = options.message;
    }
); 

My question here is about 'function (options)'. Is this the function that will be called before 'function (value, element, params)' and is responsible for extracting the unobtrusive tags into a data structure that can be understood by jQuery.Validation. From the code example it seems to me that options is an object that contains both, the attribute values from the tag (such as options.message) and the jQuery relevant properties it must map to (such as options.messages['ClientSideValidationFunctionName']. If so how are custom parameters retrieved and mapped.

I hope I have not added any additional confusion

Thanks in advance

You could use the ValidationParameters property to add custom parameters to the rule:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelClientValidationRule
    {
        ErrorMessage = this.ErrorMessage,
        ValidationType = "futuredate",
    };
    rule.ValidationParameters.Add("param1", "value1");
    rule.ValidationParameters.Add("param2", "value2");
    yield return rule;
}

which could be used in the adapter:

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    [ 'param1', 'param2' ],
    function (options) {
        var param1 = options.params.param1; // shall equal 'value1'
        var param2 = options.params.param2; // shall equal 'value2'
        // TODO: use those custom parameters to define the client rules
    }
);

UPDATE:

As requested in the comments section here's how you could pass those parameters to the custom validator rule function:

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    [ 'param1', 'param2' ],
    function (options) {
        // simply pass the options.params here
        options.rules['greaterThan'] = options.params;
        options.messages['greaterThan'] = options.message;
    }
);

jQuery.validator.addMethod('greaterThan', function (value, element, params) {
    // params here will equal { param1: 'value1', param2: 'value2' }
    return ...
}, '');

Facebook/ Twitter with dotnetopenauth?

9 votes

Hi

I've pretty much got OpenID working using the DotNetOpenAuth library. Now I would like users to be able to use Facebook and Twitter to login.

This requires OAuth so I am looking for a tutorial on how to use DotNetOpenAuth OAuth.

I'm having trouble trying to find some simple tutorials on how to do this.

Can anyone please help?

At the time of me answering this question, DotNetOpenAuth has a public Community Tech Preview (CTP) which is available to download.

Pew Pew

  1. Download that source code. Should be a .7z file. Use 7-Zip to unzip the code if u don't already have that free program. (it should already be your default zip/unzipping application if you're a smart person).
  2. Open up the samples source code solution in Visual Studio. I'm using VS2010 but I think there's also a VS2008 solution file...

More Pew Pew

  1. Now, open up the web.config file for the OAuthClient project. enter image description here

  2. Next, edit the following settings :-

    <!-- Facebook sign-up: http://developers.facebook.com/setup/ -->

    <add key="facebookAppID" value="------"/>

    <add key="facebookAppSecret" value="------------"/>

If you don't know what these values are or should be .. then mate .. I can't help you and programming shouldn't be for you.

Moving right along ...

  1. Debug/Run the OAuthClient Web Application ... which will then kick in an instance of Casini/Visual Studio Developer Server .. and a web browser should then pop open ... and look like this :-

enter image description here

and viola! you can now connect to Facebook to log in. Nice.

If you get errors, then you need to make sure that...

  1. You've manually changed the Facebook app settings in your web.config file.
  2. You've correctly setup your app settings in the Facebook developer website.

GL/HF.

How to keep validation DRY?

8 votes

Using this approach to view models in MVC: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

leaves an unanswered question in my mind. So it is about time I had it cleared up.

If I'm using auto mapper to map domain properties to a dto, then I appreciate that my domain layer can return a set of validation rules when the dto is mapped to a domain entity which is saved.

But, I don't see a DRY way of getting client validation to work and adding the errors to model state so they correspond to the correct property on the view model.

Cheers

I found a related question with some interesting responses:

Mapping Validation Attributes From Domain Entity to DTO

I've been thinking about this, and in a way it is analogous to the situation we have with server-side and client-side validation. (e.g. Using both NHibernate Validator and jQuery.validate).

These days it's pretty well accepted that you should have a full set of server-side validation, and adding client-side validation is an option you can choose in order to make your application more user-friendly. It used to be that you had to implement your client-side validation manually, but you accepted the duplication of work because of the benefit in usability.

I'd argue that what we're dealing with here is very similar. You should have validation in your domain layer. You can't rely on the consuming applications to always add the validation themselves.

You then have the option in your application of adding validation on your DTO/view models. You do this because it's more helpful to deal with validation errors in the view rather than letting them get through to the domain which could throw an exception or give a less helpful error message. The point is that from the domain perspective you don't rely on this being done. You're still confident in your system because you know if any bad data does get through, your model will catch it.

The client/server case is a non-issue these days because so much work has been done to automate it, generating the client-side code from the server-side code (e.g. ModelValidatorProvider in ASP.Net MVC). I believe that as more and more people take up the use of view models/DTOs we'll start to see similar solutions for mapping domain validation onto the DTOs automatically (it's already happening with AutoMapper).

So in short, my (pragmatic rather than ideal ;)) answer is:

Accept the violation of DRY for now, do validation in both places, and try to contribute to projects that aim to automate it in future

ASP.NET MVC / EF4 / POCO / Repository - How to Update Relationships?

8 votes

Hi Guys,

I have a 1..* relationship between Review and Recommendations.

The relevant portion of my model (which is also the POCO mapped by EF4):

public class Review
{
   public ICollection<Recommendations> Recommendations { get; set; }
}

On an Edit View, i represent the Recommendations as a set of checkboxes.

When i try and add a new Recommendation as part of editing the Review (e.g check another box), nothing is happening - and i know why...

I use the "stub technique" to update my entities - e.g i create a entity with the same key, attach it to the graph, then do ApplyCurrentValues. But this only works for scalar properties, not for navigational properties.

I found this StackOverflow question which looks good, but i am trying to work out how to get this to work with POCO's/Repository (and ASP.NET MVC - detached context).

As i'm using POCO's, review.Recommendations is an ICollection<Recommendation>, so i can't do review.Recommendations.Attach. I'm not using Self-Tracking Entities either, so i need to manually work with the graph/change tracking - which hasn't been a problem until now.

So you can visualize the scenario:

Review:

  • Recommendations (ICollection<Recommendation>):
    • RecommendationOne (Recommendation)
    • RecommendationTwo (Recommendation)

If im on the edit view, two of the checkboxes are already checked. The third one (representing RecommendationThree) is unchecked.

But if i check that box, the above model becomes:

Review:

  • Recommendations (ICollection<Recommendation>):
    • RecommendationOne (Recommendation)
    • RecommendationTwo (Recommendation)
    • RecommendationThree (Recommendation)

And so i need to attach RecommendationThree to the graph as a new entity.

Do i need hidden fields to compare the posted data the existing entity? Or should i store the entity in TempData and compare that to the posted entity?

EDIT

To avoid confusion, here is the full app stack call:

ReviewController

[HttpPost]
public ActionResult Edit(Review review)
{
   _service.Update(review); // UserContentService
   _unitOfWork.Commit();
}

UserContentService

public void Update<TPost>(TPost post) where TPost : Post, new()
{
   _repository.Update(post); // GenericRepository<Post>
}

GenericRepository - used as GenericRepository<Post>

public void Update<T2>(T2 entity) where T2 : class, new()
{
   // create stub entity based on entity key, attach to graph.

   // override scalar values
   CurrentContext.ApplyCurrentValues(CurrentEntitySet, entity);
}

So, the Update (or Add or Delete) Repository methods needs to be called for each recommendation, depending it's new/modified/deleted.

Perhaps I need more context but whats wrong with:

recommendations.Add(newRecomendation)

?

In reply to comment:

Ok so whats wrong with

SomeServiceOrRepository.AddNewRecommendation( newRecommendation )

or

SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )

Last Sentence? You mean the two questions?

This shouldn't be hard at all.

To summarize my answer I think you are doing things "the hard way" and really should focus on posting form values that correspond to the CRUD action your trying to accomplish.

If a new entity could come in at the same time as your edited entities you should really prefix them differently so the model binder can pick up on it. Even if you have multiple new items you can use the same [0] syntax just prefix the "name" field with New or something.

A lot of times in this scenario you can't rely on Entity Frameworks graph features because removing an entity from a collection never means it should be set for deletion.

If the form is immutable you could also try using the generized attach function off of ObjectSet:

theContect.ObjectSet<Review>().Attach( review )

Tons of ways out of this. Maybe you could post your controller and view code?

Learning ASP.NET MVC with solid WPF/.NET background

8 votes

I have a solid .NET background except ASP.NET MVC, which I'd like to pick up. In particular I've got a lot of experience with WPF (MVVM), various flavors of Silverlight, LINQ (POCO and XML) and of course the core framework (C#, VB and recently F# as well).

What I'm missing and could be relevant is (obviously besides ASP.NET MVC) LINQ to SQL and the Entity Framework.

I know the basics of "plain old" ASP .NET (but really not that much beyond the basics), but I'm no stranger to SQL, HTML, CSS, JS, etc.

The question: if you did learn ASP.NET MVC3 starting from a background similar to mine, what approach have you found to be the effective (or not) and thus would recommend?

Note: I should mention that I'm interested in best practices and patterns as well. I found out at my expense that this is maybe more important than learning "how stuff work" (for ex. almost every WPF book teaches you everything about templates, binding, etc. but don't mention MVVM or other patterns that are fundamental for a large project).

I tend to read the blogs of some of the developers on the ASP.NET MVC team as well as some other individuals that have good insight into the framework:

  1. Phil Haack
  2. Steve Sanderson
  3. Brad Wilson
  4. Scott Hanselman
  5. Scott Gu (Normally does release announcements and introductions to new features)

Also, this link post by Scott Hanselman has a bunch of ASP.NET MVC 3 links towards the middle that will help supplement the reading you can do at www.asp.net/mvc

Html5 data-* with asp.net mvc TextboxFor html attributes

8 votes

How do I add data-* html attributes using TextboxFor?

This is what I currently have:

@Html.TextBoxFor(model => model.Country.CountryName, new { data-url= Url.Action("CountryContains", "Geo") })

As you see, the - is causing a problem here data-url. Whats the way around this?

You could use underscore (_) and the helper is intelligent enough to do the rest:

@Html.TextBoxFor(
    model => model.Country.CountryName, 
    new { data_url = Url.Action("CountryContains", "Geo") }
)

And for those who want to achieve the same in pre ASP.NET MVC 3 versions they could:

<%= Html.TextBoxFor(
    model => model.Country.CountryName, 
    new Dictionary<string, object> { 
        { "data-url", Url.Action("CountryContains", "Geo") } 
    }
) %>

Recommended approach to port to ASP.NET MVC

7 votes

I think many of us used to face the same question: what's the best practice to port existing web forms App to MVC. The situation for me is that we'll support both web forms and MVC at the same time. It means, we create new features in MVC, while maintaining legacy pages in web forms, and they're all in a same project.

The point is: we want to keep the DRY (do not repeat yourself) principle and reduce duplicate code as much as possible. The ASPX page is not a problem as we only create new features in MVC, but there're still some shared components we want to re-use the both new / legacy pages:

  • Master page
  • UserControl

The question here is: Is that possible to create a common master page / usercontrol that could be used for both web forms and MVC? I know that ViewMasterPage inherits from MasterPage and ViewUserControl inherits from UserControl, so it's maybe OK to let both web forms and MVC ASPX page refer to the MVC version. I did some testing and found sometimes it generates errors during the rendering of usercontrols.

Any idea / experience you can share with me? Very appreciate to it.


Background:

This UI project has been created for years and there're 20+ people working on that. Before I start the common master page trial, there're about 50+ web forms pages and only one MVC page. We create new features on MVC, but the old pages keep remaining in web forms.

This situation will keep for a long time, probably because this's a business-driven company so new features are always in a higher priority. This means we need to support both at the same time.

Sharing MasterPages: see this thread.

User Controls:

This is one of the banes of my existence with MVC; in MVC2 and previous revs, there's no direct equivalent to webforms user controls. A sort-of workaround is creating HtmlHelpers - (effectively extension methods to the Html object available in views that return HTML), but that means you'll have to render your HTML in code. Teh suck.

With MVC3 and the Razor view engine, a new class of Html Helpers is available that provides most of the benefits of user controls, including the ability to place them in separate assemblies (and therefore can be used in multiple projects). I'll see if I can dig up an example link, but Scott Guthrie's blog had an example in one of his recent MVC3/Razor posts.

MVC routing when a file actually exists at the specified location

7 votes

So I have a route like this in my MVC 3 application running under IIS 7:

routes.MapRoute(
               "VirtualTourConfig",
               "virtualtour/config.xml",
               new { controller = "VirtualTour", action = "Config" }
               );

The trick is that a file actually exists at /virtualtour/config.xml. It seems like the request is just returning the xml file at that location instead of hitting the route, which processes the XML, makes some changes and returns a custom XmlResult.

Any suggestions on how I can tell my application to hit the route and not the actual file in the event that the file exists on disk?

EDIT: It appears that I can use routes.RouteExistingFiles = true; in the RegisterRoutes method of Global.asax to tell the application to ignore files on disk. This, however, sets the flag globally and breaks a lot of other requests within the application. For example, I still want calls to /assets/css/site.css to return the CSS file without having to specifically set routes up for each static asset. So now the question becomes, is there a way to do this on a per-route basis?

So far the best answer to this that I have found is to globally apply routes.RouteExistingFiles=true and then selectively ignore the routes I want to pass through to existing files like .js, .css, etc. So I ended up with something like this:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("*.js|css|swf");
            routes.RouteExistingFiles = true;

            routes.MapRoute(
               "VirtualTourConfig",
               "virtualtour/config.xml",
               new { controller = "VirtualTour", action = "Config" }
               );
}

If anyone has a better solution, I'd like to see it. I'd much prefer to selectively apply an "RouteExistingFIles" flag to individual routes but I don't know if there's a way to do that.

ASP.NET MVC - Alternative to Role Provider?

7 votes

Hey there,

I'm trying to avoid the use of the Role Provider and Membership Provider since its way too clumsy in my opinion, and therefore I'm trying to making my own "version" which is less clumsy and more manageable/flexible. Now is my question.. is there an alternative to the Role Provider which is decent? (I know that I can do custom Role provier, membership provider etc.)

By more manageable/flexible I mean that I'm limited to use the Roles static class and not implement directly into my service layer which interact with the database context, instead I'm bound to use the Roles static class which has its own database context etc, also the table names is awful..

Thanks in advance.

I'm in the same boat as you - I've always hated the RoleProviders. Yeah, they're great if you want to get things up and running for a small website, but they're not very realistic. The major downside I've always found is that they tie you directly to ASP.NET.

The way I went for a recent project was defining a couple of interfaces that are part of the service layer (NOTE: I simplified these quite a bit - but you could easily add to them):

public interface IAuthenticationService
{
    bool Login(string username, string password);
    void Logout(User user);
}

public interface IAuthorizationService
{
    bool Authorize(User user, Roles requiredRoles);
}

Then your users could have a Roles enum:

public enum Roles
{
    Accounting = 1,
    Scheduling = 2,
    Prescriptions = 4
    // What ever else you need to define here.
    // Notice all powers of 2 so we can OR them to combine role permissions.
}

public class User
{
    bool IsAdministrator { get; set; }
    Roles Permissions { get; set; }
}

For your IAuthenticationService, you could have a base implementation that does standard password checking and then you could have a FormsAuthenticationService that does a little bit more such as setting the cookie etc. For your AuthorizationService, you'd need something like this:

public class AuthorizationServiceService : IAuthorizationService
{
    public bool Authorize(User userSession, Roles requiredRoles)
    {
        if (userSession.IsAdministrator)
        {
            return true;
        }
        else
        {
            // Check if the roles enum has the specific role bit set.
            return (requiredRoles & user.Roles) == requiredRoles;
        }
    }
}

On top of these base services, you could easily add services to reset passwords etc.

Since you're using MVC, you could do authorization at the action level using an ActionFilter:

public class RequirePermissionFilter : IAuthorizationFilter
{
    #region Fields

    private readonly IAuthorizationService authorizationService;
    private readonly Roles permissions;

    #endregion

    #region Constructors

    public RequirePermissionFilter(IAuthorizationService authorizationService, Roles requiredRoles)
    {
        this.authorizationService = authorizationService;
        this.permissions = requiredRoles;
        this.isAdministrator = isAdministrator;
    }

    #endregion

    #region Methods

    private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
    {
        return this.authorizationService ?? new FormsAuthorizationService(httpContext);
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var authSvc = this.CreateAuthorizationService(filterContext.HttpContext);
        // Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
        var userSession = (User)filterContext.HttpContext.Session["CurrentUser"];

        var success = authSvc.Authorize(userSession, this.permissions);

        if (success)
        {
            // Since authorization is performed at the action level, the authorization code runs
            // after the output caching module. In the worst case this could allow an authorized user
            // to cause the page to be cached, then an unauthorized user would later be served the
            // cached page. We work around this by telling proxies not to cache the sensitive page,
            // then we hook our custom authorization code into the caching mechanism so that we have
            // the final say on whether or not a page should be served from the cache.
            var cache = filterContext.HttpContext.Response.Cache;
            cache.SetProxyMaxAge(new TimeSpan(0));
            cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
            {
                validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
            }, null);
        }
        else
        {
            this.HandleUnauthorizedRequest(filterContext);
        }
    }

    private void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Ajax requests will return status code 500 because we don't want to return the result of the
        // redirect to the login page.
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new HttpStatusCodeResult(500);
        }
        else
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }

    public HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
    {
        var authSvc = this.CreateAuthorizationService(httpContext);
        var userSession = (User)httpContext.Session["CurrentUser"];

        var success = authSvc.Authorize(userSession, this.permissions);

        if (success)
        {
            return HttpValidationStatus.Valid;
        }
        else
        {
            return HttpValidationStatus.IgnoreThisRequest;
        }
    }

    #endregion
}

Which you can then decorate on your controller actions:

[RequirePermission(Roles.Accounting)]
public ViewResult Index()
{
   // ...
}

The advantage of this approach is you can also use dependency injection and an IoC container to wire things up. Also, you can use it across multiple applications (not just your ASP.NET one). You would use your ORM to define the appropriate schema.

If you need more details around the FormsAuthorization/Authentication services or where to go from here, let me know.

EDIT: To add "security trimming", you could do it with an HtmlHelper. This probably needs a little more... but you get the idea.

public static bool SecurityTrim<TModel>(this HtmlHelper<TModel> source, Roles requiredRoles)
{
    var authorizationService = new FormsAuthorizationService();
    var user = (User)HttpContext.Current.Session["CurrentUser"];
    return authorizationService.Authorize(user, requiredRoles);
}

And then inside your view (using Razor syntax here):

@if(Html.SecurityTrim(Roles.Accounting))
{
    <span>Only for accounting</span>
}

EDIT: The UserSession would look something like this:

public class UserSession
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public bool IsAdministrator { get; set; }
    public Roles GetRoles()
    {
         // make the call to the database or whatever here.
         // or just turn this into a property.
    }
}

This way, we don't expose the password hash and all other details inside the session of the current user since they're really not needed for the user's session lifetime.

How to unit test ValueProviderFactories in ASP.NET MVC3?

6 votes

We wanted to upgrade our projects from ASP.NET MVC 2 to 3. Most of our tests succeeded, but there are some that fail on ValueProviderFactories.Factories.GetValueProvider(context).

Here is a simple test class that ilustrates the problem.

[TestFixture]
public class FailingTest
{
  [Test]
  public void Test()
  {
    var type = typeof(string);
    // any controller
    AuthenticationController c = new AuthenticationController();
    var httpContext = new Mock<HttpContextBase>();
    var context = c.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), c);

    IModelBinder converter = ModelBinders.Binders.GetBinder(type);
    var bc = new ModelBindingContext
    {
      ModelName = "testparam",
      ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type),
      ValueProvider = ValueProviderFactories.Factories.GetValueProvider(context)
    };
    Console.WriteLine(converter.BindModel(context, bc));
  }
}

Exception "Object reference not set to an instance of an object." is thrown when ValueProviderFactories.Factories.GetValueProvider(context) is called. The stacktrace looks like this:

Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(System.Web.HttpContext context) + 0x23 bytes   
Microsoft.Web.Infrastructure.dll!Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(System.Web.HttpContext context, out System.Collections.Specialized.NameValueCollection form, out System.Collections.Specialized.NameValueCollection queryString, out System.Collections.Specialized.NameValueCollection headers, out System.Web.HttpCookieCollection cookies) + 0xbe bytes    
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequest request) + 0x73 bytes  
System.Web.WebPages.dll!System.Web.Helpers.Validation.Unvalidated(System.Web.HttpRequestBase request) + 0x25 bytes  
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory..ctor.AnonymousMethod__0(System.Web.Mvc.ControllerContext cc) + 0x5a bytes   
System.Web.Mvc.DLL!System.Web.Mvc.FormValueProviderFactory.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0xa0 bytes    
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider.AnonymousMethod__7(System.Web.Mvc.ValueProviderFactory factory) + 0x4a bytes  
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Web.Mvc.ValueProviderFactory,<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>>.MoveNext() + 0x24d bytes   
System.Core.dll!System.Linq.Enumerable.WhereSelectEnumerableIterator<<>f__AnonymousType2<System.Web.Mvc.ValueProviderFactory,System.Web.Mvc.IValueProvider>,System.Web.Mvc.IValueProvider>.MoveNext() + 0x2ba bytes 
mscorlib.dll!System.Collections.Generic.List<System.Web.Mvc.IValueProvider>.List(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> collection) + 0x1d8 bytes    
System.Core.dll!System.Linq.Enumerable.ToList<System.Web.Mvc.IValueProvider>(System.Collections.Generic.IEnumerable<System.Web.Mvc.IValueProvider> source) + 0xb5 bytes 
System.Web.Mvc.DLL!System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(System.Web.Mvc.ControllerContext controllerContext) + 0x24d bytes 
test.DLL!FailingTest.Test() Line 31 + 0xf9 bytes    C#

I wanted to know the reason why it throws the exception and saw:

public static ValidationUtility.UnvalidatedCollections GetUnvalidatedCollections(HttpContext context)
{
    return (ValidationUtility.UnvalidatedCollections) context.Items[_unvalidatedCollectionsKey];
}

So, are we back in past when we were dependent on HttpContext.Current? How to workaround it?

This can easily be solved by prox-ing ValueProviders that access HttpContext to a the one ignoring it.

I have explained everything in my blog post: Unit test actions with ValueProviderFactories in ASP.NET MVC3.

The key is this code:

public static class ValueProviderFactoresExtensions {
    public static ValueProviderFactoryCollection ReplaceWith<TOriginal>(this ValueProviderFactoryCollection factories, Func<ControllerContext, NameValueCollection> sourceAccessor) {
        var original = factories.FirstOrDefault(x => typeof(TOriginal) == x.GetType());
        if (original != null) {
            var index = factories.IndexOf(original);
            factories[index] = new TestValueProviderFactory(sourceAccessor);
        }
        return factories;
    }

    class TestValueProviderFactory : ValueProviderFactory {
        private readonly Func<ControllerContext, NameValueCollection> sourceAccessor;


        public TestValueProviderFactory(Func<ControllerContext, NameValueCollection> sourceAccessor) {
            this.sourceAccessor = sourceAccessor;
        }


        public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
            return new NameValueCollectionValueProvider(sourceAccessor(controllerContext), CultureInfo.CurrentCulture);
        }
    }        
}

So it can be used as:

ValueProviderFactories.Factories
    .ReplaceWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form)
    .ReplaceWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString);

It was actually very easy :)

MVC 3 Layout Page, Razor Template, and DropdownList

6 votes

I want to include a drop down list of years across all the pages in my website. I assumed a good place to put this logic was in the layout page (_layout.cshtml). If a user changes the year I want to change my year session (ModelBinder) to change as well. This was so easy to do with ASP.NET web forms, but seems near impossible to do in MVC. I tried a partial view with no luck. Anybody have any ideas?

As usual you could start by defining a view model:

public class YearsViewModel
{
    public string Year { get; set; }
    public IEnumerable<SelectListItem> Years
    {
        get
        {
            return new SelectList(
                Enumerable.Range(1900, 112)
                .OrderByDescending(year => year)
                .Select(year => new SelectListItem
                {
                    Value = year.ToString(),
                    Text = year.ToString()
                }
            ), "Value", "Text");
        }
    }
}

Then a controller:

public class YearsController : Controller
{
    public ActionResult Index()
    {
        return View(new YearsViewModel());
    }

    [HttpPost]
    public ActionResult Index(int year)
    {
        // TODO: do something with the selected year
        return new EmptyResult();
    }
}

and a corresponding view for the index action:

@model SomeAppName.Models.YearsViewModel
@{
    Layout = null;
}
@Html.DropDownListFor(x => x.Year, Model.Years)

And finally inside your _Layout.cshtml you could use this controller:

<div id="selectyear">@Html.Action("index", "years")</div>

and attach a corresponding script which would send an AJAX request when the value changes:

$(function () {
    $('#selectyear select').change(function () {
        $.post('@Url.Action("index", "years")', { year: $(this).val() }, function (result) {

        });
    });
});

How to scope out Dbcontexts (to prevent singleton context for entire application)

6 votes

Hello,

I was wondering how do you scope out your Dbcontexts in Entity Framework so you don't use a single Dbcontext for your entire application. I am new to Entity Framework and have been reading tutorials, but they all used a single Dbcontext as an example, so EF is pretty much a blackbox for me right now.

Let's say for example I have 3 models: Post User Comment

Each model is related to each other (A Post belongs to User, Comment belongs to User and Post). Do I make a Dbcontext for each one individually? But that wouldn't be correct since they are all related, or would I make a Dbcontext for each scenario that I need? For example, if I only need to query for Post and Comments and not user, that would be a PostCommentsContext. And then we would have a PostUserCommentContext...

Any help is appreciated! Thanks, Alex

The best solution would be to use a Unit of Work to wrap the Data Context, as well as managing the connection lifetime and allowing you to work with multiple Repositories (if you were so inclined to go down that path).

Summary of implementation:

  • Create an interface (IUnitOfWork) which exposes properties for your DbSet's, as well as a single method called Commit
  • Create an implementation (EntityFrameworkUnitOfWork), implementing as required. Commit simply calls SaveChanges on the base class (DbContext), and also provides a good hook-in for last minute logic.
  • Your controller accepts a IUnitOfWork, use DI (preferably) to resolve a EntityFrameworkUnitOfWork, with a HTTP-context scoped lifetime setting (StructureMap is good for this)
  • (optional, but recommended) create a Repository which also takes the IUnitOfWork, and work off that via your Controller.

HTH

EDIT - In Response to Comments

Oh, how can you do work that involves creating records in multiple models then? i.e., create a new user and a new post in the same transaction.

Given your using ASP.NET MVC, your controllers should accept an IUnitOfWork in their constructor.

Here's an example, based on what you asked

public SomeController : Controller
{
   private IUnitOfWork _unitOfWork;
   private IUserRepo _userRepo;
   private IPostRepo _postRepo;

   public SomeController(IUnitOfWork unitOfWork, IUserRepo userRepo, IPostRepo postRepo)
   {
      _unitOfWork = unitOfWork; // use DI to resolve EntityFrameworkUnitOfWork
      _userRepo = userRepo;
      _postRepo = postRepo;
   }

   [HttpPost]
   public ActionResult CreateUserAndPost(User user, Post post)
   {
      // at this stage, a HTTP request has come in, been resolved to be this Controller
      // your DI container would then see this Controller needs a IUnitOfWork, as well
      // as two Repositories. DI smarts will resolve each dependency.
      // The end result is a single DataContext (wrapped by UoW) shared by all Repos.
      try
      {
         userRepo.Add(user);
         postRepo.Add(post);
         // nothing has been sent to DB yet, only two objects in EF graph set to EntityState.Added
         _unitOfWork.Commit(); // two INSERT's pushed to DB
      }
      catch (Exception exc)
      {
          ModelState.AddError("UhOh", exc.ToString());
      }
   }
}

And one more question, what does the HTTP-context scoped lifetime do?

Objects in DI-talk have scope management settings that include per thread, per session, per http request, singleton, etc.

HTTP-context scoped is the recommended setting for web apps. It means "new up a context when a HTTP request comes in, and get rid of it when the request is finished".

Should I learn asp.NET MVC 3 without knowledge of MVC 1 or 2?

6 votes

Hi guys,

FYI, I know asp.NET and planning to learn asp.NET MVC. I heard that asp.NET MVC 3 has been released . Should I learn MVC 3 right now? or should I start with MVC 1 or 2?

Thanks

The core principles are all the same so learning MVC 3 would be the way to go. "Learning" 1 or 2 first would just mean you'd initial knowledge of the framework is already out of date. I always advice against learning older versions of frameworks because you often have to unlearn things when you get to the newest version of the framework