Skip to content
Latest

Using CKEditor in ASP.NET MVC

If you’re using ASP.NET MVC, setting up CKEditor is pretty straightforward from NuGet, especially if you’re using jQuery as well.

1. First, install it from NuGet:

2. By default, it will put the files under the Scripts directory. I prefer to have it under /js/libs, so I move it there after it’s done downloading.

3. After that, select a view (.cshtml) file and after jquery is loaded, include the following .js files:

4. Now, assuming you have a textarea of id=”message”:

apply the following method:

$("#message").ckeditor();

5. At this point, you’re done with CKEditor with the default settings.

6. If you want to configure the editor, update the config.js. The following makes the textarea 500px and shows a limited number of tool buttons:

CKEDITOR.editorConfig = function (config) {
  config.width = 500;
  config.toolbar =
  [
    [
      'SourceBold',
      'Italic',
      'Underline',
      'Strike',
      '-',
      'Subscript',
      'SuperscriptNumberedList',
      'BulletedList',
      '-',
      'Outdent',
      'Indent/Styles',
      'Format',
      'Font',
      'FontSize'
    ]
  ];
};

This is what it’ll look like:

You might want to check out the other configuration settings for the toolbar. Check out the settings and jQuery CKEditor adapter.

Validating Rich TextArea in ASP.NET MVC 3

There’s a few things to consider when validating data from a rich textarea. After setting it up (here’s a blog post about that), we have to allow the property to accept HTML, otherwise, you’ll get this:

Which would normally be good a thing for normal textboxes to avoid XSS. However, here we have to accept it. So assuming we have a property in our model called message, we add the AllowHtml attribute:

  [AllowHtml]
  public string Message { get; set; }
 

This is all great and dandy, however, once you do that, it won’t allow other validations, like Range(). So what we have to do is create our own attribute. To do this, we just create a class that inherits ValidationAttribute:

 
class AllowHtmlRangeLength : ValidationAttribute
{
  public override bool IsValid(object value)
  {
    string property = (string)value ?? "";
    if (property.Length >= 3 && property.Length <= 2000)
    {
      return true;
    }
 
    return false;
  }

In this case, we're only accepting (to attempt to validate) a string from 3 to 2000 characters. Then we use it like this in our model:

[AllowHtml]
[AllowHtmlRangeLength(ErrorMessage="Please have at least 3 to 2000 characters."]
public string Message { get; set; }

The problem with this set up is that we don't want HTML tags to count as characters. So we need to parse out HTML. Because parsing data with just one regular expression can be error-prone, it's best to use a library like HTML Agility pack – get it from NuGet:

Once you have it installed in your project, we can reuse the sample code parse out HTML from its CodePlex site.

For simplicity’s sake, I made the class and its methods static. Additionally, I added the following method to add additional scrubbing logic:

    public static string ConvertHTMLToCleanText(string html)
    {
      HtmlDocument doc = new HtmlDocument();
      doc.LoadHtml(html);
 
      StringWriter sw = new StringWriter();
      ConvertTo(doc.DocumentNode, sw);
      sw.Flush();
 
      // let's clean the string + Remove double spaces.
      string clean = Regex.Replace(sw.ToString().Trim(), @"\s{2,}", "");
 
      return clean;
    }
 

Once we have that set up, let’s now tweak our AllowHtmlRangeLength attribute we started out with to use this new ConvertHTMLToCleanText method (from above):

 
  class AllowHtmlRangeLength : ValidationAttribute
  {
    public int minHtmlLength { get; set; }
    public int maxHtmlLength { get; set; }   
 
    public override bool IsValid(object value)
    {
      string property = (string)value ?? "";
 
      if (HtmlToText.ConvertHTMLToCleanText(property.ToString()).Length >= minHtmlLength && HtmlToText.ConvertHTMLToCleanText(property.ToString()).Length <= maxHtmlLength)
      {
        return true;
      }
 
      return false;
    }
 
    public override string FormatErrorMessage(string s)
    {
      return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, minHtmlLength, maxHtmlLength);
    }
  }

We're overriding the FormatErrorMessage method so we can customize the error message to show the min and max values of the range. Also, we need to set min and max as public properties so we can use it as arguments as part of the AllowHtmlRangeLength attribute. So this is how it's called now:

[AllowHtml]
[AllowHtmlRangeLength(ErrorMessage="Please have at least {0} to {1} characters.", minHtmlLength=3, maxHtmlLength=255)]
public string Message { get; set; }

So let’s see. This throws an error, even though there’s HTML that bolds, underlines, and adds a heading of 1.

The following has no error:

The following has 3000 chars and it fails.

Check out the source files.

Common SVN Error Messages and What They Mean

Here’s a list of errors and what I do to fix them. Also, there’s a list of tips when dealing with these errors.

Action: Commit or you add

Error Message: Entry for …. has no URL

What It Means: The SVN meta files were deleted/modified by your operating system or some other application and has deleted/corrupted the meta files.

Solution: Run cleanup on that directory. Afterwards, run update on that directory. Move the troubling files to a temp directory. Commit whatever you can from that directory. Delete that directory. Go one directory up, and do an update (this will create clean versions of the meta files) on it. Then, from the temp directory, copy the troubling files into the folder that was recreated after you did an update. The new files will have a + sign. Add those files and then commit them. If you still see the exclamation point on it, run cleanup on the folder again.


Action: Updating a folder

Error Message: system cannot find the path specified

What It Means: The SVN meta files were deleted/modified by your operating system or some other application and has deleted/corrupted the meta files.

Solution: Run cleanup on that directory. Afterwards, run update on that directory. If you get a conflict afterwards, resolve the conflict or revert to the current version in the trunk. Run update again.



Action: Updating a folder

Error Message: Can’t close file / There is not enough space on the disk

What It Means: Your hard drive is out of space.

Solution: Make more space on the drive that you’re running the update on.



Action: Commit a folder

Error Message: Out of date; try updating;

What It Means: The files in the folder you’re trying to commit have conflicts (most likely), and there’s repo changes you need to update your working directory to. Chances are that after you update, you’ll have conflicts and need to resolve them.

Solution: Update that directory.



Action: Updating a folder

Error Message: An existing connection was forcibly closed by the remote host

What It Means: Your connection to SVN was dropped or timed out. Also, the SVN server could’ve been reset.

Solution: Update again.



Action: Updating a folder

Error Message: Directory/File missing or corrupt; System cannot find the path specified

What It Means: The SVN meta files were deleted/modified by your operating system or some other application and has deleted/corrupted the meta files.

Solution: Run cleanup on that folder and update again.



Action: Updating a folder

Error Message: Tree Conflict; One or more files are in a conflicted state

What It Means: Your folder has conflicts.

Solution: Go to the file(s) (in this case, e.cshtml), and resolve the conflict (or revert it if you haven’t touched it). After you do that, update the folder again.



Action: Add a file and then commit to the repo

Error Message: File … already exists

What it Means: Your metadata is not in sync with the repo and running cleanup is not helping.

Solution: This is one of the most problematic issues you may come across. Updating/cleaning up/etc may not help you here. The fastest way to resolve this is to delete from repository and add the file again to the folder and commit.

Drawback: You’ll lose the history of the file if you try to view it

Tips

Sometimes no matter what you do, it won’t work, so it may be faster to redo a checkout. That always guarantees that your directory will work afterwards. To do this, delete your working directory. Then create that directory again and do a checkout of the folder you want from the repo.

Basically, how fast you checkout depends on:
How many files you’re checking out
How many people are doing SVN operations simultaneously on the repo
The size of the files you’re checking out

If there’s a conflict, sometimes it’s just easier to revert (if you haven’t touched it) the file to the last revision (in Tortoise, you have the option to “Revert” if there’s a conflict) then trying to resolve it.

After you do a commit on a folder, the icon overlay may not update immediately, that is, you commit all the files in a folder and then you still see the exclamation icon over the folder. There are three ways you can resolve it:

Tortoise has to update all the metadata in needed files and subfolders. Depending on how many files there are in the folder, showing the icon may take a few seconds.

If you try to commit again you may get this:

So you have to wait.

If you’ve waited a while and the icon doesn’t update, with your OS, navigate to the folders where the suspected files (that have an exclamation icon) are. Once you get there, wait a few seconds. Then go up 1 directory and see if that fixed it.

If the above doesn’t work, run cleanup on that folder, and then do an update on that folder.

Add the most common actions to your context menu:

When I right-click on a folder, show the most common actions:

You can configure this by right clicking anywhere and doing settings -> Context Menu

What do the Icons Means?

Event Delegation/Propagation (Bubbling) via jQuery

I was going through the book JavaScript Programmer’s Reference by Alexei White and noticed two diagrams that explain the event delegation and event propagation:

So rather than binding an event handler to each tr or td, you bind only to the table element, and capture the event (via bubbling), detect the element that fired it, and then fire the event handler. This is more efficient than binding to each and every tr or td – instead you rely on the action bubbling up to one element.

Let’s demonstrate this using jQuery:

$(document).ready( function() {

 // http://api.jquery.com/event.target/
 // http://docs.jquery.com/Events_%28Guide%29

 $("#datatable").click( function(event) {    
   // Detect the element that fired off the event
   if ( event.target.nodeName == "TD" )
   {
     alert( "you clicked on a td element" );
   }
 });
});
<table id="datatable" border="1">
 <tr>    
   <th>Header 1a</th>
   <th>Header 1b</th>
   <th>Header 1c</th>
   <th>Header 1d</th>
   <th>Header 1e</th>
 </tr>

 <tr id="row1">
   <td id="aaa">Data 1a</td>
   <td>Data 1b</td>
   <td>Data 1c</td>
   <td>Data 1d</td>
   <td>Data 1e</td>
 </td>

 <tr id="row2">
   <td>Data 1a</td>
   <td>Data 1b</td>
   <td>Data 1c</td>
   <td>Data 1d</td>
   <td>Data 1e</td>
 </td>
 
 <tr id="row3">
   <td>Data 1a</td>
   <td>Data 1b</td>
   <td>Data 1c</td>
   <td>Data 1d</td>
   <td>Data 1e</td>
 </td>
 
 <tr id="row4">
   <td>Data 1a</td>
   <td>Data 1b</td>
   <td>Data 1c</td>
   <td>Data 1d</td>
   <td>Data 1e</td>
 </td>

</table>


MongoDB Console Tip: Better Display of Object on Console

Quick tip. This is just a neat way to better display the JSON from the console. For example, if I did this in one of my dbs:

db.client.find()

I would get:

{ "_id" : ObjectId("4da45b3cf980ed161462c2ef"), "ContactEmergencyDetails" : { "awesome" :
[ 1111, 2222, 3333 ], "cool" : "awesome", "dan" : [ 4.5, 5.322 ] }, "firstname" : null, "lastname" : null }

But if I changed it to this (converting it to Array):

db.client.find().toArray()

It formats it better:

[
  {
    "_id" : ObjectId("4da45b3cf980ed161462c2ef"),
    "ContactEmergencyDetails" : {
      "awesome" : [
                    1111,
                    2222,
                    3333
                ],
        "cool" : "awesome",
        "dan" : [
                  4.5,
                  5.322
                ]
  },
  "firstname" : null,
  "lastname" : null
  }
]

Dapper – A C# Micro ORM Library Makes Data Access a Snap – Part 1

I’ve recently gotten a chance to try out Dapper – a micro ORM library by Sam Saffron, which originally spawned from StackOverflow’s codeset. If you’re looking for an extremely light-weight library to access your SQL Server, I highly recommend it. It focuses more on convention than configuration. It also does not have the overhead that EntityFramework or NHibernate have, and it emphasizes speed and ease-of-use.

Dapper is a collection of Extension Methods that enhance the Connection object (or rather extends the IDbConnection interface). It also embraces the use of inline SQL in your code, which is useful when retrieving sets that are less trivial. With its buddy, Dapper-Extensions by Thad Smith, it makes DB interaction even easier.

Both of these libraries can be downloaded from NuGet, and you’ll need them to run the following code.

There’s really just one thing that I found irritating about the Dapper project: the documentation. While there is documentation found at its Google Code home page, I find it very lacking.

Anywhoot, to get started first we need to create our table. Once we have that and add some data, we’ll use the Dapper to extract the data and map to objects.

----------------------------------------
-- Customer Schema
----------------------------------------
CREATE TABLE [dbo].[Customer](
[CustomerID] [uniqueidentifier] NOT NULL,
[FirstName] [nvarchar](50) NULL,
[LastName] [nvarchar](50) NULL,
[Age] [int] NULL,
[IsAllowed] [bit] NULL,
[DateRegistered] [datetime] NULL,
[AdditionalDetails] [nvarchar](max) NULL,
CONSTRAINT [PK_Customer_1] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Customer] ADD  CONSTRAINT [DF_Customer_CustomerID]  DEFAULT (newid()) FOR [CustomerID]
GO

----------------------------------------
-- Data
----------------------------------------
INSERT INTO [dbo].[Customer]([CustomerID], [FirstName], [LastName], [Age], [IsAllowed], [DateRegistered], [AdditionalDetails])
  SELECT N'5458dff8-cea2-4bdb-9431-1dea56f109f8', N'Bruce', N'Wayne', 31, 1, '20080604 08:13:44.000', N'I am Batman' UNION ALL
  SELECT N'1767bc57-af03-4b17-891f-2aa9af244180', N'Peter', N'Parker', 25, 1, '20120103 12:22:28.000', N'I am Spider-Man' UNION ALL
  SELECT N'dc2bf42d-045b-4189-bd6b-8bf1bf120291', N'Eddie', N'Brock', 34, 0, '20040123 02:45:41.000', N'We are Venom'

OK, so this is what we have so far:

Because Dapper focuses more on convention over configuration, it’s important to note that when you create your class:

  • The name of your class must match the name of the table, otherwise an exception is thrown.
  • All column names must match all class property names, otherwise an exception is thrown.
  • If a column is not used (in NOT written out in the SELECT statement) and the class property exists, it will set to NULL or assign a default value – more on this later.

Now that we’re aware of these points, let’s see the code to get started:

using System;
using Dapper;
using System.Data.SqlClient;

class Customer
{
  public Guid CustomerID { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public int Age { get; set; }
  public bool IsAllowed { get; set; }
  public DateTime DateRegistered { get; set; }
  public string AdditionalDetails { get; set; }
}

class Program
{
  private static void Main()
  {

    using (SqlConnection conn = new SqlConnection("Data Source=NARUTO;Initial Catalog=TESTDATABASE;Integrated Security=True"))
    {
      conn.Open();

      // Put dapper code here.

      conn.Close();
    }

    Console.ReadLine();
  }
}

OK, so there’s nothing out of the ordinary here. We’re creating the class we want to map our data to, creating a Connection object, opening, closing, and then disposing (via using). Now let’s suck up some data within our Main() method:

private static void Main()
{
  using (SqlConnection conn = new SqlConnection("Data Source=NARUTO;Initial Catalog=TESTDATABASE;Integrated Security=True"))
  {
    conn.Open();

    IEnumerable customers = conn.Query("SELECT * FROM Customer");

    foreach (Customer c in customers)
    {
      Console.WriteLine("CustomerID: " + c.CustomerID);
      Console.WriteLine("FirstName: " + c.FirstName);
      Console.WriteLine("LastName: " + c.LastName);
      Console.WriteLine("Age: " + c.Age);
      Console.WriteLine("IsAllowed: " + c.IsAllowed);
      Console.WriteLine("DateRegistered: " + c.DateRegistered);
      Console.WriteLine("AdditionalDetails: " + c.AdditionalDetails);
      Console.WriteLine("-----------------------------------------------");
    }

    conn.Close();
  }

  Console.ReadLine();
}

As we see, the Query extension method pulls in the data based on the select statements. The Query method returns a strongly-typed list of Customer. You should see the following:

Now what if we want to get the number of records in the table? Well, because Query returns an IEnumerable, we cannot use the Count property. To do so, we convert to a List:

List customers = (List)conn.Query("SELECT * FROM Customer");
Console.WriteLine(customers.Count);

That should give us the count. To see how the data types were mapped, we can take a look at the Dapper source code, SqlMapper.cs inside the static method SqlMapper(). For reference, you can take a peek at these two sources to understand SQL Server/CLR/.NET framework data types:

http://stackoverflow.com/questions/425389/c-sharp-equivalent-of-sql-server-2005-datatypes

http://msdn.microsoft.com/en-us/library/ms131092.aspx

For our example though, we saw that the data types were mapped:

Mismatches and Nulls

In the query that we ran, we were making the assumption that all columns in the db match all the properties in the class. What if we have a mismatch? What if we have this?

IEnumerable customers = conn.Query("SELECT LastName FROM Customer");

Notice that we’re only retrieving the LastName, while we have other properties in our Customer class. Well, this is what you would see:

So from the above, we notice that Dapper will handle Nulls by:

  • Setting strings as Nulls
  • Setting a Guid to 00000000-0000-0000-0000-000000000000
  • Setting an int to 0
  • Setting bool to false
  • Setting a DateTime to 1/1/0001 12:00:00 AM

Which will all happen when Dapper cannot find a class property to map to from the column value. Also, if you column name that DOES NOT match a property, it will throw a SqlException of “Invalid Column Name”.

Parameterization

What if we want to pass a parameter into our SQL? Let’s pass a parameter:

IEnumerable customers = conn.Query("SELECT * FROM Customer WHERE AGE > @Age", new { Age = 30 } );

Notice that I’m passing in an anonymous object. I could also have done:

Customer someCustomer = new Customer {
Age = 33,
FirstName = "Clark Kent"
};

IEnumerable customers = conn.Query("SELECT * FROM Customer WHERE AGE > @Age", someCustomer );

Which yields:

The property in someCustomer (FirstName = “Clark Kent”) is silently ignored because it’s not used in the SELECT statement.

When the above is run, SQL Server Profiler shows that the following is run:

exec sp_executesql N'SELECT * FROM Customer WHERE AGE > @Age',N'@Age int',@Age=33

So we know it’s parameterized.

To call a stored procedure and map it to objects, we do, assuming we have the following sproc:

CREATE PROCEDURE GetCustomers
(
@IsAllowed BIT
)
AS
BEGIN

SELECT * FROM Customer
WHERE IsAllowed = @IsAllowed

END

We do:

// The second @ token does not have to be named "@IsAllowed" and can be named anything as
// long as the C# property in the anonymous object is named the same thing.
IEnumerable customers = conn.Query("GetCustomers @IsAllowed = @IsAllowed", new { IsAllowed = 33 } );

Also accepted:

IEnumerable customers = conn.Query("GetCustomers @IsAllowed = @Allow", new { Allow = 33 } );

Or you can be more explicit by specifying the command type (StoredProcedure):

IEnumerable customers = conn.Query("GetCustomers", new { IsAllowed = 33 }, commandType: CommandType.StoredProcedure );

Or writing it a more secure way:

DynamicParameters parameters = new DynamicParameters();
parameters.Add("@IsAllowed", 'f', dbType:DbType.Int32);

IEnumerable customers = conn.Query("GetCustomers", parameters, commandType: CommandType.StoredProcedure );

To insert data, we do as follows:

Customer customer = new Customer{
  FirstName = "Mazinger",
  LastName = "Z",
  Age = 40,
  IsAllowed = true,
  DateRegistered = DateTime.Now,
  AdditionalDetails = "Metallic defender"
};

conn.Execute(@"INSERT INTO Customer
                                  ( FirstName ,
                                  LastName ,
                                  Age ,
                                  IsAllowed ,
                                  DateRegistered ,
                                  AdditionalDetails

                   )
                   VALUES  ( @FirstName ,
                                 @LastName ,
                                 @Age ,
                                 @IsAllowed ,
                                 @DateRegistered ,
                                 @AdditionalDetails
                               )"
                 , customer);

This ends part 1 of the tutorial. I will post the 2nd part soon.

Model Binding to a Dropdown

Sample #1: Binding to a List<string>

There’s a few ways to model bind a collection to a dropdown using the DropDown HTML helpers in ASP.NET MVC. Let’s first look at a simple scenario, where we have a dropdown and we want to bind to a List of states.

For this example, we’re going to construct a class called Globalization that has the list of states. (I use an this object to hold look-up data sets.)

  public class Globalization
  {
    public List States
    {
      get
      {
        return new List() 
        {
          "NY", "NJ", "IL", "TX", "FL"
        };
      }
    }

Now let’s create our model:

public class Person
{
  [Required(ErrorMessage="Please choose a state!")]
  public string State { get; set; }
}

And follow up with our Controller:

  public class HomeController : Controller
  {
    Globalization global = new Globalization();

    public ActionResult Index()
    {
      return View();
    }
    Globalization global = new Globalization();

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

    [HttpPost]
    public ActionResult Index(Person ba, FormCollection form)
    {      
      // Let's check if the state form field exists in the Global State list...
      if (global.States.Exists(s => s == form["State"] ? true : false))
      {
        // Clear the errors from the state property of the modelstate
        ModelState["State"].Errors.Clear();
      }      

      // Let's put it in the ViewBag so we can retain the user's form state when 
      // the page is refreshed and the forms are repopulated with what the user
      // previously put in.       
      ViewBag.selectedState = form["State"];

      if (ModelState.IsValid)
      {
        // Run further server-side business logic from private methods.
        // DoFurtherStuff();
        return (RedirectToAction("Success"));
      }

      return View(ba);
    }

    public ActionResult Success()
    {
      return View("Success");
    }
  }

Now let’s do the View:

@model MvcApplication10.Models.Person
@using MvcApplication10.Models;
@{
  ViewBag.Title = "BankAccount";
  Html.EnableClientValidation(false);
  Html.EnableUnobtrusiveJavaScript(false);
}

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "mvcform" }))
{    
  
    BankAccount

    <div class="editor-field">      
      @{ 
        SelectList slStates = new SelectList(new Globalization().States, ViewBag.selectedState); 
      }
      @Html.DropDownList("State", slStates.OrderBy( x =&gt; x.Text ), "")
      @Html.ValidationMessageFor(model =&gt; model.State)
    </div>

    <p>
      
    </p>
  
}
<div>
  @Html.ActionLink("Back to List", "Index")
</div>

So with this app, if we don’t pick a value from the dropdown, we get an error:

Now, let’s look at the HTML code generated:


  
  FL
  IL
  NJ
  NY
  TX

Notice that the option elements don’t have explicit values, so they’ll be set to the text inside the option elements. So that’s equivalent to:


  
  FL
  IL
  NJ
  NY
  TX

We leave the first one blank so the user has to select a value. So what if we want to bind the dropdown to a key/value pair collection like Dictionary? Like this for example:


  FL
  IL

To do the above, refer to the next section…

Sample #2: Binding to a List<SelectListItem>

Now let’s visualize a different scenerio:

Where the HTML generated is:


  
  Approval Code
  More Info Requested
  Rejected
  Deleted
  Approved

and we have to populate it with the data from the db (Status table):

Where the schema is:

CREATE TABLE [dbo].[Status](
	[StatusID] [tinyint] IDENTITY(1,1) NOT NULL,
	[Name] [varchar](50) NOT NULL
 CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED 
(
	[StatusID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

So we write the C# code a little different for this scenario. I’ve found that for this, it’s helpful to use the helper @Html.DropDownListFor(), which accepts an IEnumerable, which you’ll have to construct.

First let’s create our Model class:

public class Category
{		
    [Required(ErrorMessage = "Status is required.")]
    public Byte StatusID { get; set; }
}

For convenience, let’s also create a ViewModel class that uses Category. We’re also using Dapper here, which makes db connection a snap. Check out the tutorial for more details.

  /// <summary>
  /// We create his class so we can map data from Dapper.
  /// </summary>
  public class StateSelectListItem
  {
    public string Name { get; set; }
    public byte StatusID { get; set; }
  }

  /// <summary>
  /// This is the ViewModel for the Category Create Form
  /// </summary>
  public class CategoryViewModel
  {
    public Category CategoryModel { get; set; }

    public List ListStatusCodes
    {
      get
      {
        // We're putting an empty SelectListItem so that the first item in the drop down
        // is blank. 
        List selectList = new List() { new SelectListItem() { Text = "", Value = "", Selected = false } };

        using (SqlConnection conn = new SqlConnection("Data Source=NARUTO;Initial Catalog=GalaxyM33;Integrated Security=True"))
        {
          conn.Open();

          // Let's map the results (using Dapper) to the list of StateSelectListItem
          IEnumerable listStatus = conn.Query("select StatusID, Name From [Status]");

          // We're adding SelectList objects to the List...
          foreach (StateSelectListItem item in listStatus)
          {
            selectList.Add(new SelectListItem() { Text = item.Name, Value = item.StatusID.ToString() });
          }

          conn.Close();
        }
        return selectList;
      }
    }
  }

Here’s the controller:

    public ActionResult Create()
    {
      CategoryViewModel viewModel = new CategoryViewModel();    
      return View(viewModel);
    }

    [HttpPost]
    public ActionResult Create(CategoryViewModel viewModel)
    {      
      return View(viewModel);
    }

Now for the View:

@model ViewModels.CategoryViewModel
@{
  ViewBag.Title = "Create";
  Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>


@using (Html.BeginForm())
{
  @Html.ValidationSummary(true)
  
    Category
    <div class="editor-field">      
      @Html.DropDownListFor(m =&gt; m.CategoryModel.StatusID, Model.ListStatusCodes)
      @Html.ValidationMessageFor(model =&gt; model.CategoryModel.StatusID)
    </div>       
    <p>
      
    </p>
  
}
<div>
  @Html.ActionLink("Back to List", "Index")
</div>

That should do it.

Download Sample 1

Model Binding and Validation ASP.NET MVC Sample Project

I wanted to share this sample project I put together that demonstrates model-binding and validation of several form components, including complex ones such as dropdowns, checkboxes, and radio buttons. There are several aspects to think about when submitting a form:

  1. HTML form value has to map to the correct C# property/object via Model Binding.
  2. Value submitted has to be properly validated based on Attributes or logic in controller.
  3. If the form has invalid data, the form has to refresh itself and be populated with all the values the user previous attempted to submit.
  4. The appropriate error messages have to be displayed next. The error messages can be part of the set of attributes above the property or handled via ViewBag/ViewData object.

The following example has all that. It also uses a ViewModel (BankAccount.cs) to piece together the main Business Domain Models (e.g. Person).

Download the Sample Project

Here’s our classes in the project:

View Bound Events from an Element in jQuery

The .data() method is a neat way to find out what data is associated with an element. For example, let’s say we have the following HTML:

<a href="http://www.google.com" id="google-link">Click me to proceed.</a>

and we run the following:

$("#google-link").data("events");

We’ll get (in Chrome’s console):

We get an undefined because there are no events bound to it. Now, let’s add some events:

    $("#google-link").click(function(e) {      
      alert("Google link was clicked.");
      e.preventDefault();
    });

    $("#google-link").hover(function(e) {      
      alert("Google link was hovered.");
      e.preventDefault();
    });

Now let’s view the page and run the data method again in the console.

$("#google-link").data("events");

The results:

Thus it’s very handy, especially when you’re using plugins and they misbehave by randomly hooking themselves into elements, getting in your way.

Error Logging in ASP.NET MVC with ELMAH

Error logging in ASP.NET MVC is extremely easy with ELMAH (Error Logging Modules and Handlers). I suggest you get it from NuGet:

Once you have it installed, it can instantly start recording errors, even Yellow Screens of Deaths. Here’s a list of errors from Elmah:

So what happens, is after you set it up via NuGet, you can get started viewing issues (if there are any) via elmah.axd. So in my case:

http://localhost:56598/elmah.axd

Now, if you’re using Chrome, and you view this page, Chrome will try to request for favicon.ico, which is not delivered from this web page. So, you have to write a filter in your web.config to ignore the .ico file:

     .
     .
     .
  <elmah>    
    <errorFilter>
      <test>
        <regex binding="Context.Request.ServerVariables['URL']" pattern="/favicon\.ico(\z|\?)" />     
      </test>
    </errorFilter>    
  </elmah>

</configuration>

Or if you want to completely ignore 404 errors:

     .
     .
     .
  <elmah>    
    <errorFilter>
      <test>
        <equal binding="HttpStatusCode" value="404" type="Int32" />     
      </test>
    </errorFilter>    
  </elmah>
</configuration>

By default, ELMAH stores error details in memory, so every time the app pool gets restarted, it gets flushed. To dump the data to SQL Server instead, update your web.config:

  <elmah>
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="ElmahConnectionString"/>
  </elmah>
</configuration>

The Elmah connection string is pretty straightforward:

<connectionStrings>
<add name="ElmahConnectionString " connectionString="Initial Catalog=my_database;data source=.\SQLEXPRESS;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
</connectionStrings>

Then you go to the Elmah project page and run the SQL Server script via SSMS – MAKE SURE YOU RUN ‘USE ‘ first!

http://code.google.com/p/elmah/downloads/list

It then creates a table:

Testing JavaScript Code in Sublime Text with Node.js

Testing JavaScript code can easily be done now with today’s browser in console mode. If you want to this within an IDE like Sublime Text from the press of a button, read on. :)

The first thing you’ll need is a JavaScript interpreter like Node.js. Get it from http://nodejs.org/ . Then in Sublime Text go to:

Tool -> Build System -> New Build System

Create a new build system file. In this example, we’ll use JavaScript.sublime-build

By default, it’ll create it under the path:

C:\Users\Dan\AppData\Roaming\Sublime Text 2\Packages\User

Once you do that, configure the build file using the settings from the Sublime Text official docs.

So your config file will look like this:

{
  "cmd": ["C:/Program Files (x86)/nodejs/node.exe", "-p", "$file"]  
}

and that should be it.

So now when you press F7, the build system will run and the output will be shown like so:

Search For Column Name in All Databases

Whipped up this little script today to search for a column in all my databases.

--------------------------------------------------------------------------------------------------
-- Search for the column name
--------------------------------------------------------------------------------------------------
DECLARE @searchFor AS VARCHAR(50)     = 'CustomerID' -- keyword to search for
DECLARE @databaseList AS VARCHAR(MAX) = 'Database1,Database2,Database3,Database4' -- comma delimited

DECLARE @orderBy AS VARCHAR(50)       = 'DateCreated ASC' -- You can also append a space and the ASC/DESC
-- Order By One of These
-- ObjectID, ColumnName, Table, Database, Date Created, Modified Date
--------------------------------------------------------------------------------------------------

DECLARE @sql NVARCHAR(MAX) = ''
-- loop
SET @databaseList = @databaseList + ','
DECLARE @pos AS INT
DECLARE @val AS VARCHAR(255)

WHILE PATINDEX('%,%', @databaseList)  0
BEGIN       
  SELECT  @pos = PATINDEX('%,%', @databaseList)
  SELECT  @val = LEFT(@databaseList, @pos - 1)       
  SELECT  @databaseList = STUFF(@databaseList, 1, @pos, NULL)
  SET @sql = @sql + 
             'SELECT  *
              FROM    ( SELECT  portalColumns.OBJECT_ID AS ObjectID ,
                                portalColumns.[NAME] ColumnName ,
                                ''[' + @val + '].dbo.['' + portalTables.[NAME] + '']'' AS [Table] ,
                                ''[' + @val + ']'' AS ''DatabaseName'',                  
                                create_date AS ''DateCreated'' ,
                                modify_date AS ''ModifiedDate''
                        FROM    [' + @val + '].sys.columns portalColumns
                                INNER JOIN [' + @val + '].sys.tables portalTables ON portalColumns.object_id = portalTables.object_id
                        WHERE   portalColumns.name LIKE ''%' + @searchFor + '%''
                      ) AS Results
              UNION '                       
END

SET @sql = LEFT(@sql, LEN(@sql) - 5) + ' ORDER BY ' + @orderBy

--PRINT @sql    
        
EXECUTE sp_executesql  @sql

Upgrading from SQL Server 2000 to 2008

First have a look at Microsoft Whitepaper that gives you an overview of the process. Make sure you get the SQL Server 2008 Upgrade Advisor. It will analyze databases and check for incompatibilities. It does not write any data to database you’re analyzing. I highly recommend it, if you’re making such a big jump as we did. Also, apply SP1 (currently the newest), and Windows updates.

The machine I’m working on is a 64bit machine with 16 GB RAM. Make sure you adjust the max memory taking up by SQL Server. By default, it will try to consume the max you have (the setting will be set at roughly 2 petabytes). To prevent OS starvation of memory, on a machine used for just SQL Server, allot 10-15% of memory to the OS. The rest will be for SQL Server. With 16GB, I set it to 13GB (13312 MB) of memory.

111

Also, I urge to change the compatibility level to SQL Server 2008 (100), that is of course, the Upgrade Adviser throws a lot of issues and you’re on a tight deadline. Certain features have been deprecated and removed. For example, this will no longer work on 2008 (order by [table alias].[column alias]):

select AreaID aid, ParentID, Name 
from Areas a
order by a.aid

or this way of truncating will no longer work:

To avoid problems for future upgrades, it’s best to switch the compatibility to 2008.

Also, as an obvious reminder, keep a backup, especially if you’re going to do a detach/attach migration. Once you attach the 2000 db files to 2008, you can’t retach to 2000. I prefer a traditional .bak restore.

Lastly, I highly recommend this checklist when upgrading.

Send A File Path from the Windows Context Menu to App

Here’s an easy way to pass the file path to a console app. I needed a way to right click on a folder or a file, and send the path to a console app, where the app does its thing with the file(s).

To show you what I mean:

When I right-click on “coolbeans” it runs the following console app, which simply displays the path:

The C# app is pretty straightforward. Basically, once you have the file path, you can apply any operations on the file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace dan_rocks
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine(args[0]);
      Console.ReadLine();
    }
  }
}

So there’s nothing crazy going on in the above sample. You basically just have to add a few entries to the Registry.

If you want to pass a folder path when you right-click on it, and select the option in the context menu, create a new entry:

HKEY_LOCAL_MACHINE/Software/Classes/Folder/Shell

If you want to pass in a file, do the following:

HKEY_CLASSES_ROOT\*\shell

Let’s pass in the filepath to note.exe:

Do that and you’ll see the following:

SSIS Data Import from Excel

I’ve always found the SSIS import tool a bit clunky. I tried to import a CSV file and it get crapping out before completing.

– Copying to [dbo].[export] (Stopped)

Messages

  • ·Error 0xc02020a1: Data Flow Task 1: Data conversion failed. The data conversion for column “Email Address” returned status value 4 and status text “Text was truncated or one or more characters had no match in the target code page.”.
    (SQL Server Import and Export Wizard)
  • ·Error 0xc020902a: Data Flow Task 1: The “output column “Email Address” (10)” failed because truncation occurred, and the truncation row disposition on “output column “Email Address” (10)” specifies failure on truncation. A truncation error occurred on the specified object of the specified component.
    (SQL Server Import and Export Wizard)

I sifted through the data, only a few hundred rows, and I could not see anything wrong with it. I made the columns the right type and increased the column size more than the largest number of characters in the fields. I searched Google and Microsoft Forums trying a handful of solutions and nothing worked.

So what did I do? I had enough and just opened the file from CSV, into Excel, resaved it as an Excel 2007. Re-ran the SSIS import wizard to open an Excel file instead of a CSV, and voila. I’m still baffled though, as to what exactly the problem is.