Feed

Ever since I started discovering the MVC world I decided to completely forget Web Forms and actually adopt asp.net MVC as my web programming framework of choice. What's great about it is that you have complete control over the generated html output and absolute separation of business logic, presentation layer and model. It's all about convention over configuration! This is one of the reasons why I decided to learn Ruby on Rails some time ago. It is clear that the ASP.NET MVC .NET Framework 3.5 SP1 release was somewhat influenced by Rails.

One of the great things about ASP.NET MVC is model binding. K. Scott Allen wrote a great blog post with tips on binding. Basically, it collects your request data and fills up your view model with the incoming data.

There is a search pattern that the DefaultModelBinder uses to look for the data in the request. Basically you need to name your form elements with the same name of your model properties. For example:

1. The view model:

   1:  public class NewsModuleViewModel
   2:  {
   3:      [ScaffoldColumn(false)]
   4:      public int ID { get; set; }
   5:   
   6:      [DisplayName("News Date")]
   7:      [DisplayFormat(DataFormatString = "d", ApplyFormatInEditMode = true)]
   8:      [Required(ErrorMessage = "Por favor selecione uma data")]
   9:      public DateTime? Date { get; set; }
  10:   
  11:      [DisplayName("Title")]
  12:      [Required(ErrorMessage = "Por favor digite um título")]
  13:      public string Title { get; set; }
  14:   
  15:      [DisplayName("Resume")]
  16:      public string Resume { get; set; }
  17:   
  18:      [DisplayName("My Text")]
  19:      [UIHint("HtmlText")]
  20:      [Required(ErrorMessage = "Por favor digite um texto para a notícia")]
  21:      public string Text { get; set; }
  22:   
  23:      [DisplayName("Image")]
  24:      [UIHint("DbImage", null, "AspectRatio", 1.0)]
  25:      public string ImagePath { get; set; }
  26:  }

2. The form:

   1:  <form action="/uac/NewsModule/Edit/12" enctype="multipart/form-data" method="post">
   2:   
   3:  <p class="datalist-form">
   4:      <label for="Date">Date</label>
   5:      <input type="text" id="Date" name="Date" value="22/1/2010" />
   6:  </p>
   7:  <p class="datalist-form">
   8:      <label for="Title">Title</label>
   9:      <input class="text-box single-line" id="Title" name="Title" type="text" />
  10:  </p>
  11:  <p class="datalist-form">
  12:      <label for="Resume">Resume</label>
  13:      <input class="text-box single-line" id="Resume" name="Resume" type="text" />
  14:  </p>  

(I've reduced the HTML markup snippet for the sake of simplicity).

Please note that the input field name properties match the view model property names. The DefaultModelBinder will look for these names when doing the data binding!

You can even bind complex objects like lists, for example, but, in this case, you'll have to name your input fields according to the element index on the list! Eg.:

   1:  <input type="text" name="ImageList[0].FileName" size="44" />

It is really straightforward once you get used to it. Just always make sure to create a ViewModel class for you, instead of using the data classes directly (eg.: with LINQ to SQL).

If you need to bind uploaded files as well, it gets a bit more complicated. The best solution I've found so far was not to bind file input control and later retrieve the uploaded file in the controller action from the Request.Files collection.

Tags:

With ASP.NET Dynamic Data you get an entire dynamic data-driven website for free with almost no effort. Of course this has drawbacks, which is that in fact you lose some flexibility and need to do things in the way they can be done.

Recently I needed to improve the default List.aspx template GridView with some different colors and per-line selection for entry details, instead of clicking through an DynamicHyperlink. The first step was to attach an OnRowDataBound event in the grid, in order to set up on onmouseover event in the data rows:

DynamicData\PageTemplates\List.aspx:

<asp:GridView ID="GridView1" runat="server" DataSourceID="GridDataSource" EnablePersistedSelection="true" OnRowDataBound="GridView_RowDataBound">

Next, attach the event and set up the event (DynamicData\PageTemplates\List.aspx.cs):

protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        e.Row.Attributes["onclick"] = "location.href = '" + table.GetActionPath("Details", e.Row.DataItem) + "'";
    }
}

And I'ts all done. You get an entire row click selection for the entry details :)

 

ASP.NET offers a powerful built-in provider-based support for membership, roles and profile management. That means you get a fully functional set of features for free on your project just out of the box.

I've been working in order to integrate membership and profile management into a MVC application I am developing, so I had to refresh my mind on these topics. There are some gotchas around these topics that are worth sharing.

First of all, the default ASP.NET providers implementation for membership, roles and profiles are based on SQL Server database storage, so you'll have to configure one to start playing. A gotcha here is to run the aspnet_regsql tool to generate the required tables, schemas, stored procedures, etc. in order for the providers to work. Do not forget also to configure them correctly in the web.config file, including the connectionStringName and applicationName properties (very important!).

There are several resources and tutorials around the net explaining how to set up these providers to start working. For profile management, I recommend this one. Profiles are the way to go if you need to include additional information pertaining your application users, so, instead of creating a custom membership provider, get along with the default one and set up profile properties in the web.config file. These settings are then stored in the database and associated with the user that is currently logged on. It is possible to add profile data to anonymous users, also, which is great!

So, in order to integrate it with your MVC application, you can easily store and retrieve your profile properties inside your Controller with this.HttpContext.Profile["ProfilePropertyName"].

Also, do not forget to declare your property in the web.config file this way:

 <profile defaultProvider="AspNetSqlProfileProvider">
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider"
             type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
             connectionStringName="tagsdConnectionString"
             applicationName="/"
                />
      </providers>
      <properties>
        <add name="ProfilePropertyName" type="System.String"/>
      </properties>
    </profile>

Handling exceptions in ASP.NET MVC is quite easy once you get to know the details surrounding the HandleError attribute. You can specify that a controller or a specific Action will handle errors and you can even choose which View to show based on the exception being thrown. Scott Guthrie wrote about this.

What he didn't tell us, is that there are some prerequisites for it to work as expected. First of all, you need to enable custom errors in the application web.config file:

<customErrors mode="On" />

Second, the order that you put the HandleError attributes will affect which View will actualy gonna be rendered at the end. I was trying to render a custom view based on an InvalidOperationException being throw on my action. The code looked like this:

   1:  [HandleError]
   2:  public class HomeController : QuavioController
   3:  {
   4:          [HandleError(ExceptionType = typeof(InvalidOperationException), View = "InvalidOpJoinRoom")]
   5:          public ActionResult JoinRoom()
   6:          {
   7:               throw new InvalidOperationException();
   8:          }
   9:  }

What happened is that the JoinRoom was returning the shared Error View, instead of the InvalidOpJoinRoom View. This happens because the HomeController contains a HandleError attribute (line 1), which overrides the JoinRoom's HandleError behavior (line 4), which should return the InvalidOpJoinRoom View.

The fix is very simple: Remove the HandleError atribute from the controller and let the actual actions define the proper error handling behavior.

Conclusion: Be aware that multiple HandleError attributes in a controller may alter the order which View evaluation is executed and then causing the final View not to be the view you were actually expecting. 

Tags:

ASP.NET MVC and IIS 6

by Felipe Lima 21/10/2009 07:01

I've been playing lately with ASP.NET MVC 2 Preview 2. I had no previous experience with this and it has been kind of a change in paradigms changing from web forms to mvc. At the same time, I feel very excited about this and believe that It is a big step forward when compared to web forms, mainly due to the separation of concerns and responsibilities.

As a drawback, It is very weird not to be able to drop ASP.NET server controls in the page. Initially, you'll feel a bit 'lost'. MVC uses a different method to render the page. You don't have the runat='server' stuff anymore so, instead, you'll have to render the page mostly based on what you have in your ViewData. It's not my objective to explain its functionality here, but you can find further information at the official MVC website. The latest release can be downloaded from CodePlex.

While testing my application in my hosting provider, I had a big headache since they have Windows 2003 Server installed, which comes with IIS 6. ASP.NET MVC requires a bit of work in order to work with IIS6, especially if your hosting provider does not have MVC installed in the GAC.

I would definitely suggest Phil Haack's blog as the first point for reference on this subject. There are two key posts regarding this issue that I followed in order to make it work.

First of all, MVC assembly needs to be deployed in the application Bin directory if it is not installed in the server. Detailed info here.

Second, you'll need to change the application routes for IIS 6 to correctly receive the requests and forward them to the controllers. Haacked.com walkthrough here.

As a last tip, I also found out that my MVC application wouldn't work if I used VS Publish tool to send it to the production server after following the two steps above. No clue why, but just copying the whole website files to the server worked fine. Weird!

Tags:

CKEditor is a great open source wysiwyg editor for the web. It was formerly called FCKEditor and, one of its previous versions contained a nice implementation for ASP.NET integration. The current version (3.0) has been completely rebuilt from scratch and does not support quick upload yet (I read in the forums it will be available in version 3.1).

I've been using the FCKEditor  2.6.3 for .NET, which includes an ASP.NET control for seamless integration. However, I could never make one of its great features to work: the ability to upload images on the fly and embed them in the text. There are some things that need to be set up before.

1. FCKEditor config file (fckconfig.js), when you download it, comes with its connectors default language to php. You need to change it to aspx:
    var _FileBrowserLanguage = 'aspx' ; // asp | aspx | cfm | lasso | perl | php | py 
    var _QuickUploadLanguage = 'aspx' ; // asp | aspx | cfm | lasso | perl | php | py  

2. Configure the folder that contains the fckeditor scripts in FCKEditor.cs (You will need to create a virtual directory in IIS with this path):

[ DefaultValue( "/fckeditor/" ) ]

public string BasePath 

3. Enable the upload connector in fckeditor\editor\filemanager\connectors\aspx\config.ascx (SECURITY WARNING: do not forget to implement authentication verification here!):

private bool CheckAuthentication()

{ return true; }

4. Configure the directory that will receive the uploaded images (same file as above) - This can be overriden in your website web.config appSettings section, creating a setting called FCKeditor:UserFilesPath

UserFilesPath = "/userfiles/";

 

This should be  everything that is needed in order to make file upload to work. See ya!

 

[UPDATE]:

For those of you that saw this working fine in your development machine but unexplainably not working on the production server, please refer to this forum post for the fix.

[UPDATE 2]:

Another issue that I found regarding the update above is that in a server, it did not fix the problem an I was getting a 'permission denied' JS error on the window.parent.OnUploadCompleted event. If that happens, try this fix (scroll to the bottom of the page for the code)

I had to temporarily disable comments on this blog due to the huge amount of automated spam comments I have been receiving lately. I'll enable captcha challenges on the comments posting and reenable it once this is done!

Sorry for the inconvenience, people. See you!

Tags: ,

If you've been on the latest news on the web development standards, you probably know that HTML version 5 is slowly being adopted by mainstream browsers, despite it has not reached its final release yet (it is still a draft). I've always been a fan of the impressive results that you can get with Adobe Flash and, since it is today present in almost 99% of the browsers, it has been almost a no-brainer for anyone that wanted to create a highly dynamic and interactive website for the last couple years. Despite its many drawbacks, Flash has managed to become almost ubiquitous and extremely popular in the web. Just to cite a few of these weaknesses:

  • It is a proprietary technology held by Adobe. This, by definition, is not good for the community, since we're all in Adobe's hands;
  • Even after 10 major releases during the last 13 years, Adobe did not managed to create a decent IDE for it, which today still relies mainly on a timeframe for movie clip creation and has poor debugging support;
  • Its programming language, ActionScript, which only recently became more robust with its version 3, is based on ECMAScript (prototype oriented), which implies loose typing and few compile time checks -> more runtime errors.

Now that HTML5 is here, we're clearly seeing that Flash is slowly and progressively losing market share, mostly because of the new canvas element introduced with this standard. Canvas allows 2D vector based drawing directly to the browser. This opens an entire new world of possibilities to web development, allowing creators to leverage the HTML 5 open standard to switch from Flash to canvas and, therefore, forcing browsers to implement it and improve rendering performance. Other elements like audio and video embedding are also planned to be available in this standard.

Performance is another topic apart that, however, cannot be ignored. Some of the latest canvas experiments in the web show that we have a severe performance reduction with canvas, when compared to Flash, especially when it comes to animations and 3D. This is something that still blows me today: How do browsers manage to have such a big performance difference? Google Chrome, which is the fastest browser i've ever seen, cannot be compared to Firefox when it comes to JavaScript performance, not to mention Internet Explorer, which must be some kind of joke of internet browsers. Even its latest version 8 did not manage to have half of the Chrome speed. I cannot understand what is so hard about JavaScript processing which makes it such a processor intensive task even for the modern browsers.

I really wish to see the web in the near future in a very different way than what we have now. With the adpotion of HTML 5 by all mainstream browsers (IE does not implement it yet), I hope we can all abandon Flash for good and focus on improving browsers performance to be able to afford with canvas the same interactivity that we have today with Flash. Besides that, I'd like to see more and more improvements to JavaScript frameworks like jQuery and hopefully, we'll enter Web 3.0 with an entire new set of web applications!

While trying to use the Session state dictionary in a web application, you may eventually receive the following exception:

Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the <configuration>\<system.web>\<httpModules> section in the application configuration.

To my surprise, session state has to be enabled before it can be used. I followed some steps that I found while digging for the solution online that might be helpful:

1. Enable the ASP.NET Session State Manager Service (under Control Panel > Administrative Tools > Services)

2. Enable it in the web.config file: <pages enableSessionState="true"> 

3. Add the proper http module to the web.config: <add name="Session" type="System.Web.SessionState.SessionStateModule" />

4. At last but not least: Make sure you're accessing the Session object after it has been initialized. This is very important, since if you try to manipulate it in the page constructor, for example (which was my case), you'll still get the above exception, even after executing steps 1, 2 and 3. You should be able to access it after the page OnLoad event has been fired.

Tags:

How to use the results of a database query using Linq to fill a DataGrid? It should be no big deal since there is a useful CopyToDataTable extension method:

   1:  public static DataTable CopyToDataTable<T>(
   2:      this IEnumerable<T> source
   3:  )
   4:  where T : DataRow

 However, there is a problem: T has to be a DataRow. When returning query results from the database, especially with EF, your query results are not DataRows (and I wonder if the same is true for Linq to Sql too). So, in this case, you'll have to fill the DataTable by hand. Thanks to this good blog post, I was able to fill it:

   1:  DataTable dataTable = new DataTable();
   2:  dataTable.TableName = "ResultsTable";
   3:  dataTable.Columns.Add("User", typeof(string));
   4:  dataTable.Columns.Add("TotalJobs", typeof(int));
   5:   
   6:  var query = /* your linq query comes here. no restrictions! */

we have the DataTable and the query set up. Now, the tricky part!

   1:  var results = query.Select(anonym => new Func<DataRow, int, int, DataRow>(
   2:      (DataRow row, int index, int count) =>
   3:      {
   4:          row["User"] = index;
   5:          row["TotalJobs"] = count;
   6:          return row;
   7:      })
   8:      .Invoke(dataTable.NewRow(), anonym.Index, anonym.TotalJobs));
   9:   
  10:  results.ToList().ForEach(row => dataTable.Rows.Add(row));

Absolutely a good piece of unreadable code :)

query variable are your Linq query results, which may be an IEnumerable of some of your data entities or even a anonymous type. It doesn't matter! For each element of this enumeration, we're creating a DataRow and assigning some values to its columns. Then, we call Invoke to indeed invoke our anonymous delegate Func, passing in our anonymous type properties, that are used later to fill the DataTable.

You'll probably gonna need to read it a couple times to digest it a bit and still it will look "WTF did I really wrote this code?!?" after a couple days :)

Tags: ,

About the Author

myself
My name is Felipe Lima and I am a Software Engineer and co-founder at Quavio, a digital agency at Porto Alegre, Brazil. I am interested in technology, programming with the .Net Framework and sports. Check out my full profile.

Powered by BlogEngine.NET 1.4.5.0