Amazon.com Widgets WilliaBlog.Net | I dream in code

WilliaBlog.Net

I dream in code

About the author

Robert Williams is an internet application developer for the Salem Web Network.
E-mail me Send mail
Go Daddy Deal of the Week: 30% off your order at GoDaddy.com! Offer expires 11/6/12

Recent comments

Archive

Authors

Tags

Code Project Associate Logo

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.


Mock a database repository using Moq

The concept of unit testing my code is still fairly new to me and was introduced when I started writing applications with the Microsoft MVC Framework in Visual Studio 2008.

Intimidated somewhat by the Moq library's heavy reliance on lambdas, my early tests used full Mock classes that I would write myself, and which implemented the same interface as my real database repositories. I'd only write the code for the methods I needed, all other methods would simply throw a "NotImplementedException". However, I quickly discovered that the problem with this approach is that whenever a new method was added to the interface, my test project would no longer build (since the new method was not implemented in my mock repository) and I would have to manually add a new method that threw another "NotImplementedException". After doing this for the 5th or 6th time I decided to face my fears and get to grips with using the Moq library instead. Here is a simple example, of how you can mock a database repository class using the Moq library.

Let's assume that your database contains a table called Product, and that either you or Linq, or LLBLGen, or something similar has created the following class to represent that table as an object in your class library:

The Product Class

namespace MoqRepositorySample

{

    using System;

 

    public class Product

    {

        public int ProductId { get; set; }

 

        public string Name { get; set; }

 

        public string Description { get; set; }

 

        public double Price { get; set; }

 

        public DateTime DateCreated { get; set; }

 

        public DateTime DateModified { get; set; }

    }

}

 

Your Product Repository class might implement an interface similar to the following, which offers basic database functionality such as retrieving a product by id, by name, fetching all products, and a save method that would handle inserting and updating products.

 

The IProductRepository Interface

 

namespace MoqRepositorySample

{

    using System.Collections.Generic;

 

    public interface IProductRepository

    {

        IList<Product> FindAll();

 

        Product FindByName(string productName);

 

        Product FindById(int productId);

 

        bool Save(Product target);

    }

}

 

The test class that follows demonstrates how to use Moq to set up a mock Products repository based on the interface above. The unit tests shown here focus primarily on testing the mock repository itself, rather than on testing how your application uses the repository, as they would in the real world.

 

Microsoft Unit Test Class

 

namespace TestProject1

{

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

 

    using Moq;

 

    using MoqRepositorySample;

 

    ///<summary>

    /// Summary description for UnitTest1

    ///</summary>

    [TestClass]

    public class UnitTest1

    {

        ///<summary>

        /// Constructor

        ///</summary>

        public UnitTest1()

        {

            // create some mock products to play with

            IList<Product> products = new List<Product>

                {

                    new Product { ProductId = 1, Name = "C# Unleashed", Description = "Short description here", Price = 49.99 },

                    new Product { ProductId = 2, Name = "ASP.Net Unleashed", Description = "Short description here", Price = 59.99 },

                    new Product { ProductId = 3, Name = "Silverlight Unleashed", Description = "Short description here", Price = 29.99 }

                };

 

            // Mock the Products Repository using Moq

            Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();

 

            // Return all the products

            mockProductRepository.Setup(mr => mr.FindAll()).Returns(products);

 

            // return a product by Id

            mockProductRepository.Setup(mr => mr.FindById(It.IsAny<int>())).Returns((int i) => products.Where(x => x.ProductId == i).Single());

 

            // return a product by Name

            mockProductRepository.Setup(mr => mr.FindByName(It.IsAny<string>())).Returns((string s) => products.Where(x => x.Name == s).Single());

 

            // Allows us to test saving a product

            mockProductRepository.Setup(mr => mr.Save(It.IsAny<Product>())).Returns(

                (Product target) =>

                {

                    DateTime now = DateTime.Now;

 

                    if (target.ProductId.Equals(default(int)))

                    {

                        target.DateCreated = now;

                        target.DateModified = now;

                        target.ProductId = products.Count() + 1;

                        products.Add(target);

                    }

                    else

                    {

                        var original = products.Where(q => q.ProductId == target.ProductId).Single();

 

                        if (original == null)

                        {

                            return false;

                        }

 

                        original.Name = target.Name;

                        original.Price = target.Price;

                        original.Description = target.Description;

                        original.DateModified = now;

                    }

 

                    return true;

                });

 

            // Complete the setup of our Mock Product Repository

            this.MockProductsRepository = mockProductRepository.Object;

        }

 

        ///<summary>

        /// Gets or sets the test context which provides

        /// information about and functionality for the current test run.

        ///</summary>

        public TestContext TestContext { get; set; }

 

        ///<summary>

        /// Our Mock Products Repository for use in testing

        ///</summary>

        public readonly IProductRepository MockProductsRepository;

 

        ///<summary>

        /// Can we return a product By Id?

        ///</summary>

        [TestMethod]

        public void CanReturnProductById()

        {

            // Try finding a product by id

            Product testProduct = this.MockProductsRepository.FindById(2);

 

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual("ASP.Net Unleashed", testProduct.Name); // Verify it is the right product

        }

 

        ///<summary>

        /// Can we return a product By Name?

        ///</summary>

        [TestMethod]

        public void CanReturnProductByName()

        {

            // Try finding a product by Name

            Product testProduct = this.MockProductsRepository.FindByName("Silverlight Unleashed");

 

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual(3, testProduct.ProductId); // Verify it is the right product

        }

 

        ///<summary>

        /// Can we return all products?

        ///</summary>

        [TestMethod]

        public void CanReturnAllProducts()

        {

            // Try finding all products

            IList<Product> testProducts = this.MockProductsRepository.FindAll();

 

            Assert.IsNotNull(testProducts); // Test if null

            Assert.AreEqual(3, testProducts.Count); // Verify the correct Number

        }

 

        ///<summary>

        /// Can we insert a new product?

        ///</summary>

        [TestMethod]

        public void CanInsertProduct()

        {

            // Create a new product, not I do not supply an id

            Product newProduct = new Product

                { Name = "Pro C#", Description = "Short description here", Price = 39.99 };

 

            int productCount = this.MockProductsRepository.FindAll().Count;

            Assert.AreEqual(3, productCount); // Verify the expected Number pre-insert

 

            // try saving our new product

            this.MockProductsRepository.Save(newProduct);

 

            // demand a recount

            productCount = this.MockProductsRepository.FindAll().Count;

            Assert.AreEqual(4, productCount); // Verify the expected Number post-insert

 

            // verify that our new product has been saved

            Product testProduct = this.MockProductsRepository.FindByName("Pro C#");

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual(4, testProduct.ProductId); // Verify it has the expected productid

        }

 

        ///<summary>

        /// Can we update a prodict?

        ///</summary>

        [TestMethod]

        public void CanUpdateProduct()

        {

            // Find a product by id

            Product testProduct = this.MockProductsRepository.FindById(1);

 

            // Change one of its properties

            testProduct.Name = "C# 3.5 Unleashed";

 

            // Save our changes.

            this.MockProductsRepository.Save(testProduct);

 

            // Verify the change

            Assert.AreEqual("C# 3.5 Unleashed", this.MockProductsRepository.FindById(1).Name);

        }

    }

}

 

Download the Sample project and run the tests yourself:

MoqRepositorySample.zip (691.96 kb)


Categories: ASP.Net | C# | CodeProject | Moq | MVC | Unit Testing
Posted by Williarob on Tuesday, December 15, 2009 8:17 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Start Building with the Microsoft SDK for Facebook Platform

The Microsoft SDK for Facebook Platform contains rich social features and offers something for almost any kind of Facebook developer who is building with Microsoft technology, whether you're implementing Facebook Connect or are building a Web-based or desktop application. If you're one of the six million Microsoft software developers just starting to build for Facebook, you can use this SDK to make your applications more social, letting your users share and connect with their friends.

Learn more.


Posted by Williarob on Tuesday, November 24, 2009 6:45 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Visio files not opening in Internet Explorer

Recently, a colleague sent me a Visio file and informed me that I should be able to view it in IE. So I double clicked on it and sure enough, IE opened and warned me that "To Help protect your security, Internet Explorer has restricted this webpage from running scripts or ActiveX controls that could access your computer. Click here for options." so I did and I clicked on "Allow blocked content".

Instead of seeing the document, I was presented with a small red x, like that of a broken image. Undaunted, I thought that perhaps there is a Visio Viewer, rather like the Word and Powerpoint Viewers you can download, that would let me view Visio files, but not create them. Sure enough I found the Visio 2007 Viewer on Microsoft's site, downloaded it and installed it.

.vsd files were still associated with IE, and the results were the same. So I tracked down the Visio Viewer application in C:\Program Files (x86)\Microsoft Office\Office12; and double clicked it. "This Program can only run from within another program." Great. So, I checked the security settings in IE, and went to Manage Add-ons to see if the Visio Viewer was disabled or blocked, but it didn't even appear in the list.

With a little help from Google, I discovered that the solution was to uninstall a security update "Cumulative Security Update for ActiveX Killbits for Windows 'Your Version of Windows Here' (KB973525)"

The link to the update is here:  http://go.microsoft.com/fwlink/?LinkId=158202.  It appears that Microsoft in it's infinite wisdom accidentally put the Visio Viewer's CLSID in the kill list. Uninstalling this update, resolved the issue and I can now open Visio documents in IE.


Categories: Office
Posted by Williarob on Thursday, November 19, 2009 1:22 PM
Permalink | Comments (5) | Post RSSRSS comment feed

How to get the length (duration) of a media File in C# on Windows 7

If you have ever looked at a media file (audio or video) in the explorer window on a Windows 7 PC, you may have noticed that it displays additional information about that media file that previous versions of Windows didn't seem to have access to, for example the length/duration of a Quicktime Movie Clip:

 

Even right clicking the file and choosing Properties > Details does not give me this information on my Vista Ultimate PC. Of course, now that Windows has the ability to fetch this information, so do we as developers, through the Windows API (The DLL to Import by the way is "propsys.dll"):

        internal enum PROPDESC_RELATIVEDESCRIPTION_TYPE

        {

            PDRDT_GENERAL,

            PDRDT_DATE,

            PDRDT_SIZE,

            PDRDT_COUNT,

            PDRDT_REVISION,

            PDRDT_LENGTH,

            PDRDT_DURATION,

            PDRDT_SPEED,

            PDRDT_RATE,

            PDRDT_RATING,

            PDRDT_PRIORITY

        }

 

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern int PSGetNameFromPropertyKey(

            ref PropertyKey propkey,

            [Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszCanonicalName

        );

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern HRESULT PSGetPropertyDescription(

            ref PropertyKey propkey,

            ref Guid riid,

            [Out, MarshalAs(UnmanagedType.Interface)] out IPropertyDescription ppv

        );

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern int PSGetPropertyKeyFromName(

            [In, MarshalAs(UnmanagedType.LPWStr)] string pszCanonicalName,

            out PropertyKey propkey

        );

However, before you rush off to play with these, you may be interested to know that Microsoft has created a great Library that showcases this and many of the other new API features of Windows 7. It's called the WindowsAPICodePack and you can get it here.

If you open the WindowsAPICodePack Solution and compile the Shell Project, it creates a nice wrapper around all the neat new system properties available through propsys.dll. Adding a reference to WindowsAPICodePack.dll and WindowsAPICodePack.Shell.dll in a console application will allow you to get the duration of just about any media file that Windows recognizes. (Of course the more codec packs you install, the more types it will recognize, I recommend The Combined Community Codec Pack to maximize your range of playable files.)

Here is a simple example showing how to get the duration of a media file in C# using this library:

namespace ConsoleApplication1

{

    using System;

 

    using Microsoft.WindowsAPICodePack.Shell;

 

    class Program

    {

        static void Main(string[] args)

        {

            if(args.Length < 1)

            {

                Console.WriteLine("Usage: ConsoleApplication1.exe [Filename to test]");

                return;

            }

 

            string file = args[0];

            ShellFile so = ShellFile.FromFilePath(file);

            double nanoseconds;

            double.TryParse(so.Properties.System.Media.Duration.Value.ToString(), out nanoseconds);

            Console.WriteLine("NanaoSeconds: {0}", nanoseconds);

            if (nanoseconds > 0)

            {

                double seconds = Convert100NanosecondsToMilliseconds(nanoseconds) / 1000;

                Console.WriteLine(seconds.ToString());

            }

        }

 

        public static double Convert100NanosecondsToMilliseconds(double nanoseconds)

        {

            // One million nanoseconds in 1 millisecond, but we are passing in 100ns units...

            return nanoseconds * 0.0001;

        }

    }

}

As you can see, the System.Media.Duration Property returns a value in 100ns units so some simple math will turn it into seconds. Download the Test Project which includes the prebuilt WindowsAPICodePack.dll and WindowsAPICodePack.Shell.dll files in the bin folder:

ConsoleApplication1.zip (218.76 kb)

For the curious, I tested this on Windows XP and as you'd expect, it didn't work:

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'propsys.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

On Vista Ultimate SP2, it still didn't work - nanoseconds was always 0, though it didn't throw any exceptions.

For the older systems I guess we are limited to using the old MCI (Media Control Interface) API:

        using System.Runtime.InteropServices;

 

        [DllImport("winmm.dll")]

        public static extern int mciSendString(string lpstrCommand, StringBuilder lpstrReturnString, int uReturnLength, int hwndCallback);

 

        [DllImport("winmm.dll")]

        private static extern int mciGetErrorString(int l1, StringBuilder s1, int l2);

 

        private void FindLength(string file)

        {

            string cmd = "open " + file + " alias voice1";

            StringBuilder mssg = new StringBuilder(255);

            int h = mciSendString(cmd, null, 0, 0);

            int i = mciSendString("set voice1 time format ms", null, 0, 0);

            int j = mciSendString("status voice1 length", mssg, mssg.Capacity, 0);

            Console.WriteLine(mssg.ToString());

        }

Which works fine for .mp3 and .avi and other formats that play natively in Windows Media Player, but even with a codec pack installed, it doesn't work on Quicktime or .mp4 files, where the new Windows 7 API did.


Categories: C# | CodeProject | Windows | Windows 7
Posted by Williarob on Wednesday, October 21, 2009 12:14 PM
Permalink | Comments (0) | Post RSSRSS comment feed

New and Improved Microsoft AntiXss 3.1.

Until now, the preferred way to selectively allow only certain HTML tags like <b> and <i> was to regex the input to ensure it contained only valid Unicode letter and number characters and those specified tags, something like this:

if (!Regex.IsMatch(input, @"^([\p{L}\p{N}'\s]|<b>|</b>|<i>|</i>){1,40}$")) throw new Exception();

This approach will prevent all unwanted tags, but it will also prevent all attributes on the allowed tags. Sometimes this is good – attackers can add malicious script to onmouseover attributes of <b> and <i> tags – but again, sometimes this is overkill and blocks the use of benign attributes like lang or title. It would be theoretically possible to extend the regular expression to allow these attributes, as well as other safe HTML tags and their attributes, but realistically that would be an incredibly difficult regex both to develop and maintain.

AntiXss 3.1 takes care of all of this logic for you, using the same whitelist approach: it filters the input using a list of known good tags and attributes and strips out all other text. Simply pass the untrusted input through the AntiXss.GetSafeHtml or GetSafeHtmlFragment method to sanitize it:

string output = AntiXss.GetSafeHtml(input);

I strongly encourage everyone to download the new AntiXss 3.1 and incorporate it into your applications starting today. It’s a very effective defense, especially when used in conjunction with the output encoding functionality that’s been a part of AntiXss from the beginning.

Read the Full Article Here.

Download AntiXss 3.1 from Microsoft.


Posted by Williarob on Tuesday, September 29, 2009 1:50 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Shepherds Pie Recipe

Ingredients

1 tsp1 Worcestershire Sauce

1/4 tsp thyme

1/8 tsp pepper

1/4 tsp salt

1/8 tsp Paprika

2 tbsp2 Ketchup

1 tsp Parsley

1 Medium Onion

1 lb3 Ground Lamb (If you use Beef you're making Cottage Pie, not Shepherds Pie!)

2 Large Carrots

1 24oz4 Package Refrigerated Garlic Mashed Potatoes5

1 packet Gravy mix6

1 Cup7 Water

Directions

Peel & Chop the onion, then combine it and the ground Lamb in a large skillet over a medium heat. Meanwhile, peel and chop the carrots, then add them to the skillet and cover it, but don't forget to stir it occasionally while you work on the sauce.

In a small saucepan, add the gravy mix, Worcestershire Sauce, thyme, salt, pepper, paprika, ketchup, parsley and water. Mix it all up then put it on a high heat and keep stirring it until it boils (if you stop stirring it may get lumpy). When it boils, remove it from the heat. Assuming the meat is brown at this point, drain excess juices from the skillet and pour in the sauce. Cover the skillet and let it simmer over a low heat while you heat the potatoes.

Pour the contents of the skillet into a casserole or baking dish and add the potatoes evenly across the top, fluffing with a fork. I usually place the baking dish on top of a foil covered baking sheet to catch any drips, but that's because my dish is always packed to the very top with juicy goodness and it tends to make a small mess in the oven. Broil (Grill for those in Britain) until the potatoes are golden brown on top and a little crispy (about 5 mins). Serves 4.

Notes

  1. tsp = Teaspoon
  2. tbsp = Tablespoon
  3. 1 Pound (lb) = 0.45 kilograms (453.59 grams)
  4. 24 ounces is equal to 0.68 kilograms (680.39 grams)
  5. I use Bob Evans Garlic Mashed potatoes.
  6. An oxo/bovril cube gravy would be better, but very hard to come by in the US, so I use McCormick Brown Gravy mix which is a pretty good substitute.
  7. 1 cup is equal to 236.59 milliliters (ml)

Categories: recipes
Posted by Williarob on Monday, September 21, 2009 11:00 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Adobe Flex 3: Multiline text moves up by one line when selected

In Adobe Flex, <mx:Text> controls are selectable. When compiled into flash .swf files, you will be able to highlight that text using the mouse when it appears in your browser. However, we found that when you highlight the text in a dynamic multiline text control that text actually moves/scrolls up a line which in our case caused the top line of text to disappear. One solution is to set the selectable property to false, but this is not ideal.

Steps to reproduce:
1. Create a dynamic Text field in AS and assign a multi line string to htmlText and set selectable=true (alternatively use <mx:Text>".
2. Select any portion of the text with the mouse in a top to bottom motion.
3. The text will move up exactly one line making the first line of the text disappear.  

The problem seems to be that textField.setActualSize doesn't consider the leading property when calculating the size. This bug is documented here, and the solution is in the comments but unless you have edited the source of the Felx sdk before, you might not know how to take full advantage of it, so I thought I'd document the steps I took to correct the problem.

  1. First locate the file Text.as, which using the default install settings should be found here: C:\Program Files\Adobe\Flex Builder 3\sdks\3.0.0\frameworks\projects\framework\src\mx\controls
  2. Comment out line 342 which should look like this:

    textField.setActualSize(unscaledWidth - paddingLeft - paddingRight, unscaledHeight - paddingTop - paddingBottom);
  3. Insert the following code beneath it:

    var leading:Number = getStyle("leading");
    textField.setActualSize(unscaledWidth - paddingLeft - paddingRight, unscaledHeight - paddingTop - paddingBottom + leading);
  4. Go back to Adobe Flex and right click on your project and choose "Properties".
  5. Click on "Flex Build Path".
  6. Click on "Add Folder".
  7. Paste/type or browse to C:\Program Files\Adobe\Flex Builder 3\sdks\3.0.0\frameworks\projects\framework\src and click "OK".
  8. Click "OK" again to exit the properties dialog.
  9. Run your project again and hightlight the text. It should not move.

 

 


Categories: Flash | Flex
Posted by Williarob on Thursday, July 23, 2009 2:00 PM
Permalink | Comments (2) | Post RSSRSS comment feed

Working with Buttons inside a GridView Control

This post is as much for my benefit as anyone elses. Every few months I find myself scratching my head, wondering how I can find all the pieces of information I need inside the handler for a button click on a grid view.

Since I just had to do this again yesterday, this time, I'm going to record my findings here, so that next time I can come right to this page.

So here is the scenario: there is a GridView control on a page, bound to data in a database. It has multiple columns of read only data, but the last column contains a textbox and a button. When that button is pushed, I need to get the value of the text box, the orderid and the primary key of the current row in order to update a record in the database. I have found at least three ways to do this without getting too fancy.

      <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" DataSourceID="SqlDataSource1">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False" ReadOnly="True" SortExpression="ID" />
<asp:BoundField DataField="NAME" HeaderText="NAME" SortExpression="NAME" />
<asp:BoundField DataField="ORDERID" HeaderText="ORDERID" SortExpression="ORDERID" />
<asp:BoundField DataField="CITY" HeaderText="CITY" SortExpression="CITY" />
<asp:TemplateField>
<ItemTemplate>
<asp:TextBox" ID="TextBox1" runat="server" Text='<%# Eval("PHONE") %>'
<asp:LinkButton ID="LinkButton1" runat="server" OnClick="LinkButton1_Click">Get Row Info</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

The first way I'm going to explore may be considered rather "ghetto" in the sense that while it works beautifully it does bend some rules so anyone who tries to adhere to common patterns and practices would probably frown upon it. However, as you'll see, it does have a number of advantages over some of the more official techniques so don't rule it out right away! To use this ghetto code, add a RowDataBound event to your gridview and use it to find each instance of your button control and add some new attributes to it containing the information you'll need for this row:

protected void gv_RowDataBound(object sender, System.Web.UI.WebControls.GridViewRowEventArgs e)

    {

        GridViewRow gvr = (GridViewRow)e.Row;

 

        if (e.Row.RowType == DataControlRowType.DataRow)

        {

            // Find your controls

            LinkButton LinkButton1 = (LinkButton)gvr.FindControl("LinkButton1");

 

            // this is ghetto, but you can, add orderid and other data to attributes so we can grab them later           

            int orderId = Convert.ToInt32(DataBinder.Eval(e.Row.DataItem, "OrderID"));

 

            LinkButton1.Attributes.Add("OrderID", orderId);          

        } // end if this is a real row

    }

 

Obviously, if you choose to do this your linkbutton will be rendered with an OrderID attribute <a href="..." OrderID="123" ... /> which you can grab later when posting back. For example, if you add OnClick="LinkButton_Click" to your linkbutton inside the gridview, then inside LinkButton_Click you can get the orderID like so:

 

    protected void LinkButton1_Click(object sender, EventArgs e)

    {

        LinkButton lb = (LinkButton)sender;

        int orderID = int.Parse(lb.Attributes["OrderID"]);

        // ...

    }

 

It's quick, it works and while it doesn't seem like the "Microsoft approved" method to me, it is more scalable than the counting the cells (If your OrderID is rendered as a label in the third column, you might use Convert.ToInt32(row.Cells(2).Text); to obtain the orderid). But what happens when you or someone else adds an additional column to the gridview in front of orderid? Suddenly your orderid might be something else entirely and if it still compiles and runs perhaps no one will even notice!


Let's explore two more quick examples that adhere to what Microsoft had in mind. The first, again assumes you add OnClick="LinkButton_Click" to your linkbutton inside the gridview and that you need not only the orderid, but also the Primary Key and the value of TextBox1:

 

    protected void LinkButton1_Click(object sender, EventArgs e)

    {

        LinkButton lb = (LinkButton)sender;

        GridViewRow row = (GridViewRow)lb.NamingContainer;

 

        // get the value of the textbox

        TextBox txt1 = row.Cells[4].FindControl("TextBox1") as TextBox;

        string phoneNumber = txt1.Text;

 

        // get the Primary Key Value

        int ID = GridView1.DataKeys[row.RowIndex].Value;

 

        // ... Do something with these values like update a row in a database

    }

 

It is unfortunate that you cannot address a cell by anything other than its Index as if you later need to add another column in front of this one, your application will break. You might think this is no big deal, but consider what happens if you have this sort of code applied to two or three columns, and each one addresses multiple cells.

Then your boss asks you to add a new column. Trust me, you will groan, as you now have to recalculate the index of all cells addressed in this fashion. It can turn a 2 minute task into a 20 minute task.


As an alternative to the OnClick Event applied to an individual button, you can instead use the RowCommand Event of the GridView. This allows you to store some data inside the ComandArgument property of your button, which is arguably similar to the custom attribute method discussed at the beginning of this article, but since you can only set one command argument, if you need three pieces of information from it you are faced with a choice of either combining the information through concatenation - setting your command argument up as a string you plan to split apart later e.g. "OrderID=123|ProductID=456" or you are back to counting cells. And what's more, despite what this MSDN article implies, the CommandArgument does NOT contain the RowIndex by default, you would need to add it there yourself which can be done declaratively like so:

<asp:LinkButton ID="LinkButton1" runat="server"CommandArgument='<%# gv.Rows.Count.ToString() %>' CommandName="UpdatePhone">Get Row Info</asp:LinkButton>

Then your RowCommand Handler would look something like this:

 

       protected void GridView1_RowCommand(Object sender, GridViewCommandEventArgs e)

       {

            if (e.CommandName == "UpdatePhone")

            {

 

 

                // Convert the row index stored in the CommandArgument

                // property to an Integer.

                int index = Convert.ToInt32(e.CommandArgument);

 

 

                // Retrieve the row that contains the button clicked

                // by the user from the Rows collection.

                GridViewRow row = GridView1.Rows[index];

 

                // get the value of the textbox

                TextBox txt1 = row.Cells[4].FindControl("TextBox1") as TextBox;

                string phoneNumber = txt1.Text;

 

                // get the Primary Key Value

                int ID = GridView1.DataKeys[row.RowIndex].Value;

 

                // ... Do something with these values like update a row in a database

            }

        }

 

Conclusions


So there you have it. Three ways to Handle the onClick event of a button in a grid view. In my opinion, the OnRowCommand Event of the GridView is the least useful of them when trying to fulfil this brief. Sure it gives you the CommandArgument property, but you immediately need to use it for storing the row index. Whenever possible, I usually choose a Microsft approved approach so this time I opted for the OnClick event of the button but if I have to come back and add another column again, I'll probably use the custom attributes approach, because I do not want to have to increment/decrement all the cell index values again.


Categories: ASP.Net | C#
Posted by Williarob on Thursday, June 18, 2009 6:19 AM
Permalink | Comments (1) | Post RSSRSS comment feed

Asynchronous Programming with ASP.Net MVC Futures

Using the AsyncController

Introduction

The AsyncController is an experimental class offered inside the latest MVC Futures dll to allow developers to write asynchronous action methods.  The usage scenario for this is for action methods that have to make long-running requests, such as going out over the network or to a database, and don’t want to block the web server from performing useful work while the request is ongoing.

In general, the pattern is that the web server schedules Thread A to handle some incoming request, and Thread A is responsible for everything up to launching the action method, then Thread A goes back to the available pool to service another request.  When the asynchronous operation has completed, the web server retrieves a Thread B (which might be the same as Thread A) from the thread pool to process the remainder of the request, including rendering the response.  The diagram below illustrates this point.

Configuration

The asynchronous types are declared in the Microsoft.Web.Mvc namespace in the assembly Microsoft.Web.Mvc.dll.  The first change a developer needs to make is to declare a route as asynchronous.  There are MapAsyncRoute() extension methods to assist with this; these are analogs of the normal MapRoute() extension methods already provided by the MVC framework.  In Global.asax:

routes.MapAsyncRoute(

    "Default",

    "{controller}/{action}/{id}",

    new { controller = "Home", action = "Index", id = "" }

);

A route declared with MapAsyncRoute() can correctly handle both synchronous and asynchronous controllers, so there is no need to create complex routing logic such that sync controllers are serviced by the normal MapRoute() handler and async controllers are serviced by the MapAsyncRoute() handler.  However, a sync route [MapRoute()] cannot in general correctly execute async controllers and methods.

Secondly, if you are using IIS6 or IIS7 classic mode, you need to replace the standard *.mvc handler with its asynchronous counterpart.  Replace these lines in Global.asax (they don’t necessarily appear adjacent to one another):

<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

With these:

<add verb="*" path="*.mvc" validate="false" type="Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc"/>

<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="Microsoft.Web.Mvc.MvcHttpAsyncHandler, Microsoft.Web.Mvc"/>

Writing asynchronous action methods

In general, any asynchronous controllers that you author should subclass the AsyncController type.  Remember to import the Microsoft.Web.Mvc namespace.

public class MyAsyncController : AsyncController {

    // ...

}

The default constructor of the MyAsyncController sets the ActionInvoker property to an instance of the AsyncControllerActionInvoker.  This specialized invoker can understand several asynchronous patterns.  You can mix and match these patterns inside of your controllers.  If an ambiguity is found, we do our best to throw a detailed exception.

The AsyncController understands three async patterns.  These patterns can be mixed and matched within a single AsyncController.  Additionally, an AsyncController can contain normal (synchronous) action methods.  This makes it easy to change the base class of your controller from Controller to AsyncController in order to add async methods while allowing your original sync methods to run correctly.

The IAsyncResult pattern

The IAsyncResult pattern is a well-known pattern in the .NET Framework and is documented heavily.  A method pair signature which implements the IAsyncResult pattern is shown below.

public IAsyncResult BeginFoo(int id, AsyncCallback callback, object state);

public ActionResult EndFoo(IAsyncResult asyncResult);

This is the asynchronous analog of a synchronous method with the signature public ActionResult Foo(int id).  Note that the BeginFoo() method takes the same parameters as the Foo() method plus two extra – an AsyncCallback and a state object – and returns an IAsyncResult.  The EndFoo() method takes a single parameter – an IAsyncResult – and has the same return type as the Foo() method.

Standard model binding takes place for the normal parameters of the BeginFoo() method.  The invoker will pass a callback and state object for the BeginFoo() method to consume when its asynchronous task has finished.  When the callback is called, we automatically invoke the EndFoo() method and capture the result, then execute the result just as we would have in a synchronous request.

Only filter attributes placed on the BeginFoo() method are honored.  If a filter attribute is placed on EndFoo(), it will be ignored.  If an [ActionName] attribute is placed on the BeginFoo() method in order to alias it, we will look for an EndFoo() method based on the method name of BeginFoo(), not the aliased action name.  For example:

[ActionName("Bar")]

public IAsyncResult BeginFoo(int id, AsyncCallback callback, object state);

public ActionResult EndFoo(IAsyncResult asyncResult);

This will cause the BeginFoo() method to match requests for Bar rather than Foo.  Note that the completion method is still called EndFoo() instead of EndBar() since it matches the name of the entry method, not the entry alias.

The event pattern

In this pattern, the action method is divided into a setup method and a completion method.  The signatures are below:

public void Foo(int id);

public ActionResult FooCompleted(...);

When a request comes for Foo, we execute the Foo() method.  When the asynchronous operations are completed, we invoke the FooCompleted() method and execute the returned ActionResult.

The invoker will model bind parameters to the Foo() method in the standard way.  Parameters to FooCompleted() are not provided using model binders.  Rather, they come from the AsyncController.AsyncManager.Parameters dictionary, which can be populated as part of the asynchronous setup.  Keys in the dictionary correspond to parameter names of the FooCompleted() method, and any parameters which do not have corresponding keys in the dictionary are given a value of default(T).

The invoker must keep track of the number of outstanding asynchronous operations so that it does not invoke the FooCompleted() method prematurely.  To do this, a counter has been provided, accessible from AsyncController.AsyncManager.OutstandingOperations.  This counter can be incremented or decremented to signal that an operation has kicked off or concluded, and when the counter hits zero the invoker will invoke the completion routine.  For example:

public void Foo(int id) {

    AsyncManager.Parameters["p"] = new Person();

    AsyncManager.OutstandingOperations.Increment();

    ThreadPool.QueueUserWorkItem(o => {

        Thread.Sleep(2000); // simulate some work

        AsyncManager.OutstandingOperations.Decrement();

    }, null);

}

public ActionResult FooCompleted(Person p) {

    // consume 'p'

}

There is also an AsyncController.AsyncManager.RegisterTask() method that is helpful for wrapping calls to IAsyncResult pattern methods from within an event pattern method.  The RegisterTask() method also handles incrementing and decrementing the counter correctly.  For example:

public void Foo(int id) {

    AsyncManager.RegisterTask(

        callback => BeginGetPersonById(id, callback, null),

        ar => {

            AsyncManager.Parameters["p"] = EndGetPersonById(ar);

        });

    AsyncManager.RegisterTask(

        callback => BeginGetTotalUsersOnline(callback, null),

        ar => {

            AsyncManager.Parameters["numOnline"] = EndGetTotalUsersOnline(ar);

        });

}

public ActionResult FooCompleted(Person p, int numOnline) {

    // ...

}

This will kick off two asynchronous tasks and wait for both to finish before executing the FooCompleted() method with the values that were returned.

Only filter attributes placed on the Foo() method are honored.  If a filter attribute is placed on FooCompleted(), it will be ignored.  If an [ActionName] attribute is placed on the Foo() method in order to alias it, we will look for an FooCompleted() method based on the method name of Foo(), not the aliased action name.  For example:

[ActionName("Bar")]

public void Foo(int id);

public ActionResult FooCompleted(...);

This will cause the Foo() method to match requests for Bar rather than Foo.  Note that the completion method is still called FooCompleted() instead of BarCompleted() since it matches the name of the entry method, not the entry alias.

The Foo() method is allowed to return anything.  The invoker ignores the return value of this method; it only cares about the return value of the FooCompleted() method.  This is to allow writing Foo() methods that are easier to unit test.

The delegate pattern

This pattern is very similar to the event pattern, except that the method Foo() returns a parameterless delegate type and there is no FooCompleted() method.  For example:

public Func<ActionResult> Foo(int id) {

    Person p = null;

    int numOnline = 0;

    AsyncManager.RegisterTask(

        callback => BeginGetPersonById(id, callback, null),

        ar => {

            p = EndGetPersonById(ar);

        });

    AsyncManager.RegisterTask(

        callback => BeginGetTotalUsersOnline(callback, null),

        ar => {

            numOnline = EndGetTotalUsersOnline(ar);

        });

    return () => {

        ViewData["p"] = p;

        ViewData["numOnline"] = numOnline;

        return View();

    };

}

Or, more succinctly:

public Func<ActionResult> Foo(int id) {

    AsyncManager.RegisterTask(

        callback => BeginGetPersonById(id, callback, null),

        ar => {

            ViewData["p"] = EndGetPersonById(ar);

        });

    AsyncManager.RegisterTask(

        callback => BeginGetTotalUsersOnline(callback, null),

        ar => {

            ViewData["numOnline"] = EndGetTotalUsersOnline(ar);

        });

    return View;

}

Since this pattern supports only parameterless delegates instead of parameterful delegates, the earlier discussion about AsyncController.AsyncManager.Parameters is not applicable.

Timeouts

There is a Timeout property accessible from AsyncController.AsyncManager.Timeout that specifies the number of milliseconds to wait for a response from the action method before canceling the request.  The default value is 30000 (equal to 30 seconds).  If the action method has not returned by the specified time, we throw a TimeoutException.  Action filters and exception filters may handle this particular exception type if they wish.  Setting the Timeout property to System.Threading.Timeout.Infinite signifies that we will never throw this exception.

The timeout duration can be specified on a per-controller or per-action basis by attributing a class or method with [AsyncTimeout] or [NoAsyncTimeout].

Known issues

-          Asynchronous actions generally cannot be called by synchronous invokers or handlers.  If you receive an exception message about an action being unable to be executed synchronously, ensure that you’re using the MapAsyncRoute() extension method in your Global.asax and that your controller subclasses AsyncController.

-          The asynchronous invoker will not match any method beginning with Begin or End or ending with Completed.  This is to prevent web calls to the BeginFoo(), EndFoo(), and FooCompleted() methods directly.  If you need to make an action with this name accessible to web users, use the [ActionName] attribute to alias the method:

[ActionName("Begin")]

public ActionResult DoSomething();

The above is an example of a normal synchronous method that has been renamed to Begin to work around the invoker’s blocking of this name.

-          If the route that normally handles requests for the application root (/) is an asynchronous route, the Default.aspx file should be removed from the web application.  The Default.aspx file included in the template only works with synchronous requests.


Categories: ASP.Net | Asynchronous | C# | MVC
Posted by Williarob on Thursday, June 18, 2009 6:03 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Sample Scrolling Silverlight Video Playlist 2.0

Since the release of Silverlight 2, there have been a number of requests for me to update my Scrolling Silverlight Video Playlist Sample and I have finally made the time to do just that. I feel it is important to point out that Microsoft Expression Encoder 2 Service Pack 1 is now out and it ships with a handful of new Player templates just for Silverlight 2 including two with a built in Scrolling playlist feature (Frosted Gallery and Silverlight 2 Gallery). 

Still, partly because not everyone has Expression Encoder 2 and mostly because I felt it would be educational to do so, I went ahead and rebuilt the Scrolling Silverlight Video Playlist sample from the ground up for Silverlight 2 in C# and here it is:

This time, instead of using a player from the growing Expression Encoder template library, I went with the opensource Blacklight player which is a nice lightweight player. And rather than add my scroll widget directly to the player as I did with version 1.0, I chose to keep it seperate, and created a 'ScrollWidget' User Control that could be used in other projects (for example as a thumbnail scroller for photos, or as a toolbar) with only minor modification.

As always, the complete source code is available:

ScrollingPlaylist2.zip (1.22 mb)

[ Special thanks to Sean at FlawlessCode.com for developing this Silverlight Extension for BlogEngine.NET ]


Categories: C# | Silverlight
Posted by Williarob on Thursday, April 30, 2009 8:47 AM
Permalink | Comments (5) | Post RSSRSS comment feed