Best asp.net-mvc questions in February 2011

ASP.NET MVC 3 - Partial vs Display Template vs Editor Template

26 votes

Hi Guys,

So, the title should speak for itself.

To create re-usable components in ASP.NET MVC, we have 3 options (could be others i haven't mentioned):

Partial View:

@Html.Partial(Model.Foo, "SomePartial")

Custom Editor Template:

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

Custom Display Template:

@Html.DisplayFor(model => model.Foo)

In terms of the actual View/HTML, all three implementations are identical:

@model WebApplications.Models.FooObject

<!-- Bunch of HTML -->

So, my question is - when/how do you decide which one of the three to use?

What i'm really looking for is a list of questions to ask yourself before creating one, for which the answers can be used to decide on which template to use.

Here's the 2 things i have found better with EditorFor/DisplayFor:

  1. They respect model hierarchies when rendering HTML helpers (e.g if you have a "Bar" object on your "Foo" model, the HTML elements for "Bar" will be rendered with "Foo.Bar.ElementName", whilst a partial will have "ElementName").

  2. More robust, e.g if you had a List<T> of something in your ViewModel, you could use @Html.DisplayFor(model => model.CollectionOfFoo), and MVC is smart enough to see it's a collection and render out the single display for each item (as opposed to a Partial, which would require an explicit for loop).

I've also heard DisplayFor renders a "read-only" template, but i don't understand that - couldn't i throw a form on there?

Can someone tell me some other reasons? Is there a list/article somewhere comparing the three?

EditorFor vs DisplayFor is simple. The semantics of the methods is to generate edit/insert and display/read only views (respectively). Use DisplayFor when displaying data (i.e. when you generate divs and spans that contain the model values). Use EditorFor when editing/inserting data (i.e. when you generate input tags inside a form).

The above methods are model-centric. This means that they will take the model metadata into account (for example you could annotate your model class with [UIHintAttribute] or [DisplayAttribute] and this would influence which template gets chosen to generate the UI for the model. They are also usually used for data models (i.e. models that represent rows in a database, etc)

On the other hand Partial is view-centric in that you are mostly concerned with choosing the correct partial view. The view doesn't necessarily need a model to function correctly. It can just have a common set of markup that gets reused throughout the site. Of course often times you want to affect the behavior of this partial in which case you might want to pass in an appropriate view model.

You did not ask about @Html.Action which also deserves a mention here. You could think of it as a more powerful version of Partial in that it executes a controller child action and then renders a view (which is usually a partial view). This is important because the child action can execute additional business logic that does not belong in a partial view. For example it could represent a shopping cart component. The reason to use it is to avoid performing the shopping cart-related work in every controller in your application.

Ultimately the choice depends on what is it that you are modelling in your application. Also remember that you can mix and match. For example you could have a partial view that calls the EditorFor helper. It really depends on what your application is and how to factor it to encourage maximum code reuse while avoiding repetition.

Reason to upgrade from ASP.NET MVC2 to MVC3

15 votes

With the release of ASP.NET MVC 3, I am wondering what are the major reasons (killer features) we should upgrade our app to MVC3.

Our app is currently in MVC2 using:

  • Ninject & Ninject.Web for IoC,
  • Linq to Sql
  • jQuery (no ASP.NET Ajax)
  • flot charts.
  • MVC Contrib
  • Custom REST (through dynamic WCF no XML config files)

We do not use ASP.NET charts or EF etc.

Is there any real compelling reason we should upgrade to ASP.NET MVC 3?

Here's a few additional features in ASP.NET MVC 3

  1. Razor view engine (Widely seen as a vast improvement on the previous ASPX view engine)
  2. Improved model validation with unobtrusive JavaScript and jQuery support. Including new remote validation amoungst others
  3. Partial page output caching.
  4. Dependency Injection Improvements, new IDependencyResolver`

More imporvements and details can be found at ASP.NET MVC site along with Scott Guthrie's anouncment

But only you can evaluate whether you should upgrade or not for your specific project.

It should be noted (as Robert Koritnik commented) that MVC 3 requires .NET 4.

How to implement a caching model without violating MVC pattern?

12 votes

Hi Guys,

I have an ASP.NET MVC 3 (Razor) Web Application, with a particular page which is highly database intensive, and user experience is of the upmost priority.

Thus, i am introducing caching on this particular page.

I'm trying to figure out a way to implement this caching pattern whilst keeping my controller thin, like it currently is without caching:

public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences)
{
   var results = _locationService.FindStuffByCriteria(searchPreferences);
   return PartialView("SearchResults", results);
}

As you can see, the controller is very thin, as it should be. It doesn't care about how/where it is getting it's info from - that is the job of the service.

A couple of notes on the flow of control:

  1. Controllers get DI'ed a particular Service, depending on it's area. In this example, this controller get's a LocationService
  2. Services call through to an IQueryable<T> Repository and materialize results into T or ICollection<T>.

How i want to implement caching:

  • I can't use Output Caching - for a few reasons. First of all, this action method is invoked from the client-side (jQuery/AJAX), via [HttpPost], which according to HTTP standards should not be cached as a request. Secondly, i don't want to cache purely based on the HTTP request arguments - the cache logic is a lot more complicated than that - there is actually two-level caching going on.
  • As i hint to above, i need to use regular data-caching, e.g Cache["somekey"] = someObj;.
  • I don't want to implement a generic caching mechanism where all calls via the service go through the cache first - i only want caching on this particular action method.

First thought's would tell me to create another service (which inherits LocationService), and provide the caching workflow there (check cache first, if not there call db, add to cache, return result).

That has two problems:

  1. The services are basic Class Libraries - no references to anything extra. I would need to add a reference to System.Web here.
  2. I would have to access the HTTP Context outside of the web application, which is considered bad practice, not only for testability, but in general - right?

I also thought about using the Models folder in the Web Application (which i currently use only for ViewModels), but having a cache service in a models folder just doesn't sound right.

So - any ideas? Is there a MVC-specific thing (like Action Filter's, for example) i can use here?

General advice/tips would be greatly appreciated.

My answer is based on the assumption that your services implement an interface, for example the type of _locationService is actually ILocationService but is injected with a concrete LocationService. Create a CachingLocationService that implements the ILocationService interface and change your container configuration to inject that caching version of the service to this controller. The CachingLocationService would itself have a dependecy on ILocationService which would be injected with the original LocationService class. It would use this to execute the real business logic and concern itself only with pulling and pushing from cache.

You don't need to create CachingLocationService in the same assembly as the original LocationService. It could be in your web assembly. However, personally I'd put it in the original assembly and add the new reference.

As for adding a dependency on HttpContext; you can remove this by taking a dependency on

Func<HttpContextBase> 

and injecting this at runtime with something like

() => HttpContext.Current

Then in your tests you can mock HttpContextBase, but you may have trouble mocking the Cache object without using something like TypeMock.


Edit: On further reading up on the .NET 4 System.Runtime.Caching namespace, your CachingLocationService should take a dependency on ObjectCache. This is the abstract base class for cache implementations. You could then inject that with System.Runtime.Caching.MemoryCache.Default, for instance.

Publishing an ASP.NET MVC2 site with Web Deploy

11 votes

I currently use Web Deploy, http://learn.iis.net/page.aspx/346/web-deploy/ to publish my MVC2 app. It used to work well, but now it is got to the point where I can't continue using it:

When the MVC app was small and had only a few users it was easy to publish. Just right click the project in Visual Studio and choose "Publish". And because there were only a few users it was easy to find a time when no one was using the site to do a quick update.

Then the app got bigger and had a few more users. The "Publish" action started taking longer and longer and occasionally timing out. Even when I recycled the app pool before deploy it still took a long time.

Also it became harder to find a time when no one was using the site so the update could be done without affecting anyone.

Then the "Publish" action started timing out every single time, and I had to switch to manual deployment as per this earlier unanswered question: Visual Studio 2010 - web deploy times out - what to do?

Now the manual deploy is taking longer and longer, from 5 to 20 minutes. And the number of users has grown significantly, so the deployment always affects someone (slow response times, timeouts, site unavailable, etc)

So what can I do? Is there a better alternative to using web deploy?

Edit:

Today's deployment took 18 minutes to publish just 49 changed files. The situation is just ridiculous and is one of the biggest weaknesses of our site right now. So I'm starting a decent sized bounty in the hopes of solving this.

Some more questions that may lead to a solution:

  • Why would it take so long when only a few files have been changed?
  • Why does the web deploy zip always include the entire codebase and not just changed files?
  • Why don't I just manually copy the changed files myself and skip the whole web deploy? But it is hard to manually work out what files have changed. I use SVN - does it have a way to output only files that have changed between two branches?
  • What other questions should I be asking but haven't thought of yet?

In reply to answers:

Re: http://www.troyhunt.com/2010/11/you-deploying-it-wrong-teamcity_24.html This is exactly how I was doing the deploy, and would be an ideal method. Web deploy does correctly identify which files have changed, however it times out and no publish occurs. There are around 2500 files in the solution, perhaps it is taking too long to identify which ones are changed? Or it could be that publish has a short timeout value and that just uploading the 15mb zip file uses all that time up.

I do have full control over the server, and it does support web deploy. There are actually 2 servers: the primary live server, and a redundant server that we keep ready in case the first falls over. So any solution has to be easy to deploy to more than one server (web deploy was ideal until it stopped working).

The suggestion of creating a new folder for each release and then just changing IIS to point to that new folder sounds like it will result in lower downtime/slowtime when during the publish. But it is a very manual process and I would prefer something more automated.

Edit #2

I have managed to narrow it down, and found exactly where it is slow - but not why. This is from the deploy log:

[9/02/2011 12:11:56 a.m.] Performing synchronization pass #1.
[9/02/2011 12:11:56 a.m.] Parameter entry 'IIS Web Application Name/1' is applicable to 'iisApp/C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp' because of its scope.
[9/02/2011 12:11:56 a.m.] Parameter entry 'IIS Web Application Name/2' is applicable to 'setAcl/C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp' because of its scope.
[9/02/2011 12:11:56 a.m.] Parameter entry 'IIS Web Application Name/2' is applicable to 'setAcl/C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp' because of its scope.
[9/02/2011 12:11:56 a.m.] Parameter entry 'Add write permission to App_Data Folder/1' is applicable to 'setAcl/C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp\App_Data' because of its scope.
[9/02/2011 12:11:56 a.m.] Source createApp (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp) does not match destination (Default Web Site/virtual-dir/) differing in attributes (isDest['False','True']). Update pending.
[9/02/2011 12:11:56 a.m.] Update operation on createApp (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp) skipped because of rule CreateApplicationRule.
[9/02/2011 12:11:56 a.m.] Source filePath (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp\App_Data\Create.sql) does not match destination (Default Web Site/virtual-dir/App_Data\Create.sql) differing in attributes (size['259691','259697'],lastWriteTime['02/08/2011 10:45:20','02/06/2011 03:48:16']). Update pending.

[400 lines of file updates skipped, time expired 2 seconds ....]

[9/02/2011 12:11:58 a.m.] Delete operation on filePath (Default Web Site/v2/zzz_app_offline.htm) skipped because of rule DoNotDeleteRule.
[9/02/2011 12:11:58 a.m.] Source setAcl (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp) does not match destination (Default Web Site/virtual-dir/) differing in attributes (isDest['False','True'],setAclUser,setAclAccess). Update pending.
[9/02/2011 12:11:58 a.m.] Updating setAcl (Default Web Site/virtual-dir/).
[9/02/2011 12:13:47 a.m.] Source setAcl (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp) does not match destination (Default Web Site/virtual-dir/) differing in attributes (isDest['False','True'],setAclUser,setAclAccess). Update pending.
[9/02/2011 12:13:47 a.m.] Updating setAcl (Default Web Site/virtual-dir/).
[9/02/2011 12:17:11 a.m.] Source setAcl (C:\src\Site.2010\Site.UI\obj\Release\Package\PackageTmp\App_Data) does not match destination (Default Web Site/virtual-dir//App_Data) differing in attributes (isDest['False','True'],setAclUser,setAclAccess). Update pending.
[9/02/2011 12:17:11 a.m.] Updating setAcl (Default Web Site/virtual-dir//App_Data).
[9/02/2011 12:17:11 a.m.] The dependency check 'DependencyCheckInUse' found no issues.
[9/02/2011 12:17:11 a.m.] The synchronization completed in 1 pass(es).

The cause of the slowness is the "Updating setAcl" component. I am examining the ACLs of the development box and server box to see what is different. However it seems like an extremely bad idea to copy the ACL from a dev box to a server box! I already had the ACL set up just fine on the server.

I'd start by trying to isolate where the timeout is happening. You've mentioned a 15MB zip with 2,500 files which doesn't strike me as particularly large. Have you tried creating a deployment package in Visual Studio then running it directly on the server? This will take network latency out of the picture which is a pretty fundamental variable when it comes to timeouts.

As for why a zip with the entire application needs to be uploaded, you need to remember the actual identification of what has changed and subsequent deployment into IIS all happens on the server. It's not Visual Studio or msdeploy on your local machine calling the shots on this.

As for why you don't just manually copy the changed files over, it's summarised in my blog post you've referenced but in short, it's laborious and error prone. It means you need to consciously work through the thought process of "which of my 2,500 files just changed" rather than simply saying "make my target site match my development version". You haven't mentioned if you're publishing the web.config or not but obviously config transforms is another important reason why the simple CTRL-C then CTRL-V approach is cumbersome.

Trying to just take a change directly from SVN is also risky. Your first problem is you need to have complete confidence in the integrity and accuracy of the revision you're updating from if you're to get the appropriate changes published. You're then left with trying to sync these to the target and you're back at the same issues raised in the previous paragraph. The other big problem is versioning object code is always nasty; you'll be in a perpetual state of conflict with anyone else on the project and VCS is simply not intended to function this way.

My advice would be to focus on solving the root cause of the problem - Web Deploy is timing out - rather than simply trying to work around the symptoms. Manually publishing changes only or messing around with IIS bindings is only going to create more trouble for you in the long run and a lot more work in the immediate term. See how you go sharing the results of creating a package, copying it to the server then executing it locally and we'll take it from there. Once you have it working as designed, you should be seeing deployments no more than a few minutes and site outage measured in seconds.

BTW - You might also like to add what sort of latency you have between your PC and the server and how long it would normally take to transfer a 15MB file over HTTP.

Where can I find an introduction to a Plugin Pattern for ASP.NET MVC?

11 votes

I am trying to figure out how to implement a "Plugin" framework with asp.net mvc. I have done some reading and found that many people recommended MEF for a plugin framework in asp.net mvc.

link: http://blog.maartenballiauw.be/post/2009/04/21/ASPNET-MVC-and-the-Managed-Extensibility-Framework-%28MEF%29.aspx

However, I'm running into a problem where I can't use ViewModels and other basic mvc components. I know that right now I'm a bit over my head. I'm looking for tutorials, books, and examples of a plugin pattern in action, but I can't find anything. And, most of the MEF documentation I find is a bit over my head (codeplex) or its from years ago before MEF was released in .NET 4.

Any direction/help would be greatly appreciated!!! I am not looking for MEF exclusive information. I've just been focusing on MEF because it's part of the actual .NET framework. I don't know if it can handle what I'm looking for.

Can you recommend any intermediate level resources on this subject?

For your viewmodels, which web.config did you modify? the one at the root or the one in the /Views dir. You need to do the later

Try this one: http://www.fidelitydesign.net/?p=104

ASP.NET MVC - How to Unit Test an Action Method which returns JsonResult?

10 votes

Hi Guys,

If i have a controller like this:

[HttpPost]
public JsonResult FindStuff(string query) 
{
   var results = _repo.GetStuff(query);
   var jsonResult = results.Select(x => new
   {
      id = x.Id,
      name = x.Foo,
      type = x.Bar
   }).ToList();

   return Json(jsonResult);
}

Basically, i grab stuff from my repository, then project it into a List<T> of anonymous types.

How can i unit-test it?

System.Web.Mvc.JsonResult has a property called Data, but it's of type object, as we expected.

So - does that mean if i want to test that the JSON object has the properties i expect ("id", "name", "type"), i have to use reflection?

EDIT:

Here's my test:

// Arrange.
const string autoCompleteQuery = "soho";

// Act.
var actionResult = _controller.FindLocations(autoCompleteQuery);

// Assert.
Assert.IsNotNull(actionResult, "No ActionResult returned from action method.");
dynamic jsonCollection = actionResult.Data;
foreach (dynamic json in jsonCollection)
{
   Assert.IsNotNull(json.id, "JSON record does not contain \"id\" required property.");
   Assert.IsNotNull(json.name, "JSON record does not contain \"name\" required property.");
   Assert.IsNotNull(json.typee, "JSON record does not contain \"type\" required property.");
}

But i get a runtime error in the loop, stating "object does not contain a definition for id".

When i breakpoint, actionResult.Data is defined as a List<T> of anonymous types, so i figure if i enumerate through these, i can check the properties. Inside the loop, the object does have a property called "id" - so not sure what the issue is.

RPM, you look to be correct. I still have much to learn about dynamic and I cannot get Marc's approach to work either. So here is how I was doing it before. You may find it helpful. I just wrote a simple extension method:

    public static object GetReflectedProperty(this object obj, string propertyName)
    {  
        obj.ThrowIfNull("obj");
        propertyName.ThrowIfNull("propertyName");

        PropertyInfo property = obj.GetType().GetProperty(propertyName);

        if (property == null)
        {
            return null;
        }

        return property.GetValue(obj, null);
    }

Then I just use that to do assertions on my Json data:

        JsonResult result = controller.MyAction(...);
                    ...
        Assert.That(result.Data, Is.Not.Null, "There should be some data for the JsonResult");
        Assert.That(result.Data.GetReflectedProperty("page"), Is.EqualTo(page));

Difference between Repository and Service Layer?

10 votes

In OOP Design Patterns, what is the difference between the Repository Pattern and a Service Layer?

I am working on an ASP.NET MVC 3 app, and am trying to understand these design patterns, but my brain is just not getting it...yet!!

Repository Layer gives you additional level of abstraction over data access. Instead of writing

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

to get single item from database, you use repostiory interface

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

and call Get(id). Repository layer exposes basic CRUD operations.

Service layer exposes business logic, which uses repository. Example service could look like:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

While List() method of repository returns all users, ListUsers() of IUserService could return only ones, user has access to.

In ASP.NET MVC + EF + SQL SERVER, I have this flow of communication:

Views <- Controllers -> Service layer -> Repository layer -> EF -> SQL Server

Service layer -> Repository layer -> EF This part operates on models.

Views <- Controllers -> Service layer This part operates on view models.

EDIT:

Example of flow for /Orders/ByClient/5 (we want to see order for specific client):

public class OrderController
{
    private IOrderService _orderService;
    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; //injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _prderService.GetByClient(id);
        return View(model); 
    }
}

This is interface for order service:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

This interface returns view model:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

This is interface implementation. It uses model classes and repository to create view model:

public class OrderService  
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         clientRepository = _clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}

how to cache user details in a partial in mvc

9 votes

In the header of every page I am showing Username and User points. This is being pulled in from a partial (which gets the points from the database).

How can I cache this partial so that across each page I avoid having to check the database for the users points, and after 24hrs it can look again (cache expires). Also if the user logs off and someone else logs in it will show the new username and points (not the previously cached one).

This is user specific so I would store it in a persistent cookie. So when a user authenticates you could query the database to fetch the required information and issue a persistent cookie which will expire in 24h. Then in the partial you would check whether the cookie exists and fetch the necessary data from this cookie and if the cookie doesn't exist query the database and reemit the cookie. When the user logs out you could remove the cookie although that's not strictly necessary because when he logs back in (with the same or other username) you would query the database once again and reemit the cookie.

And because we live in 2011 and HTML5 is knocking on our doors instead of cookies I would probably use the HTML5 Local Storage and if the browser doesn't support it fallback to cookies.

ViewBag vs ViewData performance difference in MVC?

9 votes

I know that ViewData and ViewBag both use the same backing data and that neither are as good as using strongly typed models in most cases. However when choosing between the two is the dynamic nature of ViewBag slower than using ViewData?

Okay - my initial answer basically said 'no' - time for a bit of a u-turn.

It should be 'no' in a perfect dynamic world - but upon closer inspection it would appear that there will either be no difference (accounting for JIT magic) or it might be ever-so-slightly slower, although not enough to warrant not using it (I certainly am).

In theory if properly implemented, the ViewBag would ultimately outperform the use of the ViewData dictionary because the binding of the expressions (e.g. ViewBag.Foo) is very well cached across the different CallSites that the compiler will generate (reflect a method that does a read or write to the ViewBag and you'll see what I mean).

The caching layers of the DLR are well documented (if a little difficult to understand once you get in depth) but basically the runtime does its best to 'remember' where a given value instance is once its bound it - for example via a Set or Get statement.

BUT The caching, its use and effectiveness, is entirely dependent upon the underlying implementations of classes/interfaces such as DynamicObject, IDynamicMetaObjectProvider etc; as well as the end-result of the Get/Set expression binding.

In the case of the MVC internal DynamicViewDataDictionary class - it ultimately ends up binding to this:

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
  result = this.ViewData[binder.Name];
  return true;
}

For var a = ViewBag.Foo

And

public override bool TrySetMember(SetMemberBinder binder, object value)
{
  this.ViewData[binder.Name] = value;
  return true;
}

For ViewBag.Foo = Bar;

In other words - the statements are effectively being rewritten to wrappers around the dictionary indexer.

Because of that, there's certainly no way it could be faster than doing it yourself.

Were ViewData to feed off of ViewBag, instead of the other way around, and had ViewBag then been implemented with even something like ExpandoObject, then it might be a different story - as the dynamic implementation of ExpandoObject is much more intelligent and the caching rules it employs allow for some pretty cool runtime optimisations.

In Conclusion

(thanks to Shawn McLean for suggesting one was needed!)

ViewBag will be slower than ViewData; but probably not enough to warrant concern.

My logging framework is tied to my application forever!

8 votes

Ok, so I'm looking at NLog. Based on the usage, my application would be tied to the logging framework. How do I overcome this?

Also, when using NLog, I have to write too much monkey-code for every class I'm using this framework on. Is it a good practice to make one static class and access it from anywhere in my application?

example:

//the monkey code
private static Logger logger = LogManager.GetCurrentClassLogger();

//the coupling.
logger.Log(/*...*/);

  1. Create your own logging interface:

    public interface IMyOwnLogger {
        void Log(string message);
    }
    
  2. Create implementation:

    public class NLogLogger : IMyOwnLogger {
        void Log(string message) {
            StackFrame frame = new StackFrame(1, false);
            Logger logger = LogManager.GetLogger(frame.GetMethod().DeclaringType.FullName);
            logger.Log(/*...*/);
        }
    }
    
  3. Bind IMyOwnLogger to NLogLogger in your IOC container.

  4. Inject where needed (or use IOC.Get<IMyOwnLogger>()).

EDIT:

Idsa made a comment about loosing calling class. Remember you can always use stack trace:

var method = (new StackTrace()).GetFrame(1).GetMethod()

and extract calling class from there.

EDIT:

This is how GetCurrentClassLogger in NLog looks like, so using StackTrace in our class doesn't create additional overhead:

[MethodImpl(MethodImplOptions.NoInlining)]
public static Logger GetCurrentClassLogger()
{
    #if SILVERLIGHT
    StackFrame frame = new StackTrace().GetFrame(1);
    #else
    StackFrame frame = new StackFrame(1, false);
    #endif

    return globalFactory.GetLogger(frame.GetMethod().DeclaringType.FullName);
}

How does ASP.NET MVC know how to fill your model to feed your Controller's Action? Does it involve reflection?

8 votes

Having defined a Model

public class HomeModel {
    [Required]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required]
    [Display(Name = "Surname")]
    public string Surname { get; set; }
}

and having the following Controller

public class HomeController : Controller {
    [HttpPost]
    public ActionResult Index(HomeModel model) {
        return View(model);
    }

    public ActionResult Index() {

        return View();
    }
}

by some "magic" mechanism HomeModel model gets filled up with values by ASP.NET MVC. Does anyone know how?

From some rudimentary tests, it seems it will look at the POST response and try to match the response objects name with your Model's properties. But to do that I guess it must use reflection? Isn't that inheritably slow?

Thanks

Yes, you are talking about the magic ModelBinder.

ModelBinder is responsible for creating a Model and hydrating it with values from the form post-back and performing validation which its result will appear in ModelState.

Default implementation is DefaultModelBinder but you can plug-in your own.

Is it possible, in MVC3, to have the same controller name in different areas ?

8 votes

In MVC3, I have the following areas:

  • Mobile
  • Sandbox

Then i route maps like this:

    context.MapRoute(
        "Sandbox_default",
        "Sandbox/{controller}/{action}/{id}",
        new { controller = "SandboxHome", action = "Index", id = UrlParameter.Optional }

and

    context.MapRoute(
        "Mobile_default",
        "Mobile/{controller}/{action}/{id}",
        new { controller = "MobileHome", action = "Index", id = UrlParameter.Optional }
    );

The problem is this gives urls like:

http://localhost:58784/Mobile/MobileHome

and

http://localhost:58784/Sandbox/SandboxHome

But I want it like this:

http://localhost:58784/Mobile/Home
http://localhost:58784/Sandbox/Home

The problem is when I rename the SandboxHome-Controller to Home, and the MobileHome-Controller to Home, which would give the desired URLs, it won't compile, saying it has two classes for HomeController.

How can I have the same controller name in different areas ?

Yes.

As explained by this blog post: http://haacked.com/archive/2010/01/12/ambiguous-controller-names.aspx

You need to specify the namespace on the default route in global ASAX to prevent conflicts.

//Map routes for the main site. This specifies a namespace so that areas can have controllers with the same name
routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        new[]{"MyProject.Web.Controllers"}
 );

As long as you keep the Area controllers within their own namespaces. This will work.

EF Entities vs. Service Models vs. View Models (MVC)

8 votes

I'm trying to understand and figure good practices for designing your app/domain models (POCOs/DTOs).

Let's say I have the following database table, Account:

UserID int
Email varchar(50)
PasswordHash varchar(250)
PasswordSalt varchar(250)

Of course, EF4 would build the entity like so:

public class Account
{
    public int UserID { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }
}

Now, let's say I have a view model for registering a new user, which may look something like so:

public class RegistrationViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

Lastly, I have a service which needs to register the user:

public class RegistrationService
{
    public void RegisterUser(??? registration)
    {
        // Do stuff to register user
    }
}

I'm trying to figure out what to pass into the RegisterUser method. The view model is, of course, located under my web app (presentation layer), so I do not want this getting passed to my service.

So, I'm thinking one of four possibilities:

1) Set up a service model that is similar, if not identical, to the RegistrationViewModel, and use this:

public class RegistrationServiceModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(RegistrationServiceModel registration)
    {
        // Do stuff to register user
    }
}

2) Set up an interface of the model, and inherit this in my view model, and set up my method to accept the interface:

public interface IRegistrationModel
{
    string Email;
    string Password;
}

public class RegistrationServiceModel : IRegistrationModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class RegistrationService
{
    public void RegisterUser(IRegistrationModel registration)
    {
        // Do stuff to register user
    }
}

3) Pass in the Account entity, doing the RegistrationViewModel-to-Account mapping in my controller:

public class RegistrationService
{
    public void RegisterUser(Account account)
    {
        // Do stuff to register user
    }
}

4) Move my view model out of the presentation into a domain/service layer, and pass that into the service method:

public class RegistrationService
{
    public void RegisterUser(RegistrationViewModel account)
    {
        // Do stuff to register user
    }
}

None of these three scenarios seem ideal, as I see problems in each of them. So I'm wondering if there's another method I can't think of.

What are good practices for this?

Thanks in advance.

You never pass a view model to the service. A service doesn't even know about the existence of a view model that you might have defined in your presentation tier. A service works with domain models.
Use Auto mapper to map between view model and domain model and vice versa.

Personally, I've never heard of service models in DDD (view models for services).

Problem running MVC3 app in IIS 7

7 votes

I am having a problem getting a MVC 3 project running in IIS7 on a computer running Windows 7 Home-64 bit. Here is what I did.

  1. Installed IIS 7.
  2. Accessed the server and got the IIS welcome page.
  3. Created a directory named d:\MySite and copied the MVC application to it. (The MVC app is just the standard app that is created when you create a new MVC3 project in visual studio. It just displays a home page and an account logon page. It runs fine inside the Visual Studio development server and I also copied it out to my hosting site and it works fine there)
  4. Started IIS management console.
  5. Stopped the default site.
  6. Added a new site named "MySite" with a physical directory of "d:\Mysite"
  7. Changed the application pool named MySite to use .Net Framework 4.0, Integrated pipeline

When I access the site in the browser I get a list of the files in the d:\MySite directory. It is as if IIS is not recognizing the contents of d:\MySite as an MVC application.

What do I need to do to resolve this?

As requested, here is the web.config:

    <?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=152368
  -->

<configuration>
  <connectionStrings>
    <add name="ApplicationServices"
         connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

  <appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
  </appSettings>

  <system.web>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>

    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
    </authentication>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
             enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear/>
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>

    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages"/>
      </namespaces>
    </pages>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

I posted this question on "ServerFault" as well and got a resolution to the issue here.

The answer is:

Since IIS was installed after .NET 4, you likely need to run the aspnet_regiis.exe tool to register all the .NET 4 stuff with IIS.

MVC site search functionality

6 votes

i need a simple site search functionality for my mvc app. some of the pages are static and some dynamic (like news articles that are entered in cms). I would like the search to handle both. is this product any good? http://www.sitesearchasp.net any other?

@stephbu - Thank you for the mention.

If you choose to use arachnode.net, you have the choice of either Lucene.NET or SQL Full-text Indexing.

There are some 'head-scratchers' with Lucene.NET, especially when establishing concurrent read/write/search scenarios, but as a static reflection of content it works very well.

If you want something that is free, and turn-key, try Solr(.Net) or Microsoft Search Server.

http://www.microsoft.com/enterprisesearch/en/us/search-server-express.aspx (this was free last I looked at it...)

Thanks! Mike

Suggestions for jquery table pagination

6 votes

Suggestions for jquery table pagination

I have been using datatables and i really like it :)

Partial Page Caching and VaryByParam in ASP.NET MVC 3

6 votes

I'm attempting to use the new partial page caching available in ASP.NET MVC 3. In my view, I'm using:

<% Html.RenderAction("RenderContent", Model); %>

Which calls the controller method:

[Authorize]
[OutputCache(Duration = 6000, VaryByParam = "*", VaryByCustom = "browser")]
public ActionResult RenderContent(Content content)
{
   return PartialView(content);
}

Note that both the original view and the partial view are using the same view model.

The problem is that VaryByParam doesn't work - RenderContent() always returns the same cached HTML no matter what view model is passed to it. Is there something about VaryByParam that I don't understand?

I think I figured it out. It looks like the issue is that VaryByParam, when the input parameter is an object, uses ToString() on that object to determine it's uniqueness. So this leaves two options:

  1. Overriding ToString() to provide a unique identifier.
  2. Passing a unique identifier as an additional parameter:

    <% Html.RenderAction("RenderContent", Model, Model.Id); %>
    
    [Authorize]
    [OutputCache(Duration = 6000, VaryByParam = "id", VaryByCustom = "browser")]
    public ActionResult RenderContent(Content content, string id)
    {
       return PartialView(content);
    }
    

Which exception should be thrown?

6 votes

I wrote a custom action method selector attribute that has three bool properties. It's invalid for all three of them to be false. At least one of them has to be true. When IsValidForRequest gets executed I check that at least one of them is true. But if none is, which exception should I throw?

Some relevant code:

public class MyCustomAttribute : ActionMethodSelectorAttribute
{
    public bool Prop1 { get; set; }
    public bool Prop2 { get; set; }
    public bool Prop3 { get; set; }

    public MyCustomAttribute()
    {
        this.Prop1 = true;
        this.Prop2 = true;
        this.Prop3 = true;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }

        // at least one of them must be true
        if (!this.Prop1 && !this.Prop2 && !this.Prop3)
        {
            throw new ?????
        }

        // other codez here
    }
}

Attributes have this nice ability of initializing them while also providing property values, so I have to check them in the IsValidForRequest method.

[MyCustom(Prop1 = false, Prop2 = false, Prop3 = false)]

Which exception should be thrown?

I'd probably throw InvalidOperationException, because the operation is not valid for the object's current state.

Ajax.ActionLink not working, Response.IsAjaxRequest() is always false

4 votes

Hi. I have been googling/SO:ing this issue for a while and many seem to be sharing this, but I haven't found any successful solution to my problem.

Using MVC3 and Razor.

  1. Master page contains:

    <script src="@Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>

    <script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>

    <script src="@Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>

  2. AjaxTest.cshtml contains:

    <div id="AjaxTestDiv">content</div>

    @Ajax.ActionLink("Update", "AjaxTester", new AjaxOptions { UpdateTargetId = "AjaxTestDiv" })

  3. AjaxTester action method:

    public string AjaxTester()
    {
        if (Request.IsAjaxRequest())
        {
            return DateTime.Now.ToString();
        }
        else
        {
            return "FAIL";
        }
    }
    

I always get the "FAIL" returned, to a blank page, not in the targeted div.

Edit: Also note that if I remove the if (Request.IsAjaxRequest()), I still don't get back anything to the targeted div, but instead a blank page.

Edit2: Looking at the HTML generated, this is my link:

<a data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace"
data-ajax-update="#AjaxTestDiv" href="/Area/AjaxTester">Update</a>

Have tried switching the method to GET, to no avail.

By default ASP.NET MVC 3 uses unobtrusive jquery with all the Ajax.* helpers. So start by getting rid off all MicrosoftAjax scripts (this useless c**p) and put the following instead:

<script src="@Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

and then simply activate unobtrusive AJAX in your web.config (if not already done):

<appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

Now jquery is going to unobtrusively AJAXify all the links containing those HTML 5 data-* attributes.

Or even better IMHO:

In your view simply:

@Html.ActionLink("Update", "AjaxTester", new { id = "mylink" })

and in a separate javascript file AJAXify this anchor:

$(function() {
    $('#mylink').click(function() {
        $('#AjaxTestDiv').load(this.href);
        return false;
    });
});

Converting C# Razor to VB

3 votes

I'm following the ASP.NET MVC Tutorial and having started in VB.NET I'm having trouble converting the following razor code:

enter image description here

I have got

<ul>
    @For Each g As MvcApplication1.Genre In Model
        <li> @g.Name </li>
    Next

</ul>

but getting

Attribute Sepcifier is not a complete statement

on both the <li> tags. I understand I need to use line continuation but can't figure out where. I'd be greatful if you can point out the problem.

Put an @ before the li:

<ul>
    @For Each g As MvcApplication1.Genre In Model
        @<li>@g.Name</li>
    Next
</ul>

I would recommend you the following article.