Doug.Instance

Improving the world one post at a time
posts - 17, comments - 33, trackbacks - 0

My Links

News

Tag Cloud

Archives

Post Categories

My Sites

Friday, April 20, 2012

Two Best SharePoint Utility Methods Ever Written

SharePoint is very handy as a repository for data.  There are things (particularly in the intranet world) which really don't need a dedicated database or application and SharePoint is a great enabler.  However, if you want to build a lightweight, standards-friendly web page using SharePoint, you have a challenge ahead of you.

Here is the problem I was trying to solve:  I have a SharePoint site that serves as the home page for an organization.  I needed to be able to pull in various content from other sites (on other servers) and present it in a nice, clean layout.  In order to keep the maintenance of this page as simple as possible, I wanted to use JavaScript/jQuery to fetch data and then use as much standard HTML/CSS for the page itself as possible.

Luckily, SharePoint has several client APIs.  There are some fairly easy ways you can get data from SharePoint lists (the fundamental data structure used by SharePoint).  I was able to find a workable solution rather quickly to pull list data in using the ASP.Net web services via jQuery.  This worked just fine until I found some content on a different server and ran into the cross-site scripting limitations of jQuery ajax.

In order to continue, I knew I would have to use JSONP.  The easiest way I could think of to do this would be to write some code to act as an intermediary between the JavaScript client and the SharePoint servers.  This intermediary utility was broken up into two methods to perform these 2 functions:

  1. Retrieve a SharePoint list as an ADO.Net DataSet.
  2. Retrieve a SharePoint list as JSONP.

The first step was to create an ASP.Net MVC project and add references to Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime. 

Next I created the method to get a DataSet.  I understand DataSets aren't the "best" vehicle for transferring data, but they are ubiquitous so I felt this was the best short-term method to ensure potential reuse.  That method looks like this:

   1:          public static DataSet GetListAsDataSet(string serverUrl, string listName, string query)
   2:          {
   3:              ClientContext context = new ClientContext(serverUrl);
   4:   
   5:              List list = context.Web.Lists.GetByTitle(listName);
   6:   
   7:              if (list != null)
   8:              {
   9:                  context.Load(list);
  10:   
  11:                  FieldCollection fields = list.Fields;
  12:   
  13:                  context.Load(fields);
  14:                  context.ExecuteQuery();
  15:   
  16:                  DataSet ds = new DataSet();
  17:                  DataTable table = new DataTable();
  18:                  ds.Tables.Add(table);
  19:   
  20:                  foreach (Field field in fields)
  21:                  {
  22:                      if (!table.Columns.Cast<DataColumn>().Any(c => c.ColumnName == field.Title))
  23:                      {
  24:                          table.Columns.Add(field.Title, typeof(object));
  25:                          string temp = field.InternalName;
  26:                      }
  27:                  }
  28:   
  29:                  CamlQuery camlQuery = new CamlQuery();
  30:   
  31:                  if (!string.IsNullOrEmpty(query))
  32:                  {
  33:                      camlQuery.ViewXml = query;
  34:                  }
  35:   
  36:                  ListItemCollection items = list.GetItems(camlQuery);
  37:   
  38:                  context.Load(items);
  39:                  context.ExecuteQuery();
  40:   
  41:                  foreach (ListItem item in items)
  42:                  {
  43:                      DataRow row = table.NewRow();
  44:                      table.Rows.Add(row);
  45:   
  46:                      foreach (Field field in fields)
  47:                      {
  48:                          string key = field.Title;
  49:   
  50:                          if (item.FieldValues.ContainsKey(field.InternalName))
  51:                          {
  52:                              if (item[field.InternalName] == null)
  53:                              {
  54:                                  row[key] = DBNull.Value;
  55:                              }
  56:                              else
  57:                              {
  58:                                  row[key] = item[field.InternalName];
  59:                              }
  60:                          }
  61:                      }
  62:                  }
  63:   
  64:                  return ds;
  65:              }
  66:   
  67:              return null;
  68:          }

I then created an action method in my MVC controller to get the DataSet created by the list and convert it to an object with 2 properties.  The "fields" property contains an array of field names.  The "rowData" property contains an array of arrays representing rows and columns of data.  That method looks like this:

   1:          public ActionResult GetListAsJsonp(string server, string listName, int? rowLimit, string callback)
   2:          {
   3:              string query = null;
   4:   
   5:              if (rowLimit.HasValue)
   6:              {
   7:                  query = string.Format("<View><RowLimit>{0}</RowLimit></View>", rowLimit);
   8:              }
   9:   
  10:              DataSet ds = Quality.Common.SharePoint.ListHelper.GetListAsDataSet(server, listName, query);
  11:   
  12:              if (ds == null || ds.Tables.Count == 0)
  13:              {
  14:                  return new EmptyResult();
  15:              }
  16:   
  17:              DataTable table = ds.Tables[0];
  18:   
  19:              JavaScriptSerializer serializer = new JavaScriptSerializer();
  20:   
  21:              List<object> rowData = new List<object>();
  22:   
  23:              foreach (DataRow row in table.Rows)
  24:              {
  25:                  rowData.Add(row.ItemArray);
  26:              }
  27:   
  28:              var data = new { fields = table.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToArray(), rowData = rowData.ToArray() };
  29:   
  30:              return Content(string.Format("{0}({1});", callback, serializer.Serialize(data)), "application/json");
  31:          }

You can then use this MVC action like this:

   1:  function getList() {
   2:      $("#test").empty();
   3:      $("#test").append("Fetching data...<br/>");
   4:      $.ajax({
   5:          url: "http://myserver/Home/GetListAsJsonp/?server=" + $("#siteUrl").val() + "&listName=" + $("#listName").val() + "&rowLimit=5",
   6:          type: 'GET',
   7:          dataType: "jsonp",
   8:          success: testResult,
   9:          error: function (err, msg) { alert(msg); }
  10:      });
  11:  }
  12:   
  13:  function testResult(data) {
  14:      $("#test").append("Got data.<br/>");
  15:   
  16:      var output = "<table>";
  17:      
  18:      output = output + "<tr>"
  19:   
  20:      for (i in data.fields) {
  21:          output = output + "<th>";
  22:          output = output + data.fields[i];
  23:          output = output + "</th>";
  24:      }
  25:   
  26:      for (index in data.rowData) {
  27:          
  28:          output = output + "<tr>";
  29:          
  30:          for (i in data.fields) {
  31:              output = output + "<td>";
  32:              output = output + data.rowData[index][i];
  33:              output = output + "</td>";
  34:          }
  35:   
  36:          output = output + "</tr>";
  37:      }
  38:   
  39:      output = output + "</table>";
  40:   
  41:      $("#test").append(output);
  42:  }

Now when I say these are the best methods ever written, that doesn't mean this is the best code. It's just very useful in my opinion.  Hope you agree.

Posted On Friday, April 20, 2012 10:15 AM | Feedback (0) | Filed Under [ MVC jQuery SharePoint ]

Thursday, February 16, 2012

The Enemy of My Friend Writes Bad MVC Controller Actions (Microsoft)

Recommendation: Don't use the same action names for get and (Ajax) post.

I am always amazed at how bad Microsoft's code examples are.  Code generated using the default templates in Visual Studio is not much better.  To find out, create an empty project (pick your favorite type) and run static code analysis or FxCop and see how many warnings you see.  For some real fun try running StyleCop.  The default templates (and therefore commonly-used standard practices) are also not very good in my opinion.  Consider the following controller code generated using the standard MVC 3 template:

        //
        // GET: /Test/Create

        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /Test/Create

        [HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            try
            {
                // TODO: Add insert logic here

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

For those of you who have used MVC for a while, this looks pretty standard.  "So what's the big deal?" you ask. Here is my beef: there is no value to having two controller actions named "Create".  My personal opinion, is that this convention was used to smooth the transition from web forms.  The first Create method would be similar to a Page_Load event handler and the second one would be similar to a Button_Click event handler.  To use more web form language, the first method handles the initial request and the second one handles the "postback".  For people used to dealing with web forms, it makes it more obvious that these two methods are dealing with the same "page".  The only real advantage is that you don't have to specify the action name in Html.BeginForm.  This can be a good thing because it eliminates a magic string from your view.

So let's examine this more closely and see where we start running into problems.  For the sake of argument, let's say that this is a search page.  Therefore you want to use a GET transaction instead of POST so the user can see the URL, bookmark it to re-run their search, etc.  You remove the [HttpPost] attribute. You will now get an error that the action is ambiguous between action methods.

The solution to this problem is simple, just rename one of the methods. My recommendation is to call the first method "New". This then maps to the nice route "/{controller}/New".  In general this is something users expect to see in a URL - what they are looking at so I like to use nouns or adjectives for these actions (Details, New, Info, Order, Product, etc.).  The form would then call the "Create" action.  In this case I like to make my actions that do something use verbs (Create, Update, Delete, List, Rename, etc.).

This convention really starts to make even more sense once you start having multiple Ajax calls from a single page.  After all, why use a paradigm based on full-page posting (Html.BeginForm in MVC) when most applications will use partial-page posts or other script-based methods?

Posted On Thursday, February 16, 2012 11:29 PM | Feedback (5) | Filed Under [ MVC ]

Tuesday, February 07, 2012

Problem: DropDownList Always Posts Blank Value in ASP.Net MVC 2

Consider the following code:

<%= Html.DropDownList("State", new SelectList(new string[] { "","AK","AL","AR","AS","AZ","CA","CO","CT","DC","DE","FL","GA","GU","HI","IA","ID",
                    "IL","IN","KS","KY","LA","MA","MD","ME","MH","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY",
                    "OH","OK","OR","PA","PR","PW","RI","SC","SD","TN","TX","UT","VA","VI","VT","WA","WI","WV","WY"})) %>

Simple enough, right?  Display a dropdown list with a value for each state.  There is only one problem.  The resulting HTML looks like this:

<select id="State" name="state"><option></option><option>AK</option>...

While this is valid HTML in most browsers, it fails in IE 8 running in compatability mode.  In essence, it is treated like this:

<option value=""></option><option value="">AK</option>...

A simple solution is to force a value like this:

<%= Html.DropDownList("State", new SelectList((new string[] { "","AK","AL","AR","AS","AZ","CA","CO","CT","DC","DE","FL","GA","GU","HI","IA","ID",
                    "IL","IN","KS","KY","LA","MA","MD","ME","MH","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY",
                    "OH","OK","OR","PA","PR","PW","RI","SC","SD","TN","TX","UT","VA","VI","VT","WA","WI","WV","WY"}).Select(x => new { Text = x, Value = x}), "Value", "Text", null)) %>

You're welcome!

Posted On Tuesday, February 07, 2012 10:08 AM | Feedback (0) | Filed Under [ MVC ]

Thursday, December 22, 2011

Practice Version Control Now...Thank Me Later

To me the answer of whether or not you need version control is simple - do you have users?  If the answer is yes, then you need version control.  Note that "version control" is not the same as "source control".  Source control refers to maintaining history of your source code.  Version control (also known as "configuration control" or "configuration management") is more than that.  Version control, as its name indicates, means tracking versions of your software.

The best way to illustrate the purpose of version control is through a simple example:

You develop an application.  While developing the application you go through several iterations throwing away large chunks of code, refactoring, redesigning, etc.  Finally you are happy with the application and you publish it.  Just to make this example more tangible, let's say it is a web application so now you have deployed it using your favorite hosting provider.  One way or another, you get some users who regularly visit your web site.

Your users like your application, but you know it could be better.  You start a massive redesign and start to totally refactor everything.  Next thing you know, you get a message from your web hosting provider saying they are implementing new security measures and if you are doing XYZ, you will need to change to ABC by the end of the month.  The good news is this change just requires a few lines of code and the even better news is you can include this in your new version with your massive redesign.

It is now the last day of the month.  Life has happened (as it tends to) and your new version is not ready.  You now have less than 24 hours to complete a massive redesign, test, migrate data, and deploy.  Now you realize a simple truth:

THIS WOULD BE SO MUCH EASIER IF I HAD MY ORIGINAL PRODUCTION SOURCE CODE!

Now this is a very simplistic and probably somewhat unlikely situation.  After all, you probably are at least zipping up or otherwise backing up copies of your code.  There is a good chance you are doing this when you release a new version.  If you are doing that, then you are doing some version control.  However, if you don't have a process you follow for EVERY VERSION then you run the risk of running into the problem I described above.

So here are some simple rules to follow to make sure you have everything you need to properly maintain production code while continuing to develop new and exciting features.

Rule #1: Create a "Main" Branch

The idea of "branching" or "forking" may be completely foreign to you and if not, you still might not think about it if you aren't using source control.  Branching means that you make copies of your code along logical paths.  To have logical paths you need to have a begining and that beginning is your main branch.  In this case, I am referring to a physical folder typically called "Main".

The most important aspect of your main branch is that it contains EVERYTHING required for the application.  That means common code, 3rd party code, 3rd party libraries, documentation, media, etc.  This ensures that you always have the correct version of all of those external resources for that specific project.  That may even mean using local copies of assemblies instead of the GAC to ensure you can control which version you are using.  You don't want an application to sit on the shelf for a year and break because you upgrade a component that's registered in the GAC.

For me, a typical main branch folder contains the following subfolders:

  • Bin (Folder for binary references typically from 3rd parties)
  • [Project].Web (Web application source code)
  • [Project].DataAccess (Data access library source code)
  • [Me].Utilities (My common code)

Rule #2: Create a Release Branch

Copy EVERYTHING from your main branch folder to a new release branch.  Typically this would be a subfolder underneath a "Releases" folder (i.e. "Releases\1.0").

Rule #3: Don't Touch the Release Branch

No matter how trivial you think a change might be, don't make it in your release branch.  This is perhaps the most important aspect of this process - retention of your released code.  Even bug fixes which look simple on the surface can lead down a rabbit hole of problems which could cause you to end up with mangled and inoperable code - exactly what we are trying to avoid.

If you have one or two bugs to fix, branch from your release branch to another release branch (i.e. "Releases\1.0.1") and make your fixes there.  If you have ongoing development (long-term work on new features or enhancements), create a "Development" branch (i.e. "Development" folder).

Rule #4: Merge Often and CAREFULLY

The reason we call this "Branching" and not simply "Copying" is because it implies that there is a "split" in the development process.  Consider your development can go down two paths - one to maintain what is in production and one to make long-term enhancements.  Merging allows those two paths to converge into a single set of source code.

Merging simply means taking the changes from the two different branches and combining them.  You can do this manually or by using a "diff" or "merge" tool.  Manual merging means that if you make a bug fix in a new release branch, you would then make the same change in your main and development branches.  This is perhaps the most thorough method because it ensures that you make each change incrementally and indivdually.  It isn't necessarily the most efficient method and there are many tools available to allow you to merge changes to multiple files relatively easily.

So the obvious question is where do the changes actually get combined?  As I mentioned earlier, the main branch is the beginning of your logical branching path.  In fact some people refer to this as your "trunk".  All merging should happen back through your main branch.

Merge CAREFLULLY however. Your main branch should be your "Gold" copy of your application.  Only merge back to main once you are sure that your changes are complete and fully tested.  After you merge, make sure that you didn't introduce any new bugs simply through the mechanics of merging.  For example, your "most recent" version of your code may be from a recent bug fix.  However, other changes in your development branch have made the bug fix obsolete.  If you overwrite the "older" file with the "new" bug fix, you could introduce new bugs.

Summary

The basic process looks something like this:

  1. Develop your application in your "Main" folder containing EVERYTHING you need for your application.
  2. Once you are ready to release your first version, create a copy of your or "Branch" in a "Release" subfolder under a "Releases" folder (i.e. "Releases\1.0").
  3. Use your release branch to deploy.
  4. Create a "Development" branch folder as a copy of your main branch which should now match your latest release.
  5. Do your ongoing (long-term) development in the development branch folder.
  6. For bug fixes, create a new release branch from your previous release branch and make your fixes there.
  7. "Merge" any changes from your release and development branches back to your main branch.  This may require a "diff" tool.  Merge from release to development via main.  Merge back to main ONLY once everything has been tested and is ready for release.
  8. Copy your main branch to a new release branch.
  9. Repeat from step 3.

Your file structure would look something like this:

  • [Project]
    • Main
      • Bin
      • [Project].Web
      • [Project].DataAccess
      • [Me].Utilities
    • Releases
      • 1.0
        • Bin
        • ...
      • 1.01
      • 1.02
      • 2.0
    • Development
      • Bin
      • ...

References

Microsoft has some very good guidance on source control which is specifically related to Team Foundation Server, but hidden between the lines of this guidance is good version control guidance and a lot of the foundation for what I describe above.

This Stack Overflow post has a good collection of diff tools by OS and free vs. pay.

Before You Post That Comment

Note that this process is about "Version Control" NOT "Source Control".  How you would branch and merge would be somewhat different if you are using a source control system to keep track of your source code history.  The process outlined above is specifically intended to be used when you are NOT using source control.

Posted On Thursday, December 22, 2011 10:48 PM | Feedback (0) |

Tuesday, November 29, 2011

Custom Model Binding of IEnumerable Properties in ASP.Net MVC 2

MVC 2 provides a GREAT feature for dealing with enumerable types.  Let's say you have an object with a parent/child relationship and you want to allow users to modify multiple children at the same time.  You can simply use the following syntax for any indexed enumerables (arrays, generic lists, etc.) and then your values will bind to your enumerable model properties.

						1:  
						<% 
						using (Html.BeginForm("TestModelParameter", "Home"))
2: { %>
3: <table>4: <tr><th>ID</th><th>Name</th><th>Description</th></tr>
5: <%for (int i = 0; i < Model.Items.Count; i++)
6: { %>
7: <tr>
8:<td>
9: <%= i %>
10: </td>
11: <td>
12: <%= Html.TextBoxFor(m => m.Items[i].Name) %>
13: </td>
14: <td>
15: <%= Model.Items[i].Description %>
16: </td>
17: </tr>
18: <% } %>
19: </table>20: <input type="submit"/>21: <% } %>

Then just update your model either by passing it into your action method as a parameter or explicitly with UpdateModel/TryUpdateModel.

						1:  
						public ActionResult TestTryUpdate()
2: {
3: ContainerModel model = new ContainerModel();
4: TryUpdateModel(model);
5:
6: return View("Test", model);
7: }
8:
9: public ActionResult TestModelParameter(ContainerModel model)
10: {
11: return View("Test", model);
12: }

Simple right?  Well, not quite.  The problem is the DefaultModelBinder and how it sets properties.  In this case our model has a property that is a generic list (Items).  The first bad thing the model binder does is create a new instance of the list.  This can be fixed by making the property truly read-only by removing the set accessor.  However this won't help because this behaviour continues.  As the model binder iterates through the items to "set" their values, it creates new instances of them as well.  This means you lose any information not passed via the UI to your controller so in the examplel above the "Description" property would be blank for each item after the form posts.

One solution for this is custom model binding.  I have put together a solution which allows you to retain the structure of your model.  Model binding is a somewhat advanced concept so you may need to do some additional research to really understand what is going on here, but the code is fairly simple.  First we will create a binder for the parent object which will retain the state of the parent as well as some information on which children have already been bound.

						1:  
						public
						class ContainerModelBinder : DefaultModelBinder
2: {
3: /// <summary>
4: /// Gets an instance of the model to be used to bind child objects.
5: /// </summary>
6: public ContainerModel Model { get; private set; }
7:
8: /// <summary>
9: /// Gets a list which will be used to track which items have been bound.
10: /// </summary>
11: public List<ItemModel> BoundItems { get; private set; }
12:
13: public ContainerModelBinder()
14: {
15: BoundItems = new List<ItemModel>();
16: }
17:
18: protectedoverrideobject CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
19: {
20: // Set the Model property so child binders can find children.
21: Model = base.CreateModel(controllerContext, bindingContext, modelType) as ContainerModel;
22:
23: return Model;
24: }
25: }

Next we will create the child binder and have it point to the parent binder to get instances of the child objects.  Note that this only works if there is only one property of type ItemModel in the parent class since the property to find the item in the parent is hard coded.

						1:  
						public
						class ItemModelBinder : DefaultModelBinder
2: {
3: /// <summary>
4: /// Gets the parent binder so we can find objects in the parent's collection
5: /// </summary>
6: public ContainerModelBinder ParentBinder { get; private set; }
7:
8: public ItemModelBinder(ContainerModelBinder containerModelBinder)
9: {
10: ParentBinder = containerModelBinder;
11: }
12:
13: protectedoverrideobject CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
14: {
15: // Find the item in the parent collection and add it to the bound items list.
16: ItemModel item = ParentBinder.Model.Items.FirstOrDefault(i => !ParentBinder.BoundItems.Contains(i));
17: ParentBinder.BoundItems.Add(item);
18:
19: return item;
20: }
21: }

Finally, we will register these binders in Global.asax.cs so they will be used to bind the classes.

						1:  
						protected
						void Application_Start()
2: {
3: AreaRegistration.RegisterAllAreas();
4:
5: ContainerModelBinder containerModelBinder = new ContainerModelBinder();
6: ModelBinders.Binders.Add(typeof(ContainerModel), containerModelBinder);
7: ModelBinders.Binders.Add(typeof(ItemModel), new ItemModelBinder(containerModelBinder));
8:
9: RegisterRoutes(RouteTable.Routes);
10: }

I'm sure some of my fellow geeks will comment that this could be done more efficiently by simply rewriting some of the methods of the default model binder to get the same desired behavior.  I like my method shown here because it extends the binder class instead of modifying it so it minimizes the potential for unforseen problems.

In a future post (if I ever get around to it) I will explore creating a generic version of these binders.

Posted On Tuesday, November 29, 2011 5:29 PM | Feedback (0) |

Tuesday, September 27, 2011

Another Benefit of Asynchronous Processing of ASP.Net MVC Results

Most people who are really into ASP.Net MVC probably enjoy its test-driven nature or "unit-testability".  This is a key benefit of the asynchronous model used by the MVC framework when action results are returned.  Another benefit of this is in memory management.

I am working on creating a large document which is to be available both online and offline and consists of several hundred (potentialy more than a thousand) items.  The items are stored in a tree structure where each item has children and children can in turn have children, etc.  Each item generates about one printed page worth of HTML.

At the risk of beating a dead horse, I'm going to compare MVC to Web Forms.  In web forms, you would typically create a user control or custom control to render each item then within that contol you would add child controls for each child recursively.  What you may not realize is that this builds a giant control tree which maintains all of the dependent data throughout the page lifecycle.

In MVC on the other hand, you would (most likely) build a partial view and render it using Html.RenderAction.  This means that each item received its own trip through the MVC framework, instantiated its own controller, grabbed the required child data and passed it to the view engine to be rendered.  This means that your model is out of scope and once the references are released by the rendering engine that memory is freed up.  In essence the only memory accumulation is for the actual HTML output eventually streamed back to the client and everything else is freed up for garbage collection as each partial view is rendered.

Thanks MVC!

Posted On Tuesday, September 27, 2011 9:13 PM | Feedback (3) |

Thursday, September 22, 2011

Simple MVC 2 Pager

I wrote a simple pager AjaxHelper extension.  You can convert this to an HtmlExtension method by just removing the AjaxOptions and swapping out the AjaxHelper with HtmlHelper.  This assumes that your parameter for the page name is "Page".

 

						   1:  
						public
						static
						string Pager(this AjaxHelper ajaxHelper, int page, 
int pageSize, int count, int pages, string actionName, string controllerName,
object routData, AjaxOptions ajaxOptions)
						   2:          {
						   3:              StringBuilder sb = new StringBuilder();
						   4:  
						int pageCount = count / pageSize + 1;
						   5:  
						int firstPage = page / pages * pages;
						   6:  
						int lastPage = firstPage + pages;
						   7:  
						if (firstPage == 0)
						   8:              {
						   9:                  firstPage = 1;
						  10:              }
						  11:  
						if (firstPage > 1)
						  12:              {
						  13:                  firstPage--;
						  14:              }
						  15:  
						if (lastPage > pageCount)
						  16:              {
						  17:                  lastPage = pageCount;
						  18:              }
						  19:   
						  20:  
						if (firstPage > 1)
						  21:              {
						  22:                  RouteValueDictionary routeValueDictionary = new RouteValueDictionary(routData);
						  23:                  routeValueDictionary["Page"] = 1;
						  24:                  sb.Append(ajaxHelper.ActionLink("<<", actionName, controllerName, routeValueDictionary, ajaxOptions).ToHtmlString());
						  25:                  sb.Append("&nbsp;");
						  26:              }
						  27:  
						for (int i = firstPage; i <= lastPage; i++)
						  28:              {
						  29:  
						if (sb.Length > 0)
						  30:                  {
						  31:                      sb.Append("&nbsp;");
						  32:                  }
						  33:  
						if (i == page)
						  34:                  {
						  35:                      sb.AppendFormat("{0}", i);
						  36:                  }
						  37:  
						else
				
						  38:                  {
						  39:  
						string linkText = Convert.ToString(i);
						  40:  
						if ((i == firstPage && i > 1) || (i == lastPage && i < pageCount))
						  41:                      {
						  42:                          linkText = "...";
						  43:                      }
						  44:                      RouteValueDictionary routeValueDictionary = new RouteValueDictionary(routData);
						  45:                      routeValueDictionary["Page"] = i;
						  46:                      sb.Append(ajaxHelper.ActionLink(linkText, actionName, controllerName, routeValueDictionary, ajaxOptions).ToHtmlString());
						  47:                  }
						  48:              }
						  49:  
						if (lastPage < pageCount)
						  50:              {
						  51:                  RouteValueDictionary routeValueDictionary = new RouteValueDictionary(routData);
						  52:                  routeValueDictionary["Page"] = pageCount;
						  53:                  sb.Append("&nbsp;");
						  54:                  sb.Append(ajaxHelper.ActionLink(">>", actionName, controllerName, routeValueDictionary, ajaxOptions).ToHtmlString());
						  55:              }
						  56:  
						return sb.ToString();
						  57:          }

Posted On Thursday, September 22, 2011 5:44 AM | Feedback (0) |

Friday, August 05, 2011

Redirecting from a Partial View the "Right" Way in ASP.Net MVC 2

I am a firm believer that, regardless of the framework you use, if the framework is good it provides you all of the things you need.  I believe ASP.Net MVC 2 is good and therefore provides me everything I need.  This means I don't need to write a significant amount of custom UI code (jQuery, MVC Ajax, etc.) to do things that should be considered "standard".  One of these pretty standard things submitting a form via Ajax.  MVC provides for this very well with Ajax.BeginForm.  The contents of the form are submitted using the normal MVC process so I can use the standard patterns and behaviors like attribute-based validation, default model binding, etc.  One thing that isn't very obvious is how to conditionally trigger UI events depending on the result of the ajax call.

First, let's take a look at a typical scenario.  You created a new MVC 2 project in Visual Studio and it created a LogOn view which uses Html.BeginForm().  Wanting a better user experience, you create a partial view for the contents of your LogOn form and call it LogOnDetails which then looks something like this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<LogOnModel>" %>
<% using (Ajax.BeginForm("LogOn", new AjaxOptions { UpdateTargetId = "logOnDetails" }))
   { %>
<%: Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.") %>
<fieldset>
<legend>Account Informations</legend>
<divclass="editor-label"><%: Html.LabelFor(m => m.UserName) %></div>
<divclass="editor-field"><%: Html.TextBoxFor(m => m.UserName) %><%: Html.ValidationMessageFor(m => m.UserName) %></div>
<divclass="editor-label"><%: Html.LabelFor(m => m.Password) %></div>
<divclass="editor-field"><%: Html.PasswordFor(m => m.Password) %><%: Html.ValidationMessageFor(m => m.Password) %></div>
<divclass="editor-label"><%: Html.CheckBoxFor(m => m.RememberMe) %><%: Html.LabelFor(m => m.RememberMe) %></div>
<p>inputtype="submit"value="Log On"/></p>
</fieldset>
<% } %>

 

Then you change your LogOn.aspx to use Ajax.BeginForm and this partial view:

<asp:Content ID="loginTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Log On
</asp:Content>
<asp:Content ID="loginContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Log On<h2>
<p> Please enter your username and password.
<%: Html.ActionLink("Register", "Register") %> if you don't have an account. </p>
<divid="logOnDetails">
<% Html.RenderPartial("LogOnDetails", Model); %>
</div>
</asp:Content>

Next you change your controller so it returns the partial view if there is an error.

1:  [HttpPost]
2: public ActionResult LogOn(LogOnModel model, string returnUrl)
3: {
4: if (ModelState.IsValid)
5: {
6: if (MembershipService.ValidateUser(model.UserName, model.Password))
7: {
8: FormsService.SignIn(model.UserName, model.RememberMe);
9: if (!String.IsNullOrEmpty(returnUrl))
10: {
11: return Redirect(returnUrl);
12: }
13: else
14: {
15: return RedirectToAction("Index", "Home");
16: }
17: }
18: else
19: {
20: ModelState.AddModelError("", "The user name or password provided is incorrect.");
21: }
22: }
23:
24: // If we got this far, something failed, redisplay form
25: return PartialView("LogOnDetails", model);
26: }

Voila! Right? Not quite. As you probably already know if you are reading this, Redirect and RedirectToAction do not work the way you want.  The entire page is rendered inside the target element of the ajax call instead of actually redirecting the browser to the new page.

One commonly used solution to this problem is to simply brute force the browser to redirect by returning a javascript result.  That code would look something like this:

return JavaScript( string .Format( "document.location = '{0}';" , returnUrl));

So what is wrong with this approach?  You are putting UI code inside your controller so you are mixing your "V" with your "C" from an MVC perspective.  So how can we do this better?

One option is to use the JsonResult and serialize an object containing the result data which can then be handled by a javascript function set in the OnSuccess parameter of the ajax call. However, even this can be problematic since there have been some changes between MVC 2 and MVC 3 so you can have compatability problems if you need to be able to go back and forth between versions.

There is a simpler option which might seem like a hack at first, but I think it is a very natural use of the available options in MVC and generates a very clean result. First, let's change our LogOnModel class to add a few things to pass back to the UI.

1:  public class LogOnModel
2: {
3: [Required]
4: [DisplayName("User name")]
5: public string UserName { get; set; }
6:
7: [Required]
8: [DataType(DataType.Password)]
9: [DisplayName("Password")]
10: public string Password { get; set; }
11:
12: [DisplayName("Remember me?")]
13: public bool RememberMe { get; set; }
14:
15: [ReadOnly(true)]
16: public bool? IsLoggedIn { get; set; }
17:
18: [ReadOnly(true)]
19: public string ReturnUrl { get; set; }
20: }

Now we simply return this back to our partial view. Then all the magic happens in the UI (as you might say that it should).

						1:  [HttpPost]
2: public ActionResult LogOn(LogOnModel model, string returnUrl)
3: {
4: if (ModelState.IsValid)
5: {
6: if (MembershipService.ValidateUser(model.UserName, model.Password))
7: {
8: FormsService.SignIn(model.UserName, model.RememberMe);
9: model.IsLoggedIn = true;
10: model.ReturnUrl = returnUrl;
11: if (string.IsNullOrEmpty(model.ReturnUrl))
12: {
13: model.ReturnUrl = Url.Action("Index", "Home");
14: }
15: }
16: else
17: {
18: ModelState.AddModelError("", "The user name or password provided is incorrect.");
19: }
20: }
21:
22:
23: return PartialView("LogOnDetails", model);
24: }

Notice that the controller action is more compact. Also, notice that we added some additional logic to set the redirect URL to the home page if none was provided and that this is done in the controller where it belongs.

Next we will output the IsLoggedIn and ReturnUrl values in the partial view. This can go anywhere in the partial view. (Note: This is why we put the ReadOnly attribute on the properties - so we can update the model in our controller and not have the modelstate undo the changes).

<%= Html.HiddenFor(m => m.IsLoggedIn)%>
<%= Html.HiddenFor(m => m.ReturnUrl)%>
		
Next, we will write a very simple bit of jQuery to redirect after the user has logged in.
1:  function logInComplete() {
2: if ($("#IsLoggedIn").val() == 'true' && $("#ReturnUrl").val() != '') {
3: document.location = $("#ReturnUrl").val();
4: }
5: }

And finally, modify the Ajax.BeginForm call to call this function after the response is returned.

<% using (Ajax.BeginForm("LogOn", new AjaxOptions { UpdateTargetId = "logOnDetails", OnSuccess = "logInComplete" }))
    { %>
		

So there are some other advantages of this method. You can for example very easily display a message with a link to redirect the user instead of using javasccript to redirect. Also, since you are passing an indicator of whether the current user is logged in or not to the controller, you can change your partial view that renders the form so it only renders the form if the user is not logged in and otherwise displays a message that the user is logged in.

Voila! For real this time.

]]>
]]>
			
			

Posted On Friday, August 05, 2011 6:52 PM | Feedback (5) |

Friday, March 25, 2011

Key to Strongly-Typed HtmlHelper Extensions in ASP.Net MVC2

While you can do most common tasks by simply passing expressions through to existing HtmlHelper extension methods, you may stumble on something where you want to evaluate the lambda expression on your own.  While this may seem daunting at first, the MS folks have made it pretty simple.  All you have to do is this:

ModelMetadata modelMetaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

This assumes that htmlHelper and epxression are parameters of your extension method which would look something like this:

public static MvcHtmlString MyNewExtensionMethod<T, TEnum>(this HtmlHelper<T> htmlHelper, Expression<Func<T, TEnum>> expression)

Then you can get the result of your expression with the following:

var result = modelMetaData.Model;

Note that this gives you the resulting value of the expression and NOT the instance of your model.

Posted On Friday, March 25, 2011 10:23 AM | Feedback (0) |

Wednesday, March 16, 2011

Select Data From XML in MS SQL Server (T-SQL)

So you have used XML to give you some schema flexibility in your database, but now you need to get some data out.  What do you do?  The solution is relatively  simple:

 

DECLARE @iDoc INT /* Stores a pointer to the XML document */

DECLARE @XML VARCHAR(MAX) /* Stores the content of the XML */

 

set @XML = (SELECT top 1 Xml_Column_Name FROM My_Table

where Primary_Key_Column = 'Some Value')

 

EXEC sp_xml_preparedocument @iDoc OUTPUT, @XML

 

SELECT *

FROM OPENXML(@iDoc,'/some/valid/xpath',2)                     

WITH (output_column1_name varchar(50)  'xml_node_name1',                                                    

output_column2_name varchar(50)  'xml_node_name2')

 

EXEC sp_xml_removedocument @iDoc

 

In this example, the XML data would look something like this:

 

<some>

  <valid>

    <xpath>

      <xml_node_name1>Value1</xml_node_name1>

      <xml_node_name2>Value2</cml_node_name2>

    </xpath>

  </valid>

</some>

 

The resulting query should give you this:

 

output_column1_name    output_column2_name

------------------------------------------

Value1                 Value2

 

Note that in this example we are only looking at a single record at a time.  You could use a cursor to iterate through multiple records and insert the XML data into a temporary table.

Posted On Wednesday, March 16, 2011 8:31 AM | Feedback (0) |

Powered by: