Selecting ListView Items with CheckBoxes

by ashic 14. August 2010 04:57

Often we need to enable users to select multiple items from a ListView control and do a batch operation on them. While the ListView control does enable selection, it only supports selecting one row (item) at a time. This article shows a nice, easy and reusable way to enable multiple selection with checkboxes.

The Project

I've created a very simple "Empty ASP.NET WebForms Application" and added a standard Default.aspx page. I've created an App_Data folder and added a database called PeopleDB.mdf into it. The database consists of one simple table holding the Id, Name and AwesomePoints of a number of people* and also if they are attending some event. We want to list them in a simple ListView with pagination, and be able to select some of them and set all of the selected people's AwesomePoints in one go. We also want to be able to select some other people and set the boolean stating whether or not they're attending.

*these people are purely fictional and any resemblance to real people are entirely coincidental ;)

The Page Layout

The page layout is fairly simple. It's ugly and OMG I'm using tables for layout – help – the Earth is gonna explode…yeah right. At least it's apparent what needs doing. We show a list of people and provide checkboxes for each row to select the person for either batch update of their AwesomePoints or batch update of their IsAttending value. At the end of the page, we provide controls to actually carry out the batch updates. Here's the page body's markup so far:


<body>
    <form id="form1" runat="server">
    <div>
        <asp:ListView runat="server" DataKeyNames='Id' ID='lvPeople' ItemPlaceholderID='ph1'>
            <LayoutTemplate>
                <table>
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Awesome Points</th>
                            <th>Is Attending</th>
                            <th>Select for Updating Awesome Points</th>
                            <th>Select for Updating Attendee Status</th>
                        </tr>
                    </thead>
                    <tbody>
                        <asp:PlaceHolder runat="server" ID='ph1' />
                    </tbody>
                </table>
            </LayoutTemplate>
            <ItemTemplate>
                <tr>
                    <td><%# Eval("Name") %></td>
                    <td><%# Eval("AwesomePoints") %></td>
                    <td><%# Eval("IsAttending") %></td>
                    <td><asp:CheckBox runat="server" ID='chkAwesomePoints' /></td>
                    <td><asp:CheckBox runat="server" ID='chkAttending' /></td>
                </tr>
            </ItemTemplate>
        </asp:ListView
        &lt;div>
            <asp:DataPager ID="DataPager1" runat="server" PagedControlID='lvPeople' PageSize='5'>
                <Fields>
                    <asp:NumericPagerField />
                </Fields>
            </asp:DataPager>
        </div>
        <br />
        <table border='none'>
            <tbody>
                <tr>
                    <td>Set Awesome Points to: <asp:TextBox runat="server" ID='txtAwesomePoints' /></td>
                    <td><asp:Button Text="Update Awesome Points" runat="server" /></td>
                </tr>
                <tr>
                    <td>Set Attendee Status to: <asp:CheckBox runat="server" ID='chkAttending' /></td>
                    <td><asp:Button Text="Update Attending Status" runat="server" /></td>
                </tr>
            </tbody>
        </table>
    </div>
    </form>
</body>

Notice that I've set the DataKeyNames fo the ListView to Id. I'll explain this part later.

The DataSource

We have the ListView, we now need to add a datasource. I could easily use a SqlDataSource or whatever else I want. But since I'll be updating the database as well, I'll just add an Entity Framework model to the project (and use an EntityDataSource control to populate the ListView). Right click the project in solution explorer and select "Add New Item". I chose ADO.NET Entity Data Model from the popup and name it PeopleModel.edmx.

1_adding_EF

From the next window, I select "Generate From Database". I get an option to select the connection to be used. Since I don't have one to the mdf file in App_Data, I select "New Connection". I ensure that the "Microsft SQL Serv Database File (SqlClient)" is selected and I "Browse" to my .mdf file in the App_Code folder. You could obviously use other databases and you'd configure the connection here. Finishing this dialog takes you back to the previous one, where you can click "Next".

2_EF_connection

The next window gives you options of selecting a subset or all of the tables, sprocs and views in the target database. We're only interested in the People table, so select it and click "Finish".

3_select_table

And that's it – our EF4 model is done. Let's now add an EntityDataSource to the page.To do this, add the following markup right after the ListView:


<asp:EntityDataSource runat="server" ID='edsPeople'
    ConnectionString="name=PeopleDBEntities"
    DefaultContainerName="PeopleDBEntities" EnableFlattening="False"
    EntitySetName="People"></asp:EntityDataSource>

(If you're using the webforms designer to hook up the datasource, be sure to compile the project before attempting to do so. Without a recompile, the required metadata would not be available to the user.)

Next, set the DataSourceId of the ListView to match the Id of the EntityDataSource:


<asp:ListView runat="server" ID='lvPeople' DataKeyNames='Id' ItemPlaceholderID='ph1' DataSourceID='edsPeople'>

At this point, we can run the page to see this:

4_initial_results

 

The ListView Extension

Add a folder called Extensions and in it, create a class called ListViewExtensions with the following code:



public static class ListViewExtensions
{
    public static List<DataKey> GetSelectedDataKeys(this ListView control, string checkBoxId)
    {
        return control.Items.Where(x => IsChecked(x, checkBoxId))
           .Select(x => control.DataKeys[x.DisplayIndex])
           .ToList();
    }

    private static bool IsChecked(ListViewDataItem item, string checkBoxId)
    {
        var control = item.FindControl(checkBoxId) as CheckBox;
        if (control == null)
        {
            return false;
        }

        return control.Checked;
    }
}

This class is the heart of what we're trying to do. It basically finds the checkbox with the Id passed in and returns the DataKey of every item in the ListView that has said checkbox checked. Remember the DataKeyNames property of the ListView that we set earlier? This is where that comes in. You can pass the names of the properties of each data item that you wish to have access to later on to the DataKeyNames. In our case, we only need the Id property, as such we passed in "Id". If for some reason, we wanted the AwesomePoints property too, we'd pass in "Id, AwesomePoints". Notice that our extension method returns a list of DataKeys. What is a DataKey? It's basically an object that holds the values of the properties mentioned in DataKeyNames. It has two properties – Value and Values. Value hold the value of the first data key property. So, if we had passed in "Id, AwesomePoints", Value would hold the value of the Id property for the associated item. Values on the other hand has an ordered dictionary. You would then find the value of the Id property in Values[0] and the value of the AwesomePoints property in Values[1] and so on.

Putting it to Work

We'll now add two handlers for the two button click events:



protected void UpdateAwesomePointsButton_Click(object sender, EventArgs e)
{
    var selectedKeys = lvPeople.GetSelectedDataKeys("chkAwesomePoints");
    var selectedIds = selectedKeys.Select(x => new Guid(x.Value.ToString()));

    if (selectedIds.Count() > 0)
    {
        var targetPoints = int.Parse(txtAwesomePoints.Text);
        var db = new PeopleDBEntities();

        db.People.Where(x => selectedIds.Contains(x.Id))
            .ToList()
            .ForEach(x => x.AwesomePoints = targetPoints);

        db.SaveChanges();
        lvPeople.DataBind();
    }

}

protected void UpdateAttendeeStatusButton_Click(object sender, EventArgs e)
{
    var selectedKeys = lvPeople.GetSelectedDataKeys("chkAttending");
    var selectedIds = selectedKeys.Select(x => new Guid(x.Value.ToString()));

    if (selectedIds.Count() > 0)
    {
        var targetValue = chkAttending.Checked;
        var db = new PeopleDBEntities();

        db.People.Where(x => selectedIds.Contains(x.Id))
            .ToList()
            .ForEach(x => x.IsAttending = targetValue);

        db.SaveChanges();
        lvPeople.DataBind();
    }
}

All we've done here is used our extension method to get the datakeys of the selected items in the ListView and have updated those items accordingly. Granted there's no validation or anything fancy going on – hey, this is a demo – it shows how we can know which items of a ListView have been selected.

Source

You can download the source by clicking here.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET

ASP.NET Routing with IIS 7 – Remember Your Modules

by ashic 26. July 2010 13:04

ASP.NET 4.0 introduces routing and it works very well. With IIS 7, there are some interesting configuration options that can help improve your site's performance. When done wrong, they can have unexpected side effects. And we're going to look at one of them today.

Extensionless Urls and Wildcard Mappings

Before IIS 7, the easy way to get extensionless urls when working with routing would be to have a wildcard mapping (yes, I know there are other ways with ISAPI handlers and stuff, but that's not too simple). A wildcard mapping would simply put every request for every file through the asp.net pipeline running all the managed modules and stuff. This does carry with it a certain performance hit – whether that hit applies to you or not, you'd have had to test for yourself. There's a good chance that you'd need admin privileges to do this, which is another problem if you're on shared hosting. Even without routing, there are a ton of different things that need to be set in IIS, with no simple way of overriding in your individual application (without IIS access).

Enter IIS 7

IIS 7 reads in a section of the web.config file called <system.webserver>. The <system.webserver> node goes directly inside the <configuration> node ([i.e. it's parallel <system.web>). This section is totally ignored by previous versions and can enable you to set settings that previously required IIS access or some not so nice workarounds to do (if at all possible). In this section, you can tell ASP.NET to run certain modules, not run others – or even to run all managed modules for every request. The latter feature is also the easiest to set up, and simply goes like this:


<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
   </modules>
</system.webServer>

That would do exactly what you'd expect – it'll run all managed modules for all requests. That's kind of like the wildcard mappings. Not the question is, do we need to run every single managed module for every request? Probably not. So, this can be a chance for improvement. We only want routing to run for all requests – and that should give us extensionless urls. So let's do that now:

An Improvement

We'll now tell IIS 7 to not run all managed modules for all requests.


<system.webServer>
  <modules>
   </modules>
</system.webServer>

That was simple, but that now means that the routing module isn't running. Let's make that run for all requests. We can do so by doing this:


<system.webServer>
  <modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
  </modules>
</system.webServer>

With this, our routing should work as expected, while not loading all the other managed modules for every request. So alls well, right?

Hang on a Mo'

While what we just did is better in the sense that it will only do routing for all requests (which we need for extensionless urls), we have potentially just introduced a bug. We're only running routing for every request, and all the other managed modules will only be run for known ASP.NET resources (for example, .aspx files). Read that again – other than routing, all the other managed modules are only be run for requests to known ASP.NET resources like .aspx files. Say, we have a page at http://mysite.com/folder/index.aspx. If we target that url, all shall be fine – IIS will see that the requested file has an aspx extension and it will run the required managed modules. Now, let's say we use routing to route requests for http://mysite.com/foo to that same page. If we request that url, IIS will look at it and will think it's not for ASP.NET. Since we configured the routing module to run, that will run and the request will get processed correctly by /folder/index.aspx. Keep in mind though, that since we aren't running all managed modules for all requests and don't have a wildcard mapping, IIS will not cause the other managed modules to fire. What does this mean? It means that commonly used functionality might suddenly disappear. If our /folder/index.aspx page uses Session, then we'll get an exception. (The error message for that particular one will keep you guessing – it tells you that you need to set enableSession to true!) In other words, if the page uses Session and if it's requested via it's actual path (i.e. ~/folder/index.aspx with the .aspx extension), then it'll run fine – but if it's requested with ~/foo (i.e. the extensionless route), then trying to access Session will fail.

So How do We Fix it?

One obvious way would be to enable all managed modules for all requests. But if we wish to follow our previous approach of only loading the required modules and not any more, then we can do this:


<system.webServer>
  <modules >
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
    <remove name="Session"/>
    <add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition=""/>
  </modules>
</system.webServer>

In other words, we're first removing the Session module and then adding it in again with a blank precondition. This will ensure that the Session module also gets run for all requests and not just for known ASP.NET resources. As such, Session will work regardless of whether you're using a real path or a routed path. Keep in mind though, that if the concerned module is loaded in IIS, then we must ensure we use the exact same name in the <remove> tag(s). There is a bit of magic stringy nature to this, but luckily, you can lookup the module name easily in IIS. In IIS 7 manager, just select the server from the left and double click on "Modules" from the centre pane. You should see a list of all loaded modules with the first column showing the names. That name is what you should give the <remove> tag in the web.config file. Remember, you'll need to do this for all the modules you intend to have running. These include - but are not limited to – Session, Forms Authentication, Windows Authentication etc.

So next time you're doing "optimized" routing and your routed pages don't seem to be working right, check to see if the modules are in fact getting loaded.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET | IIS 7

BUET, World Cups, Politicians, Students, Teachers, Authorities… ah BUET

by ashic 19. June 2010 16:31

I was planning on starting a write up involving ASP.NET, EF4, Spark, MSpec etc. when Zinat came over to tell me that BUET halls were again vacant. Slightly surprised, I went through facebook comments of ex-Buetians, blog posts, somewherein; and thought I'd write about this instead. And yes, I chose the words "slightly surprised" very carefully.

Let's first go back four years to the last World Cup. I was a student of BUET back then. And our exams fell during the event. Well, it's pretty much an open secret that BUET exams usually get delayed by a week or two. And football is big in BUET. A large number of BUET students follow the English Premiere League vehemently. They stay up into the early hours of the morning to watch the UEFA Champions League. Someone who is not one of them will never understand it – it'll seem stupid, a waste of time and what not. But to those that do, adore football. And there's a large number of them in BUET. I know. I was one of them. Anyway, since the exams were falling during the WC, many students gathered together and did peaceful rallies. The authorities made a surprisingly good decision and showed their flexibility by shifting the exams by a month. I say surprisingly good for two reasons. Firstly, had they not, there would probably have been an ugly conflict because most of the students had kind of committed to the WC at that time. They could hardly go for the exams at that stage. Secondly, they did not vacate the halls. The latter was quite important. A major appeal of football in BUET halls is watching matches together, with friends, as members of various factions –ManUs, Chelseas, Barcas, Reals, Gunners, Reds and so on. Watching it alone at home is nothing compared to that. Had the halls been vacated, it would have hurt every football fan in the halls. There is, nor has there ever been, a true football fan in the BUET halls who would want the halls vacated during a WC. It simply isn't possible for such a football fan to exist. The hall TV rooms are to them what pubs are to English footy fans on Saturday waiting for Manu v Arsenal to kick off. Anyway, a large number of students in the halls did want the exams to go back, and I was one of them. Many teachers or "now teachers" would say that's wasting a month and prolonging my student life. My simple answer is so what? We stayed at BUET through floods, curfews, elections, state of emergencies – we finished our four year course in just shy of five years. How much time would we have saved had the exams not been moved back a month? 30 days? If we can get delayed by whether Hasina or Khaleda or the Army can fight with words, guns or "boithas" by months, I think we can tolerate 30 days for something a lot of the students care about – and something that most would experience only once in their BUET lives. Given the option, I'd pick that 30 day delay again. Heck, you wait for Visas for longer than that.

Hang on, but there was a revolt, clashes with the police and BUET was closed for much longer, wasn't it? Yes, it was. And this is the funny thing really. Well maybe not funny but sad. After the world cup was over, a few seniors (yes, it started in the 4-2 batch – quite surprising as that was the batch that did anty-rallies for the WC delays) started another agenda wanting one further week. And this was what the authorities got wrong. And it was an easy one to get wrong. This extension was not wanted by a majority of the students, but those very few (less than 30) – who were also involved with a political party. They somehow managed to give it momentum, which resulted in the police clashes and subsequent notice to vacate the halls. There was a major "investigation" and many students were suspended, fined etc. I myself was fined Tk. 20,000.00 and stripped off my board scholarships. I have no idea how these "investigations" were conducted, because the act that I was accused of took place on a day I wasn't even in BUET. (I was at the office, I was working part time at Code 71, then Asha-Technologies LLC, the whole day.) Yes, I supported the WC agenda, the one afterwards I did not. Yet the latter was the one that I was punished for. And on top of that, the really really interesting thing was that the less than 30 guys that started the thing – they didn't incur anything. Quite interesting that a guy sitting in an office in Dhanmondi and having nothing to do with the revolt gets fined whereas the initiators go free having affiliations with a political party. The majority of students didn't want the one week extension. Nobody asked for the halls to be vacated. Are BUET students aggressive? Is that why they broke cars and windows? Or did those few have a larger agenda? Did they want to be seen as "heroes" ahead of the general elections? Pretty weird how teachers seem to blame it on the students and not those few politicians. Strange how they get off dirt free, and are never even mentioned.

That was then. This is now.

And again, reading the blogs and comments, I see a few alarming words coming up - "neta", "leader", "rajnoitik neta", "politician". These are words that shouldn't exist in the context of BUET. I thought we had no centralised politics. How come there are "league" and "dol" and "moitri" and what not? Yes, we need bodies to protect students from rising of fees, unexpected discrepancies, preventing privatisation of BUET etc. – but shouldn't those bodies be geared towards the students? How come they seem to cater to national political parties? While I was still at BUET, a known "political leader" in BUET of our batch actually "beat up" a senior guy after being ordered by the "central" party of which the leader was a member. How come BUET students are caring more about political parties than their fellow students?

Everyone seems to be comparing 2010 with 2006. One thing they're forgetting is that the hall vacant of 2006 happened after the world cup was over. It was for a different issue with a political agenda of keeping the senior leaders on campus till the elections. The 2006 WC was watched peacefully by a LOT of BUET students in their halls – cheering and shouting together. That is not what's happened this time. The halls are vacant during the WC. The only thing in common seems to be the word "neta".

Everyone slamming the students seem to be teachers. Everyone slamming the authorities are students. I'm neither – while at BUET I was not with any student body, group, clique or gang. I'm as far from "atel" as you can go – I got no grades, I was more enthused in .NET than in Knuth and erm… (can't remember any other authors I hated or I found them irrelevant – seriously, come on, Pressman's book for Software Engineering? Anybody heard of Fowler, Evans or "Uncle Bob" Martin? It's not 1995 you know.) I was doing part time gigs at Code 71 or freelancing projects to clients worldwide (built a piece of software being used at Burj-al-Arab). And during classes, I was either a) sleeping, b) working on my laptop or c) reading "relevant" books ;) I'm not one of the teachers, and not one of those students who try to close down BUET for personal political ambitions. And here's how I see it:

1. The authority needs to smarten up. They need to know which battles they can win. Shutting down BUET is not a win for them. It's a major loss. In terms of backlog, in terms of reputation and most importantly, in terms of the future of the students. Sometimes sacrificing a week or a month to prevent a delay of six months in necessary. Yes, that would be a defeat for their egos. But it would be a win in the long run. The 2006 administration showed a lot of flexibility with the WC. The demand for the extra week afterwards was uncalled for. Even asking it was a travesty. Yet, when asked, the administration took a strong hand. And BUET was closed for months for one measly week (at the end of an already done month long delay). Hardly anybody could have predicted what would eventually happen. But it's not the job of "anybody" to make those decisions. That's what the authority is there for. Seniors in our hall (hi Rahat bhai – who was even against the WC delay) predicted that if the one week wasn't given "those few" seniors (Rahat bhai obviously not among them) would start a riot and the halls would be vacated. If a Civil Engineering student can accurately predict that, how incompetent / egoistic was the authority to neglect the severity of the situation?

2. Students need to smarten up. Pick your battles too. You want to watch the WC? Go ahead. I believe you were given the opportunity. But know the limits. In 2006, asking for that one week after the world cup – even if manipulated by a select few – was either disgusting or stupid. Don't be pawns of political games. If you really do love politics, wait till you graduate. Being a "patineta" in BUET is worthless – central parties will never make you a Hasina or Khaleda. And don't follow those "Patinetas".

3. It's been said a million times and I'll say it again. The system needs to change. We have 70% of a course riding on a 3 hour exam. It's absurd. It's arrogant. It's lazy. Sure it's easier for teachers, but get over it. Having only 20% on class tests with barely any assignments (the other 10% is for attendance) is ridiculous. BUET is not a "Bangla" university. It's an engineering university. I can confidently say that hardly any student actually learns any of the subjects in her four years. They cram before the exams and forget everything afterwards. That is why our CSE graduates cannot answer these questions during a job screening:

a) Name three design patterns (most couldn't write three).
b) Give an example of xml data (most left it blank).
c) Show an example of polymorphism (most gave an example of inheritance).
d) What is http and https? (some got http right)

And it was not a few that failed these questions – most did. These are basic questions…are BUET students stupid? Are they really that crap? No…the reason that they failed is entirely the system's fault. The syllabuses are too big and on top of that, everything rides on one 3 hour exam. Is there anyone who would not fear that exam? Maybe one or two wouldn't, but the vast majority would be easily manipulated by politicians into joining any effort to delay the exams and gain extra prep time. This is one area that really does need to change. Till then, teachers will keep blaming the students, students will keep blaming the administration and those dirty politicians will keep manipulating students into doing vile acts. Or perhaps the teachers really do want to keep BUET closed. After all, that gives them free time to do projects, write papers or some plain and simple R&R.

4. Politics in BUET is at best nothing and at worst a shadow of DU. These "patinetas" shouldn't exist and are harming BUET and her students every term. What good do they do? Seriously? Even if some are involved with charities and student rights, do we need them? If there's a just humanitarian cause (Hridoy) or a genuine protest (evening shifts), students would do so themselves. They don't need DU leaders "blessing" them to do so.

5. If you're going to do investigations, do them properly. Don't interview 500 people and then accuse someone of something they couldn't have done and let the kingpins go free.

Will this change in 2014? I do believe classes / exams will be delayed during the WC month in 2014. And in a 4-5 year context, I'd think that's acceptable. What I wouldn't want to see is another "hall vacant" and BUET being closed for 6 months.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Tags: ,
Categories: BUET

Anonymous Types are Internal, C# 4.0 Dynamic Beware!

by ashic 26. May 2010 14:10

C# 4.0 has introduced the dynamic keyword. You can declare a variable as dynamic and regardless of what can be inferred at compile time, you can access any properties and call any methods and your code will still compile. Resolving of those properties and methods will be done at runtime. If at runtime, they aren't found, you'd get a runtime exception. If they are found, your code will run fine.

An Example

Consider a class called Person that has a property called Name. Consider the following piece of code:



Person p = new Person { Name = "John" };
object o = p;
dynamic d = o;
Console.WriteLine(d.Name);

On line 2, we're assigning p to an object reference o. Before C# 4.0, if you wanted to get the value of o's Name (and it does have a Name, since we know we assigned a Person object to o) you would have had to resort to reflection. With C# 4.0 however, we can use a dynamic variable like we did on line 3, and simply access the Name property on the dynamic variable. This program would compile fine, but it would require that the object assigned to d must (at runtime) have a property called Name. If it doesn't, it'll throw an exception.

So basically, we can assign a dynamic variable with any object and call properties and methods we expect it to have and it should run fine. Right? Not quite.

The Problem

The dynamic approach has one limitation – it must know the type of object it's dealing with. This isn't a problem for most cases, but it can be an issue when dealing with anonymous types. Let's get started building the project so you can see what I mean.

The Project

Create a basic .Net 4.0 console application called DynamicTest. Add a class library called ClassLibrary1 to the solution. Make class1.cs look like this:



namespace ClassLibrary1
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    } 

    public class Repository
    {
        Person _person = new Person { Name = "Adam", Age = 21 };
       
        public object GetPerson()
        {
            return _person;
        }

        public object GetPersonWrappedInAnonymousType()
        {
            return new { Person = _person };
        }
    }
}

We have a repository that has two methods. One simply returns a person as an object while the other wraps it in an anonymous type and returns an instance of that anonymous type. Going back to the main project's Program.cs, add the following code:



class Program
{
    static void Main(string[] args)
    {
        new Program().Run();
    }    

    private void Run()
    {
        var rep = new Repository();

        dynamic data = rep.GetPerson();
        Console.WriteLine(data.Name); 

        dynamic data2 = rep.GetPersonWrappedInAnonymousType();
        Console.WriteLine(data2.Person.Name);
    }
}



That looks simple enough. data and data2 are both dynamic variables. As such, their Name and Person.Name properties will be resolved at runtime. The program compiles fine, but when we run it, we get this:

Adam

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'obj
ect' does not contain a definition for 'Person'
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T
0 arg0)
   at DynamicTest.Program.Run() in F:\MyTests\C#\DynamicTest\DynamicTest\Program
.cs:line 24
   at DynamicTest.Program.Main(String[] args) in F:\MyTests\C#\DynamicTest\Dynam
icTest\Program.cs:line 13
Press any key to continue . . .

How come? The call to data.Name resolved fine, but the call to data2.Person failed. The error message states that the Person property could not be found at runtime. We can obviously see by looking at the code that data2 most definitely has a Person property and that in turn has a Name property. [And if you don't trust your eyes, you can run in debug mode, set a breakpoint just after data2 is initialized and if you use the watch window to view the properties of data2, you'll see that data2 does indeed have a data2.Person property but if you type in "data2.Person" in the watch window, you'll get the same exception…magic!]. So what gives?

Explanation

The reason the call to data2.Person fails is that the type information of data2 is not available at runtime. The reason it's not available is because anonymous types are not public. When the method is returning an instance of that anonymous type, it's returning a System.Object which references an instance of an anonymous type -  a type who's info isn't available to the main program. The dynamic runtime tries to find a property called Person on the object, but can't resolve it from the type information it has. As such, it throws an exception. The call to data.Name works fine since Person is a public class, that information is available and can be easily resolved.

This can affect you in any of the following cases (if not more):

1. You're returning a non-public, non-internal type using System.Object.
2. You're returning a non-public, non-internal derived type via a public Base type and accessing a property in the derived type that's not in the base type.
3. You're returning anything wrapped inside an anonymous type from a different assembly.

i.e. you need to be able to access the type info for the object at the point where its property / method is being resolved at runtime.

Solution

The solution is actually quite simple. All we have to do is open up AssemplyInfo.cs of the ClassLibrary1 project and add the following line to it:


[assembly:InternalsVisibleTo("DynamicTest")]


What this does is allow the main project to "look into" the internal types of ClassLibrary1. This means the dynamic runtime can find the type information of the anonymous type being returned and data2.Person.Name resolves perfectly. Anonymous types are internal. Adding that piece of code makes the type information "visible" to our main project.

Drawback

As you can see, the solution means you actually have to have control over the class library's source code for this to work. If you don't, then I guess you'd need to resort to good old fashioned reflection.

Application

This approach can come in handy for unit testing. You would expose the internal types of the assembly under test to the tests, and as such should be able to take advantage of the dynamic functionality of C# 4.0 when evaluating results from methods that return anonymous types.

Source

download

Shout it kick it on DotNetKicks.com

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

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

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

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Tags: , ,
Categories: C# | .NET

ASP.NET MVC – Unit Testing JsonResult Returning Anonymous Types

by ashic 25. May 2010 15:41

Unit testing of ASP.NET MVC JsonResults can be a source of confusion. The problem arises from the fact that an Action Method itself doesn't produce any html / json / string output – it simply returns an Action Result. ASP.NET MVC then calls the ExecuteResult() method on that Action Result. The ExecuteResult() method is what causes output to be written to the Response stream. Let's take the following controller and action method for example:



public class HomeController : Controller
{
    public ActionResult Index()
    {
        var people = new List<Person>
        {
             new Person{ Name = "Adam", Age=21},
             new Person{ Name = "Eve", Age=20},
             new Person{ Name = "Joe", Age=50}
        }; 

        return Json(new
        {
            People = people,
            Count = people.Count,
            Success = true
        }, JsonRequestBehavior.AllowGet);
    }
}

Here's the (very simple) Person class:



public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

As you can see, the Index method is simply returning an anonymous type which holds a list of people, the count, indicates success and wraps the whole thing in a JsonResult. When the Index() action method executes (i.e. is called from the browser), it produces the following json:

{"People":[{"Name":"Adam","Age":21},{"Name":"Eve","Age":20},{"Name":"Joe","Age":50}],"Count":3,"Success":true}

Nothing new there. Now let's say we wish to unit test this method [I know, I know…in a real app, the list of people would come from a service or repository or whatever…but for unit tests, you'd have mock whatevers or stub whatevers and would know what the data being returned would be]. We naively write something like this:



[TestMethod]
public void IndexTestWhichShouldFail()
{
    //arrange
    HomeController controller = new HomeController();

    //act
    var result = controller.Index() as JsonResult;

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        result.Data.ToString());
}

We run the test, and it fails miserably:

Assert.AreEqual failed. Expected:<{"People":[{"Name":"Adam","Age":21},{"Name":"Eve","Age":20},{"Name":"Joe","Age":50}],"Count":3,"Success":true}>. Actual:<{ People = System.Collections.Generic.List`1[MvcApplication1.Models.Person], Count = 3, Success = True }>.

What went wrong? The reason that the test failed was because we were comparing apples to oranges. We expected a json string of results, but we were passing the ToString() value of the JsonResult's Data property. But shouldn't Data.ToString() simply give us the json string? Erm…no. The Data property simply holds the object passed into the Json() method in Index(). As such, it's simply an instance of an anonymous type. Calling ToString() on it does not cause serialization to json…it simply tells .NET to convert it to a string. The Actual value of the test result above shows what that looks like (i.e. instead of the People list getting serialzed to json, we get an ugly List`1 of Person). So how come it gives us proper json when running from a browser? The reason is that ASP.NET MVC takes the JsonResult and calls its ExecuteResult() method, which (among other things) writes the json to the Response.

So how do we unit test that?

Well, if we were returning non-anonymous types, then we could simply cast JsonResult.Data to the expected type and check its properties. But we're returning an anonymous type, aren't we?

So how do we unit test that?

Well, there're three approaches we can take.

Approach 1: The Easy Way

We know that we have the object and we know what the json for it should be. We can just serialize the object and see if it matches what we expect. Here's the test:



[TestMethod()]
public void IndexTest()
{
    //arrange
    HomeController controller = new HomeController(); 
   
    //act
    var result = controller.Index() as JsonResult;
    var serializer = new JavaScriptSerializer();
    var output = serializer.Serialize(result.Data);

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        output);
}

Nothing complicated there. It's basically doing the same thing as the ExecuteResult() method of JsonResult does (in terms of producing the Json string via the JavaScriptSerializer). Now some of you may argue that this is not a unit test as it tests serialization as well as the method under test (i.e. Index). I would disagree. The reason being that you should only be testing your code. Testing JavaScriptSerializer should not be your focus when you're testing the Index method. And since checking the serialzed result is easier for you (i.e. you know what the json should be), you may as well check against that.

Approach 2: The "Pure" Way

This approach executes pretty much what the MVC framework does to produce output. For this, we're going to use Moq to fake a controller context and response and capture the content written to Response during execution in a StringBuilder. We will execute the result with the fake context, which will fill our StringBuilder with the serialized output. We will then check to see if it is what we want it to be. Here's the test:



[TestMethod()]
public void IndexTestWithMock()
{
    //arrange
    HomeController controller = new HomeController();
    var result = controller.Index();
    var sb = new StringBuilder();
    Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
    response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
    {
        sb.Append(y);
    });
    Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
    controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);

    //act
    result.ExecuteResult(controllerContext.Object);

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        sb.ToString());
}

Notice that our "act" of the test is not when we call controller.Index(), rather when we call ExecuteResult() on the JsonResult. In the previous approach, we simply called into the controller's action method. In this approach, we are actually doing what MVC does when dealing with a Request (at least, parts of it). To make it play the way we want it to, we created Mocks that behave the way we want them to. [Of course, the "arrange", "act" and "assert" parts are just nomenclature and one could say that the "act" is when Index() is called and everything afterwards is part of "assert"…]

Approach 3: Reflection

The previous two approaches compared the generated json against the expected json. As I said, that might upset some people as it kind of tests serialization too. I'm fine with it, but if someone wanted to test the JsonResult itself and not the generated output, one approach they can take is reflection. Here's a test that does just that:



[TestMethod]
public void IndexTestWithReflection()
{
    //arrange
    HomeController controller = new HomeController();

    //act
    var result = controller.Index() as JsonResult;

    //assert
    var data = result.Data;
    var type = data.GetType();
    var countPropertyInfo = type.GetProperty("Count");
    var expectedCount = countPropertyInfo.GetValue(data, null);

    Assert.AreEqual(3, expectedCount);
}

Since result.Data is an instance of an anonymous type, you can't really cast it to anything useful. You can check the values of result.Data via reflection. It can be a bit messy, but you could write a utility library to get to the properties in an easier fashion. There is one problem though…you're checking for strings (i.e. the property "Count") and as such, refactorings like renaming of a property will cause your tests to fail.

Approach 4: Dynamic

One way to avoid reflection would be to use .Net 4.0's dynamic capabilities. This does, however, require a bit of tinkering. We'll need to open up AssemblyInfo.cs of the MVC application and add this to the bottom:


[assembly:InternalsVisibleTo("TestProject1")]

Why we need to do this is explained in this article.

Once that's done, we can write our test like this:



[TestMethod]
public void IndexTestWithDynamic()
{
    //arrange
    HomeController controller = new HomeController();    

    //act
    var result = controller.Index() as JsonResult;    

    //assert
    dynamic data = result.Data; 

    Assert.AreEqual(3, data.Count);
    Assert.IsTrue(data.Success);
    Assert.AreEqual("Adam", data.People[0].Name);
}

Note how we're avoiding reflection, but still are testing the data that went into the JsonResult as opposed to the json string gotten from executing the JsonResult.


All three of these approaches will work. Pick whichever one suits your ideologies.

And yes, I'm aware that the Index() method would be better off returning a PagedList<Person>, and that Success=true is for the most part redundant (if there was an error, it should throw an exception and the calling javascript code should be able to understand that there was an error). The Index() method is for illustrative purposes only ;)

Update

I've added a fourth approach (i.e. dynamic). The source code download below contains the update.

Code

Click here to download the source code.

Shout it kick it on DotNetKicks.com

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

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

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

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET | ASP.NET MVC

Fetching a Property Value via Reflection

by ashic 10. May 2010 21:31

Reflection is a method by which we can use metadata at runtime to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. It has many uses, ranging from allowing usage of attributes to creating instances of dynamically generated classes and using them. You can even use reflection to read and modify private member variables of an object. Today, we're going to look at a very simple example which will allow us to read the value of a public property given an instance.

The Class

Let's take a very simple class:



namespace MyNamespace
{
    public class MyClass
    {
        public int MyProperty { get; set; }
    }
}

Note, that I've put it in a separate namespace to show that it's easy to deal with this when using reflection.

The Runner

We'll run everything in a very simple console application:



public class Program
{
    static void Main(string[] args)
    {
        new Program().Run();
    }

    private void Run()
    {
        MyClass o = new MyClass { MyProperty = 25 };
        object value = GetPropertyValue(o, "MyProperty");
        Console.WriteLine(value);
    }
}

We're basically creating an instance of MyClass and passing the instance and the string "MyProperty" to a method called GetProperty. The method is returning an object (which we could have cast to anything we needed). We're passing the returned object to Console.WriteLine.

The Method

This is where we use reflection to find the value of the "MyProperty" property given an instance. Add the following method to the class:



private object GetPropertyValue(object o, string propertyName)
{
    Type type = o.GetType();
    PropertyInfo info = type.GetProperty(propertyName);
    object value = info.GetValue(o, null);
    return value;
}

We're getting the type information by calling GetType() on the object. Next, we call GetProperty() on the type information. Note, this only gives us information about the property and not the property itself. We then have to call GetValue() on the property information passing in the instance from which to get the actual value.

And that's all it takes :)

PS: You'll need a reference to System.Reflection to use Reflection.

PPS: Yes, this is a very very simple example of using reflection, but I found quite a few questions over at the forums today that would be answered by this. We all have to start somewhere, right?

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET | C#

ASP.NET MVC: ModelBinding Multiple File Uploads to an Array

by ashic 6. May 2010 14:46

Web apps often need to upload files. ModelBinding a file upload to an HttpPostedFileBase action parameter is straightforward as long as you know the exact name of the file upload html control. There may be some situations where there are quite a few file upload controls and for whatever reason, they have very different names (they could be autogenerated on the page for example). In such a case, it becomes difficult to know what names to give to the action parameters at compile time. The usual option would be to hook into the Request parameters. That works, but is not very testable (without a good mocking framework like SharpMock, Moles, TypeMock Isolator etc.). In such situations, we can use a model binder to bind all file uploads to a single upload parameter.

The Container

The first thing to do is to create the class to hold the information relating to the uploaded files:



public class UploadedFileInfo
{
    public string Name { get; private set; }
    public HttpPostedFileBase File { get; private set; }

    public UploadedFileInfo(string name, HttpPostedFileBase file)
    {
        Name = name;
        File = file;
    }  
}

The class is pretty simple. It holds the name of the file upload control that uploaded a file and the uploaded file itself.

The ModelBinder

Next comes the ModelBinder:



public class UploadedFileInfoArrayBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var files = controllerContext.HttpContext.Request.Files;
        var list = new List<UploadedFileInfo>();

        for (int i = 0; i < files.Count; i++)
        {
            var file = files[i];
            var name = files.AllKeys[i]; 
            var fileInfo = new UploadedFileInfo(name, file); 
            list.Add(fileInfo);
        }

        return list.ToArray();
    }
}

Basically, the binder looks into Request.Files and for each file present, it creates and instance of UploadedFileInfo and adds it to a list. It simply returns the list as an array. Please note that if a user does not select a file for an upload control, it will still have an entry in Request.Files, only the uploaded file's ContentLength would be zero.

The Controller

And the controller:



public class UploadTestController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(UploadedFileInfo[] files)
    {
        var uploadedCount = files.Where(x => x.File.ContentLength > 0).Count();
        ViewData["Message"] = string.Format("{0} file(s) uploaded successfully", uploadedCount);

        return View();
    }
}

The GET Index() is trivial. The Post Index() fetches the number of files in the model-bound files array that have a non-zero content length and passes the info to the View.

The View

And the view:


<body>
    <div>
        <form enctype="multipart/form-data" method="post">
            <input type='file' name='file1' id='file1' />
            <input type='file' name='file2' id='file2' />
            <input type="submit" value='upload' />
        </form>
    </div>
    <br />
    <%: ViewData["Message"] %>
</body>

The View is the same for the GET and the POST (don't mind the fact that I should be redirecting after the post – this is a demo after all!). There's a form that has two file uploads in a form. The file uploads have different names. The View also spits ViewData["Message"] if present.

Assigning the ModelBinder in Global.asax.cs

At this point, our code still won't work as the ModelBinder is not being applied to the files parameter of the POST Index(). We can add an attribute to the parameter, but there's a cleaner, nicer way to set it up in the global.asax.cs file:



protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders[typeof(UploadedFileInfo[])] = new UploadedFileInfoArrayBinder();
}

In the last line, we're basically telling MVC that whenever it encounters a parameter to an ActionMethod that is an array of UploadedFileInfo, it should use an UploadedFileInfoArrayBinder. The binder will then fetch the files from the Request and assign it to the parameter.

Running the App

With all this in place, we can run the app, select a couple of files and hitting upload should tell us that the files have been uploaded.

Source

download

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

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

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

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: ASP.NET | ASP.NET MVC

Speaking on Silverlight 4 and WCF RIA Services at BRAC University

by ashic 20. March 2010 04:54

I'm speaking on Silverlight 4 and WCF RIA Services at BRAC University on March 31, 2010. Hoping to wet the appetites of the developers of tomorrow.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: Community

Speaking On Silverlight 4 and WCF RIA Services at NSU

by ashic 20. March 2010 04:50

I'm speaking on Silverlight 4 and WCF RIA Services at North South University on March 31, 2010. Hoping to finally meet David Lim :)

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: Community

Encrypted Hidden Redux : Let's Get Salty

by ashic 13. March 2010 09:37

This article builds on the ideas presented in my earlier article http://www.heartysoft.com/post/2010/02/25/Encrypted-Hidden-Inputs-in-ASPNET-MVC.aspx

If you haven't read that yet, I'd recommend doing so before proceeding.

Problems with the Previous Approach

The approach outlined in the previous article is pretty secure and easy to use. However, there are a few issues that can be improved upon:

  1. Security: The approach is using a symmetric encryption process with a fixed private key. Asymmetric encryption would no doubt provide more security, but the performance costs and hassle is probably not worth it. The problem with symmetric encryption is that brute force attacks can potentially break it. As such, using only a fixed encryption key could be a security issue. Given enough time and test pages, the encrypted values could lead to the leaking of the private key. This would result in the whole process being compromised. A couple of people provided feedback on this and were concerned. Still, such brute force attacks could take hundreds of years as the attacker would be required to test at least 2^56 keys. That's not an easy task, but we can use some salting techniques to make it even harder (a lot harder) to break. This would still be within the realms of symmetric encryption but would provide (a LOT of) added security. I'll explain the salting technique later.
  2. CSRFs: With the previous approach, an attacker could take the encrypted value and submit it as part of another request from his browser. The system would decrypt and use the value as normal. The previous process expected that the developer would use ASP.NET MVC's AntiForgeryToken to prevent CSRFs. In other words, the process did not prevent CSRFs on its own, but expected the developer to handle them by other means. With the new process outlined in this article, this will no longer be necessary.
  3. Encryption and Hashing method: The previous process used Triple DES for encryption and MD5 for hashing. The new process uses Rijndael encryption and SHA256 hashing. Rijndael is approved by the US government and is natively supported by the .Net framework. If the encryption and decryption are both going to be done on Windows boxes, then Rijndael is definitely the encryption algo to use over TDES. Similarly. SHA256 has a managed implementation and is superior to MD5.
  4. Code Smell: The previous implementation kind of had some code smells. Stuff was there where it shouldn't have been and it just didn't feel right. I reworked the design and although all smells aren't gone (hey, it's a demo!), a lot of the stench has been removed.

So What's Our Salt?

A salt is basically some random bytes added into our actual data during the encryption process. Having access to the key and the salt enables complete decryption. Having access to one but not the other doesn't. The salt needs to be random enough so that different users have different ones. We still need the salt to facilitate decryption on the server. A common technique is to store part of the salt at the server and part of it in the html sent to the browser. Other techniques generate the salts using some logic and sends some or none of the salt to the client. The bottom line is that the salt needs to be random so that an attacker can't simply take some values from a legitimate page output and then post it himself. The salt still needs to be available on the server when a legitimate user submits the page. ASP.NET MVC actually has such a per user per session value baked right in. It's the AntiForgeryToken. However there are a couple of issues that prevent us from using it directly:

1. The AntiForgeryToken helper uses some internal classes to generate the token data. As it's internal to MVC, we can't directly access the token data. We need to generate the AntiForgeryToken input tag and parse its value.

2. The AntiForgeryToken value is per user per session. If the developer puts an AntiForgeryToken on the page, then that value will be visible to the attacker. That essentially hands the attacker the salt. This is not acceptable. Luckily, the AntiForgeryToken helper can accept any string as its salt (yes, its own salt – not our final salt). We can pass a fixed string as the salt and that would ensure that using AntiForgeryTokens on the page doesn't interfere with or expose our salt.

As I said, the AntiForgeryToken's value is constant for the same user in the same browser session and the same "salt" parameter passed to the helper. But if the user opens another browser window or another user uses the system, then the value would be completely different. With this approach, we don't need to send any of the salt to the browser as all of it can be generated on the server on page submission. Only the encrypted values are ever sent to the client.

The Project

Since quite a bit of things have changed since the previous approach, I'll start afresh. First thing to do is create an MVC 2 project called "HiddenEncryptDemo".

I installed MVC 2 RC 2 before installing VS 2010 RC. If you have some other combination, then slight changes might be necessary, although the core ideas will still work (even with MVC 1.0).

Create a Models folder and add in a class called Computer:


public class Computer
{
    public string Setting { get; set; }
}

The reason I'm adding this class is to show that our approach will work not just on simple action parameters, but also on more complex types without a need for a separate model binder.

Next, add this to the Index.aspx view (in the /Views/Home folder):


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
    <%= Html.Hidden("computer.Setting", "hello world!") %>
    <input type="submit" value='submit' />
<% Html.EndForm(); %>

After that, add the following method to the Home controller:


[HttpPost]
public ActionResult Something(Computer computer)
{
    ViewData["Message"] = computer.Setting;
    return View("Index");
}

Nothing fancy, we're just setting the ViewData["Message"] to the computer.Setting value. Notice that in the code we added to Index.aspx, the form submitted to "Something" (the name of our action) and the name of the hidden input was "computer.Setting". The default model binder will find a value for "computer.Something" in the request parameters and upon seeing that it can set the Setting property of the computer parameter, it's going to set computer.Setting to the value it found (in our case, "hello world!"). If you run the project, you should see the index page with a submit button. Clicking the button will result in a post to the server where the Something action will get called. The Something action will then set the ViewData["Message"] and return the Index view. As such, you will see the words "hello world!" displayed on the page:

hello

On the initial Index page (or the page got after clicking the button), if you right click anywhere and select view source, you should see this (among other things):

hidden

Notice how the helper added a hidden input control, set its id to "computer_Setting" and name to "computer.Setting". The name is used as the key of the data in the post variables. Also notice how the value is "hello world!". The value is clearly visible to anyone looking at the source. Our goal is to come up with a way to hide that value from clear sight while at the same time ensuring everything works as smoothly and easily as it just did. A bonus would be if no change was required in the controller's code (i.e. no specific attributes needed on the consuming action or controller, no special model binding needed for the parameter etc.) – it would be unobtrusive. These are the exact requirements of the previous approach, but today, we want to add one more – the encryption should use a different salt for each user's session so that brute force approaches of uncovering the encryption key will fail and CSRFs will be prevented by default.

The Settings Provider

In the previous article, we handled settings from within the encryption provider. This seemed kind of messy and I decided to use a separate settings provider. Add a "Helpers" folder to the project and add the interface ISettingsProvider to it:


public interface ISettingsProvider
{
    byte[] EncryptionKey { get; }
    string EncryptionPrefix { get; }
    string SaltGeneratorKey { get; }
}

Next, add an implementation for the interface. The SettingsProvider class looks like this:


namespace HiddenEncryptDemo.Helpers
{
    using System.Configuration;
    using System.Security.Cryptography;
    using System.Text;
   
    public class SettingsProvider : ISettingsProvider
    {
        private static readonly byte[] _encryptionKey;
        private static readonly string _encryptionPrefix;
        private static readonly string _saltGeneratorKey;

        static SettingsProvider()
        {
            //read settings from configuration
            var useHashingString = ConfigurationManager.AppSettings["UseHashingForEncryption"];
            bool useHashing = true;
            if (string.Compare(useHashingString, "false", true) == 0)
            {
                useHashing = false;
            }

            _encryptionPrefix = ConfigurationManager.AppSettings["EncryptionPrefix"];
            if (string.IsNullOrWhiteSpace(_encryptionPrefix))
            {
                _encryptionPrefix = "encryptedHidden_";
            }

            _saltGeneratorKey = ConfigurationManager.AppSettings["EncryptionSaltGeneratorKey"];
            if (string.IsNullOrWhiteSpace(_saltGeneratorKey))
            {
                _saltGeneratorKey = "encryptionSaltKey";
            }

            var key = ConfigurationManager.AppSettings["EncryptionKey"];

            if (useHashing)
            {
                var hash = new SHA256Managed();
                _encryptionKey = hash.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hash.Clear();
                hash.Dispose();
            }
            else
            {
                _encryptionKey = UTF8Encoding.UTF8.GetBytes(key);
            }
        }

        #region ISettingsProvider Members

        public byte[] EncryptionKey
        {
            get { return _encryptionKey; }
        }

        public string EncryptionPrefix
        {
            get { return _encryptionPrefix; }
        }

        public string SaltGeneratorKey
        {
            get { return _saltGeneratorKey; }
        }

        #endregion
    }
}

Basically, we're just reading in some settings from the web.config file in the static constructor and returning the static values from the instance based getters. Notice that if UseHashingForEncryption setting is set, we first hash the encryption key in web.config using SHA256Managed before using it. The EncryptionPrefix is a prefix used to identify hidden inputs which were previously encrypted. The SaltGeneratorKey is a key used in getting the AntiForgeryToken.

The Encryption Provider

Add an interface called IEncryptString:


public interface IEncryptString : IDisposable
{
    string Encrypt(string value);
    string Decrypt(string value);
}

And an implementation called RijndaelStringEncrypter:


namespace HiddenEncryptDemo.Helpers
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public class RijndaelStringEncrypter : IEncryptString
    {
        private RijndaelManaged _encryptionProvider;
        private ICryptoTransform _encrypter;
        private ICryptoTransform _decrypter;
        private byte[] _key;
        private byte[] _iv;

        public RijndaelStringEncrypter(ISettingsProvider settings, string salt)
        {
            _encryptionProvider = new RijndaelManaged();
            var saltBytes = UTF8Encoding.UTF8.GetBytes(salt);
            var derivedbytes = new Rfc2898DeriveBytes(settings.EncryptionKey, saltBytes, 3);
            _key = derivedbytes.GetBytes(_encryptionProvider.KeySize / 8);
            _iv = derivedbytes.GetBytes(_encryptionProvider.BlockSize / 8);
        }

        #region IEncryptString Members

        public string Encrypt(string value)
        {
            var valueBytes = UTF8Encoding.UTF8.GetBytes(value);

            if (_encrypter == null)
            {
                _encrypter = _encryptionProvider.CreateEncryptor(_key, _iv);
            }

            var encryptedBytes = _encrypter.TransformFinalBlock(valueBytes, 0, valueBytes.Length);
            var encrypted = Convert.ToBase64String(encryptedBytes);

            return encrypted;
        }

        public string Decrypt(string value)
        {
            var valueBytes = Convert.FromBase64String(value);

            if (_decrypter == null)
            {
                _decrypter = _encryptionProvider.CreateDecryptor(_key, _iv);
            }

            var decryptedBytes = _decrypter.TransformFinalBlock(valueBytes, 0, valueBytes.Length);
            var decrypted = UTF8Encoding.UTF8.GetString(decryptedBytes);

            return decrypted;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_encrypter != null)
            {
                _encrypter.Dispose();
                _encrypter = null;
            }

            if (_decrypter != null)
            {
                _decrypter.Dispose();
                _decrypter = null;
            }

            if (_encryptionProvider != null)
            {
                _encryptionProvider.Clear();
                _encryptionProvider.Dispose();
                _encryptionProvider = null;
            }
        }

        #endregion
    }
}

This is a sginificant change from our previous encryption method. Previously, we created a single pair of ICryptoTransforms for encryption and decryption based on the encryption key. Since we need to provide different salts for encryption for different users and different sessions, that approach will no longer work. In the new approach, each instance of the RijndaelStringEncrypter has its own salt value. As a consequence, each instance needs its own pair of ICryptoTransforms. Looking at the constructor code:


public RijndaelStringEncrypter(ISettingsProvider settings, string salt)
{
    _encryptionProvider = new RijndaelManaged();
    var saltBytes = UTF8Encoding.UTF8.GetBytes(salt);
    var derivedbytes = new Rfc2898DeriveBytes(settings.EncryptionKey, saltBytes, 3);
    _key = derivedbytes.GetBytes(_encryptionProvider.KeySize / 8);
    _iv = derivedbytes.GetBytes(_encryptionProvider.BlockSize / 8);
}

we see that we're storing the key and iv bytes as instance variables. Please note that simply getting the bytes for the salt string using UTF8Encoding.UTF8.GetBytes(salt) is not good enough. The values generated by AntiForgeryToken - while different for each user and session – can be very similar. As such, if you're encrypting a small word (for example "hello") and use the encoding derived bytes, the first few bytes may be so similar that the encrypted output of "hello" might appear the same in the html output for different users / sessions. As such, we're using Rfc2898DerivedBytes to ensure that only the exact salt and encryption key produce the same iv bytes. Another thing to notice is that we're using the encryption key itself as the password passed to the Rfc2898DerivedBytes constructor. This essentially means that the encryption key and the passed in salt string is used to derive the actual iv (salt) used for encryption.

The Html Helper

Add a file called InputExtensions.cs and add the following code to it:


namespace HiddenEncryptDemo.Helpers
{
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using System.Text.RegularExpressions;

    public static class InputExtensions
    {
        public static HeartysoftHtmlHelper Heartysoft(this HtmlHelper helper)
        {
            return new HeartysoftHtmlHelper(helper);
        }
    }

    public partial class HeartysoftHtmlHelper
    {
        private readonly HtmlHelper _helper;
        private readonly ISettingsProvider _settings;

        private static Regex _valueExtractorRegex = new Regex(".*value=\"(.+)\"/*", RegexOptions.Compiled);

        public HeartysoftHtmlHelper(HtmlHelper helper)
            : this(helper, new SettingsProvider())
        {
        }

        public HeartysoftHtmlHelper(HtmlHelper helper, ISettingsProvider settings)
        {
            _helper = helper;
            _settings = settings;
        }

        public string GetAntiForgeryToken(string salt)
        {
            var input = _helper.AntiForgeryToken(salt).ToString();
            var match = _valueExtractorRegex.Match(input);
            return match.Groups[1].Value;
        }

        public MvcHtmlString EncryptedHidden(string name, object value)
        {
            if (value == null)
            {
                value = string.Empty;
            }

            var strValue = value.ToString();
            string salt = GetAntiForgeryToken(_settings.SaltGeneratorKey);

            var encrypter = new RijndaelStringEncrypter(_settings, salt);
            var encryptedValue = encrypter.Encrypt(strValue);
            encrypter.Dispose();

            var encodedValue = _helper.Encode(encryptedValue);
            var newName = string.Concat(_settings.EncryptionPrefix, name);

            return _helper.Hidden(newName, encodedValue);
        }
    }
}

Let's dissect the helper a bit. There are basically two important methods. The first is GetAntiForgeryToken:


public string GetAntiForgeryToken(string salt)
{
    var input = _helper.AntiForgeryToken(salt).ToString();
    var match = _valueExtractorRegex.Match(input);
    return match.Groups[1].Value;
}

This get's the anti forgery token string given a specific salt. As I mentioned before, the AntiForgeryToken helper gets its value from a class internal to ASP.NET MVC and thus we can't get the value directly. To work around this, I'm using the helper to het the html of the anti forgery token hidden input and using a Regex to parse its value. The other interesting method is the EncryptedHidden function:


public MvcHtmlString EncryptedHidden(string name, object value)
{
    if (value == null)
    {
        value = string.Empty;
    }

    var strValue = value.ToString();
    string salt = GetAntiForgeryToken(_settings.SaltGeneratorKey);

    var encrypter = new RijndaelStringEncrypter(_settings, salt);
    var encryptedValue = encrypter.Encrypt(strValue);
    encrypter.Dispose();

    var encodedValue = _helper.Encode(encryptedValue);
    var newName = string.Concat(_settings.EncryptionPrefix, name);

    return _helper.Hidden(newName, encodedValue);
}

This method uses the previous one to get the salt, creates the encrypter and encrypts the input. It then uses the regular Hidden() helper to return the <input> tag with the value set to the encrypted data.

Configuration

Go to web.config and ensure that under <configuration>, the following entries are present:


<appSettings>
    <add key="EncryptionKey" value="asdjahsdkhaksj dkashdkhak sdhkahsdkha kjsdhkasd"/>
</appSettings>

The View

At this point, go to Index.aspx and change the html of the form to:


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
    <%= Html.Heartysoft().EncryptedHidden("computer.Setting", "hello world!") %>
    <%--<%= Html.Hidden("computer.Setting", "hello world!") %>--%>
    <input type="submit" value='submit' />
<% Html.EndForm(); %>

I've just commented out the old helper and used our new helper. If you run the page and view the source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

If you refresh the page and view source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

i.e. the value of the hidden input has not changed. If you open the same page in a different browser or close and open the browser or open the same page in a new window (not tab as sessions are shared across tabs), then you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="IM94hlaZP4HmqOhxw7Ewyg==" />

Notice how the value is different from the previous value. If you kept the previous browser open, then refreshing the page and viewing source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

In other words, each session is getting a unique value that's different from the value got in a different session. As such, we can say that the encryption is using a per session salt which makes brute force attacks that much harder.

Please note that since the salt is per session, the exact values of the hidden inputs will vary greatly from the ones shown here. What's important is that in the same session, the value is the same, but the value differs from session to session.

The Controller Factory

We now need to actually decrypt values when the page is posted back. We do this via a custom ControllerFactory. Our controller factory will look for any encrypted input parameters, decrypt them and add them to RouteData using their original names so that they're available to the model binder. Add a class called DecryptingControllerFactory:


namespace HiddenEncryptDemo.Helpers
{
    using System.Linq;
    using System.Web.Mvc;
    using System.IO; 
   
    public class DecryptingControllerFactory : DefaultControllerFactory
    {
        private ISettingsProvider _settings = new SettingsProvider();

        public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            var parameters = requestContext.HttpContext.Request.Params;
            var encryptedParamKeys = parameters.AllKeys.Where(x => x.StartsWith(_settings.EncryptionPrefix)).ToList(); 

            IEncryptString decrypter = null;

            foreach (var key in encryptedParamKeys)
            {
                if (decrypter == null)
                {
                    decrypter = GetDecrypter(requestContext);
                }

                var oldKey = key.Replace(_settings.EncryptionPrefix, string.Empty);
                var oldValue = decrypter.Decrypt(parameters[key]);
                requestContext.RouteData.Values[oldKey] =  oldValue;
            }

            if (decrypter != null)
            {
                decrypter.Dispose();
            }

            return base.CreateController(requestContext, controllerName);
        }

        private IEncryptString GetDecrypter(System.Web.Routing.RequestContext requestContext)
        {
            var salt = GetCurrentSalt(requestContext);
            var decrypter = new RijndaelStringEncrypter(_settings, salt);
            return decrypter;
        }

        private string GetCurrentSalt(System.Web.Routing.RequestContext requestContext)
        {
            var controllerContext = new ControllerContext();
            controllerContext.RequestContext = requestContext;
            var viewContext = new ViewContext(controllerContext, new WebFormView("dummy"),
                new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null);

            var helper = new HtmlHelper(viewContext, new ViewPage());
            var afToken = helper.Heartysoft().GetAntiForgeryToken(_settings.SaltGeneratorKey);
            return afToken;
        }
    }
}

The controller factory is quite simple. It first checks the request parameters to see if there are any that have a key with a prefix equal to the EncryptionPrefix provided by the settings provider. If there are, we cycle through each and add an entry into RouteData with the key set to the parameter's key minus the prefix and the value set to the decrypted value. We create the decrypter only if needed so that if there aren't any encrypted hidden inputs, we don't waste any resources. Furthermore, if we do need a decrypter, we create an instance and reuse it for all the encrypted parameters for the request. Since the salt is per session, reusing the decrypter for all the encrypted parameters in the request is safe and reduces resource usage. To create the decrypter, we need the salt value that has to be equal to the salt used for encrypting. This is where the very interesting GetCurrentSalt method comes in:


private string GetCurrentSalt(System.Web.Routing.RequestContext requestContext)
{
    var controllerContext = new ControllerContext();
    controllerContext.RequestContext = requestContext;
    var viewContext = new ViewContext(controllerContext, new WebFormView("dummy"),
        new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null);

    var helper = new HtmlHelper(viewContext, new ViewPage());
    var afToken = helper.Heartysoft().GetAntiForgeryToken(_settings.SaltGeneratorKey);
    return afToken;
}

What this method is doing is it's faking a view context and controller context, setting the controller context's request context to that of the current request and using the fake view context and controller context to create a new instance of the HtmlHelper class. Then, it calls our previously coded GetAntiForgeryToken helper to get the value of the salt string. This is a very useful technique of creating an instance of the HtmlHelper class and can be used to create helpers outside of view code. Over at the ASP.NET forums, Brad Wilson expressed concerns that passing in TextWriter.Null may cause problems, but I believe this is not so. TextWriter.Null provides a TextWriter instance that can be written to but not read from. I don't think any Html helpers should be reading from the TextWriter – they should only be writing to it. And even if there are such helpers, AntiForgeryToken() and our GetAntiForgeryToken() do not read from the TextWriter, so this is perfectly safe for our purposes.

The end result of all of this is that if there are encrypted hidden inputs, then they are decrypted and the entries with original names (i.e. wothout the prefix) are added to RouteData. This ensures the original entries are available for ModelBinding purposes. The last change you need to do is open up the global.asax.cs file and ensure the Application_Start looks like this:


protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ControllerBuilder.Current.SetControllerFactory(typeof(DecryptingControllerFactory));
}

Running the Page

We'll need to stop the dev server (if you're using IIS, simply restart it). We need to do this because we made changes to the global.asax.cs page. Right click the dev server's icon in your traybar and click stop:

stop-server

You can now simply hit ctrl + F5 in VS to run the page. Click the button and you should see this:

result

Run the page in another browser and click the button. You should see the same results. Viewing the source, you'll see that while pages processed in the same session always has the same encrypted value, a different session in a different browser produces a different encrypted value. Still, when either value is posted to the server, the decrypting controller factory correctly decrypts the values and we get our original value in RouteData. Neither the encryption key nor the encryption iv is ever passed to the client and thus the process is much more secure than our previous approach.

And that's all there is to enable an unobtrusive, easy to use, secure, reusable and salted hidden inputs in ASP.NET MVC. On top of that, it should perform better since we're using managed Rijndael encryption which does not call out to non-managed resources like TDES does.

Possible Questions

I would recommend you first read the "possible questions" section of the previous article. Other than those:

3. Is the data really secure?
The previous approach was pretty secure. As I mentioned, an attacker would need at least 2^56 attempts. This new approach makes it much much more harder for the attacker to succeed. So, it's even more secure. The previous approach didn't tackle CSRFs but relied on the developer using AntiForgeryToken on the pages and the associated ValidateAntiForgeryToken attribute on the controller action being posted to. This approach handles CSRFs itself.

8. How is the performance?
The performance is pretty good. The controller factory only creates a decrypter when needed and reuses it for decrypting all encrypted parameters. We do, however, need to create an encrypter for each call to Html.Heartyfoft().EncryptedHidden(). A way to negate this would be to use some sort of per request IOC container so that one instance is used for processing a whole request. In my opinion, helpers should not have state, but if you want, implementing that part should be easy.

Source Code

Download

kick it on DotNetKicks.com Shout it

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

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

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

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET MVC | ASP.NET | Security

Powered by BlogEngine.NET 1.6.1.0
Theme by Ashic Mahtab

Need an expert?

Ashic Mahtab
ashic@live.com
(+44) 07879927393

Stats

Featured Ads

 

Donations

I maintain this site and create all content entirely in my own time just to help you guys out. If you find the stuff helpful, or cool or just like what you see, I'd appreciate you chipping in to help out with the hosting costs. It's easy to do so - just click the button below - no amount is too low :)