mostlylucid

scott galloway's personal blog...
posts - 906, comments - 722, trackbacks - 11

My Links

News

Archives

Post Categories

Misc. Coding

Monday, February 15, 2010

AutoMapper question…any ideas…

In the little app I’m working on I’m using MongoDB…which is great, however MongoDB only supports storing Document objects. Which are kinda like Dictonaries (Document[“blah”] = “bar”;). I want to use AutoMapper to map these loosely types Key-Value Pairs to my objects and I’m basing it on some code I found drifting around:

This works:

 

 public static IMappingExpression<Document, TDestination> ConvertFromDocument<TDestination>(this IMappingExpression<Document, TDestination> exp, Func<string, string> propertyNameMapper)
        {
            foreach (PropertyInfo pi in typeof(TDestination).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                if (!pi.CanWrite ||
                pi.GetCustomAttributes(typeof(TDestination), false).Length == 0)
                {
                    continue;
                }

                string propertyName = pi.Name;
                propertyName = propertyNameMapper(propertyName);
                exp.ForMember(propertyName, cfg => cfg.MapFrom(r=> r[propertyName]));
            }
            return exp;
        }

This maps my document to an object…cool…however, I’m having real problems figuring out the reverse (my lambda / knowledge of AutoMapper are letting me down):

This is NO way works…

  public static IMappingExpression<TSource, Document> ConvertToDocument<TSource>(this IMappingExpression<TSource, Document> exp, Func<string, string> propertyNameMapper)
        {  

            if (typeof(TSource) is IBaseObject)
            {

               foreach(PropertyInfo pi in typeof(TSource).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
               {
                     
                   string propertyName =pi.Name;
                   propertyName = propertyNameMapper(propertyName);
                         exp.ForMember(r=>r[propertyName], cfg=>cfg.MapFrom(p=>pi.GetValue(p));

               }
            }
            else
            {
                throw new Exception("What type are you trying to give me???");
            }
            return exp;
        }

 

Help my Jimmy Bogard, you’re my only hope :)

posted @ Monday, February 15, 2010 4:13 PM | Feedback (1) |

Saturday, February 13, 2010

WPF: Byte Array to Bitmap Value Converter

I wrote this for use in a little project but as I’m no longer using it, I though I’d stick it on here for anyone who wants it. Essentially, this allows you to take a byte Array (in my case, in the File property of my ThumbnailViewModel) and get it back as a BitmapImage for use in DataBinding…

It also has the property of accepting a parameter which lets you specify the size of thumbnail to use (ThumbSize). Here, I actually have multiple ThumbNails (in the List<ThumbNailViewModel>) and select one using the parameter.

Given this resource string and XAML,

<UserControl.Resources>
    <local:BytesToBitmapConverter x:Key="bytesToBitmapConverter"/>
</UserControl.Resources>
<Image Name="IconImage" Source="{Binding Path=ThumbNails, Converter={StaticResource bytesToBitmapConverter}, ConverterParameter=Small}"/>

 

 
    public class BytesToBitmapConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {


            var thumbs = value as List<ThumbnailViewModel>;
            var param = parameter as String;
            var thisThumb = thumbs.Find(x => x.ThumbnailSize == (ThumbSize)Enum.Parse(typeof(ThumbSize),param));

            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.CacheOption = BitmapCacheOption.OnLoad;
            bi.StreamSource = new MemoryStream(thisThumb.File);
            bi.EndInit();
            return bi;

        }



        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

posted @ Saturday, February 13, 2010 3:10 PM | Feedback (0) | Filed Under [ Code Snippets XAML WPF ]

Wednesday, January 20, 2010

WPF for the Web guy…a story of pain, despair and learning

Well, OK I’m over dramatizing it a tad :) As you may have noticed from my last post I’ve started working of some stuff which is outside my comfort zone. I recently joined a company called cozwecan,working for a chap named @RobertTheGrey, the first project we’re working on is a photo sales site…you know kind of like Getty Images / Smugmug / Flickr except for direct photographer-customer sales. As the only full-time developer (so far!)  I’ve been tasked with building the vast majority of the applications (be they web, desktop, etc…) which we need for the business to work.

Obviously one of the most critical bits for an e-commerce site is stuff to sell! In our case this is a stock of really high quality photographs taken by professional photographers around the world. First question; how do we get these images on the site, and how do we let the photographers add information to their photographs to let them be found by folks who want to buy them? Well, that’s the first problem I’ve been asked to solve!

When working on the requirements, it quickly became clear that this had to be a desktop app, it needed to allow operations on multi-megabyte photographs, needed to allow ‘tagging’ offline. Especially during initial loading, there’s 10-100s of thousands of images getting worked on…forcing this to be online adds a nasty lag and makes the task a pain in the butt to complete. Above all, it needed to be reliable!
Now, in my dev head this led to a number of decisions about the features the app would need to support:

1. Intuitive, responsive UI; the users are not necessarily computer-savvy, need to use obvious UI metaphors and make it fast enough to be pleasant to use.
2. Needs to do some offline image processing; we’re planning on hosting this on the cloud….CPU time costs cash :)
3. Should work offline
4. Needs to be built around reliable upload of multi-meg files (across potentially tiny connection bandwidths).

So, these are the basic needs…technically this leads me to some technology choices:

1. Multi-threading / concurrency (as with any real desktop app) is key…as is a decent Desktop technology…
2. See above, we need to ‘watch’ for new files arriving, make thumbnails transparently etc…
3. So, it needs some sort of persistence…along with other non-core app requirements (e.g. tagging) we’ll likely need a little, lightweight DB.
4. Needs ‘block’ based uploads with retry / verification of uploaded items…

Luckily I’ve been a dev for quite a while and had at least some idea where to start…the biggest challenge would be learning!
Firstly, the obvious desktop platform de jour is WPF…this is in essence the current replacement for WinForms, it’s VERY customizable but has a somewhat infamously steep learning curve. I’ve spent a bit of cash on books on WPF, followed all the PDC presentations, podcasts etc…on the subject matter. After 3 weeks I’m finally getting comfortable with it…
Second, I needed a DB…needed to be lightweight, needed to support LINQ (what, I like LINQ :)), needed to support some sort of ORM technology. 
The most obvious choice was SQL Server Compact Edition, a kind of cut down SQL server which is great for desktop apps…however…I could not get it working! Turns out it’s really sensitive to the versions of various installed components. This was a red light for me…I needed the DB to be as stand-alone as possible! That left one obvious choice:
SQLite, from the website:

‘SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. SQLite is the most widely deployed SQL database engine in the world. The source code for SQLite is in the public domain.’

Perfect! For .NET support, I used the SQLite ADO.NET provider (there is a C# only port of SQLite, but it’s slower and not needed for my scenario. Deployment is literally including a reference and creating a file…now it does have one pretty severe limitation which I’ll cover later :)

My ORM choices were nHibernate or EF…there being support for SQLite in both of these platforms.
Now, I have used nHibernate for a recent project (disclaimer, I left fairly early in the project :))  and one of the things which annoyed me was the large number of dependencies, and the seeming fragility across versions of these dependencies. For a low impact desktop app, this was just too large a footprint and too big a risk. So I had to count it out.
This left me with one choice…Entity Framework. Now, this wasn’t an easy choice…I have used EF in the past (tinkered with it) and found it a painful experience…(others have too which led to this). Now, I’m not getting into the politics behind MS choice to push EF (and yes, it was a political choice), but V1 is fairly buggy, and for me at least fairly unnatural. This led to my second learning ‘opportunity’…Master Entity Framework! As I write this, it’s literally 2 hours since I worked out my last bit of EF pain…which led to this model:

Capture

Looks simple…well, yeah…but it’s taken me AGES to get to this…I know SQL really well, C# REALLY well, but the mix of obscure concepts, poor documentation and obfuscated error messages makes working / learning EF way too hard! EF 4 (in VS 2010 / .NET 4) improves this…and it can’t come soon enough!

So, that’s worked out…I hope :)

So, I mentioned previously that SQLite has a bit of an issue…it doesn’t really like concurrency. Well, that’s an overstatement…it is a multi-read, single write system. Once you realize it’s an issue it’s fairly easy to solve. I use this pattern:

 

public static void AddRange(IEnumerable<UploadFile> files)
        {
            using (PixEntities ent = new PixEntities())
            {
                using (new ReadLock(entityLock))
                {

                    using (new WriteLock(entityLock))
                    {
                        foreach (var file in files)
                        {
                            ent.AddToUploadFiles(file);
                            ent.SaveChanges();
                        }
                    }
                }
            }
        }
 

As you seen, this has two main elements…I use a ReadLock (where I would normally read *from* the DB, in this case umm, I don’t :)) then I use a WriteLock to wrap any operation where I write to the DB. The observant will also notice what looks like a bug…I do ent.SaveChages() for each iteration of the loop…this is another SQLite quirk, it doesn’t support batch updates…and gives a weird error about file locks if you try :)

The little locking class I use is from here, but I’ve put it below for your use…anyway, this is part one of several parts of my experiences in working on cozwecan…stay tuned!

 

  public static class Locks
    {
        public static void GetReadLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterUpgradeableReadLock(1);
        }


        public static void GetReadOnlyLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterReadLock(1);
        }


        public static void GetWriteLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterWriteLock(1);
        }


        public static void ReleaseReadOnlyLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsReadLockHeld)
                locks.ExitReadLock();
        }


        public static void ReleaseReadLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsUpgradeableReadLockHeld)
                locks.ExitUpgradeableReadLock();
        }


        public static void ReleaseWriteLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsWriteLockHeld)
                locks.ExitWriteLock();
        }


        public static void ReleaseLock(ReaderWriterLockSlim locks)
        {
            ReleaseWriteLock(locks);
            ReleaseReadLock(locks);
            ReleaseReadOnlyLock(locks);
        }


        public static ReaderWriterLockSlim GetLockInstance()
        {
            return GetLockInstance(LockRecursionPolicy.SupportsRecursion);
        }


        public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy)
        {
            return new ReaderWriterLockSlim(recursionPolicy);
        }
    }


    public abstract class BaseLock : IDisposable
    {
        protected ReaderWriterLockSlim _Locks;


        public BaseLock(ReaderWriterLockSlim locks)
        {
            _Locks = locks;
        }


        public abstract void Dispose();
    }


    public class ReadLock : BaseLock
    {
        public ReadLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetReadLock(this._Locks);
        }


        public override void Dispose()
        {
            Locks.ReleaseReadLock(this._Locks);
        }
    }


    public class ReadOnlyLock : BaseLock
    {
        public ReadOnlyLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetReadOnlyLock(this._Locks);
        }


        public override void Dispose()
        {
            Locks.ReleaseReadOnlyLock(this._Locks);
        }
    }


    public class WriteLock : BaseLock
    {
        public WriteLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetWriteLock(this._Locks);
        }


        public override void Dispose()
        {
            Locks.ReleaseWriteLock(this._Locks);
        }
    }

posted @ Wednesday, January 20, 2010 12:53 PM | Feedback (2) | Filed Under [ Code Snippets Multi-Threading cozwecan ]

Thursday, January 07, 2010

The EF Fail Whale strikes again…

UPDATE: Thanks for all those who commented on this. Turns out the problem was pretty simple. You just have to get rid og the ‘FileId’ and ‘DirectoryId’ properties. They’re ONLY for navigation so cannot be in the entity as fields too…easy peasy :)

 

I like EF, conceptually and in practice it’s pretty nice. EF4 adds a lot of the missing goodness. There’s one thing which ALWAYS stumps me in EF 3 though…this VERY simple paradigm…

Given this class layout (generated from a SQLLite DB)

 

image

See the little 0..1->* relationship? Should be dead simple, the DirectoryId in the UploadFile entity maps to the WatchDirectory Entity…with this association:

 

image

I ask you, how the hell can that fail??? But no matter what, this happens…

 

Error    3    Error 3007: Problem in Mapping Fragments starting at lines 124, 154: Non-Primary-Key column(s) [DirectoryId] are being mapped in both fragments to different conceptual side properties - data inconsistency is possible because the corresponding conceptual side properties can be independently modified.
    G:\Work\cozwecan_code\UploadClient\cozwecan.pix.client\cozwecan.pix.client\Model.edmx    155    11    cozwecan.pix.client

 

Anyone got more of a clue about EF than I have???

posted @ Thursday, January 07, 2010 8:04 AM | Feedback (2) |

Image Utilities

Thought I’d just post some dumb little utility classes that I had hanging about in my code repository (and always end up trying to find).

They’re just little utility classes for finding out image format / extensions / content types for various images…(and before any smartass pipes up, yes much of this info is in registry…but this is intended for use where I can’t access that)

 public static class Imaging
    {

        public static string GetContentTypeByImageFormat(ImageFormat format)
        {
            string ctype = "image/x-unknown";

            if (format.Equals(ImageFormat.Gif))
            {
                ctype = "image/gif";
            }
            else if (format.Equals(ImageFormat.Jpeg))
            {
                ctype = "image/jpeg";
            }
            else if (format.Equals(ImageFormat.Png))
            {
                ctype = "image/png";
            }
            else if (format.Equals(ImageFormat.Bmp) || format.Equals(ImageFormat.MemoryBmp))
            {
                ctype = "image/bmp";
            }
            else if (format.Equals(ImageFormat.Icon))
            {
                ctype = "image/x-icon";
            }
            else if (format.Equals(ImageFormat.Tiff))
            {
                ctype = "image/tiff";
            }

            return ctype;
        }

        public static ImageFormat GetImageFormatByContentType(string contentType)
        {
            ImageFormat format = null;

            if (contentType != null)
            {
                if (contentType.Equals("image/gif"))
                {
                    format = ImageFormat.Gif;
                }
                else if (contentType.Equals("image/jpeg") || contentType.Equals("image/pjpeg"))
                {
                    format = ImageFormat.Jpeg;
                }
                else if (contentType.Equals("image/png"))
                {
                    format = ImageFormat.Png;
                }
                else if (contentType.Equals("image/bmp"))
                {
                    format = ImageFormat.Bmp;
                }
                else if (contentType.Equals("image/x-icon"))
                {
                    format = ImageFormat.Icon;
                }
                else if (contentType.Equals("image/tiff"))
                {
                    format = ImageFormat.Tiff;
                }
            }

            return format;
        }

        public static string GetFileExtensionByContentType(string contentType)
        {
            string ext = "bin";

            if (contentType.Equals("image/gif"))
            {
                ext = "gif";
            }
            else if (contentType.Equals("image/jpeg") || contentType.Equals("image/pjpeg"))
            {
                ext = "jpg";
            }
            else if (contentType.Equals("image/png"))
            {
                ext = "png";
            }
            else if (contentType.Equals("image/bmp"))
            {
                ext = "bmp";
            }
            else if (contentType.Equals("image/x-icon"))
            {
                ext = "ico";
            }
            else if (contentType.Equals("image/tiff"))
            {
                ext = "tif";
            }

            return ext;
        }

        public static string GetContentTypeByFileExtension(string fileExtension)
        {
            switch (fileExtension)
            {
                case ("gif"):
                    return "image/gif";
                case ("jpg"):
                    return "image/jpeg";
                case ("jpeg"):
                    return "image/jpeg";
                case ("bmp"):
                    return "image/bmp";
                case ("tif"):
                    return "image/tiff";


            }
            return "application/octet-stream";

        }
    }

posted @ Thursday, January 07, 2010 5:41 AM | Feedback (0) | Filed Under [ Code Snippets ]

Friday, December 18, 2009

Putting together my CV (or resume…whatever!)

Well, I got a job with a company in Glasgow just after I returned…quit today…various reasons why I left and I don’t want to rake over old ground. I’m working on my resume…so…first bit, Job history. Never quite sure how much detail to put in here…just a list of titles and dates / paragraph about each one…whaddya think?

 

Word version: here

Scott Galloway

Phone Number: 07563922053
Blog: http://www.mostlylucid.net
Email: scott.galloway@gmail.com

DOB: 02/02/1973

Work History

· 01/2007-10/2009 – Microsoft Corporation, Redmond, WA
Program Manager, ASP.NET Team / Project Server Team

I was responsible for the design of Product features; these included the design of all WebForms features in ASP.NET 4 and potential future releases. I was also responsible for managing the release process of a wide range of products including ASP.NET MVC.

· 06/2005-01/2007 – Microsoft Limited, Reading, UK
Application Development Consultant
As an ADC I advised a number of businesses on the improvement of both application design and architecture as well as improving development practices. I specialized in ASP.NET and Sharepoint as well as in the performance and scalability of enterprise web applications.

· 02/2003-06/2005 – Storm ID Ltd, Edinburgh, UK
Senior Software Architect
At StormId I was involved in the full lifecycle development of a number of customer facing Web Sites. These sites were both ASP.NET as well as classic ASP and SQL Server based.

· 03/2002-02/2003 – VisitScotland.com, Edinburgh, UK
Technical Architect (management responsibilities)
Whilst at VisitScotland.com I oversaw the migration of a Java based Corba architecture to a J2EE / JSP and patterns based system. Additionally I mentored a team of classic ASP developers in the building of a number of high scalability customer facing websites.

· 06/2000-03/2002 – GlobalFarmers.com, Edinburgh, UK
Internet Development Manager(management responsibilities)
Globalfarmers.com was the largest internet startup in Scottish history. I was brought in to bring development back on track with a third party supplier. The system was built using J2EE and an OODMBS, I also developed a subsidiary PHP based system.

· 02/1999-06/2000 – BlackID, Glasgow, UK
Web Developer
At BlackID I worked on the server side elements of web sites for huge customers such as Burger King.

posted @ Friday, December 18, 2009 11:45 AM | Feedback (0) |

Tuesday, October 13, 2009

Leaving Microsoft…for real! Got a job?

Tomorrow, after 4 1/2 years, including 2 years with the ASP.NET Team I’m finally leaving Microsoft.

The reasons for leaving are varied, I am resigning, but the timing is not entirely my own choice. I’m looking at this as an opportunity, I’ve done a huge amount of thinking about the course my life should take next and this really is a positive step in that direction. I have no bitterness nor animosity towards Microsoft, they hung in longer than a lot of employers would and that led me to stay too long in a job I wasn’t suited for. They are a great company, the majority of the people I worked with are passionate, really smart people and I’ll miss them a lot. Some others are power hungry jerks and completely dissociated from the real world…but then every company has their share of those people.

I am at heart a developer, I love designing and building web applications and frankly I’m incredibly good at it and I especially love working with other people who are good at it too (or want to be). I posted some time ago about how I got to where I was (well, am until about midday tomorrow). Now I need to look for a job which gives me the same level of excitement which I’ve lost of late. I am really grateful to have had the chance to mature professionally, all the prima-donna shit which comes through lacking confidence in my own skills that I had previously has gone. I’ve seen what life is like in the place every developer using ASP.NET longs to go and well, it wasn’t for me.

Right now I plan to move back to my native Scotland and find a job where they need someone like me, someone who still has a passion for web technologies and the drive and knowledge to build pretty much anything.

posted @ Tuesday, October 13, 2009 7:27 PM | Feedback (3) |

Monday, October 12, 2009

Dead Simple Geolocation…

Messing around with this stuff of late, @TheCodeJunkie requested it so here’s a really simple way to get a location and show a map :). This version uses the W3C Geolocation API on browsers that support it (FireFox 3.5, Safari, iPhone, Opera) and falls back to using Google Gears on other browsers (including IE).

Just a really dumb sample right now but who knows, someone might find it useful! I’ll update later with some of the more advanced stuff I’m working on in this area…

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
    <script src="Scripts/gears_init.js" type="text/javascript"></script>
    <script type="text/javascript">
        function SetLocation() {
            if (typeof (navigator.geolocation) != "undefined") {
                geo = navigator.geolocation;
                positionOptions = { maximumAge: 600000 };
            }
            else {
                if (!window.google || !google.gears) {
                returnUrl = this.location.href;
                location.href = "http://gears.google.com/?action=install&message=Gears now enabled!" +
                    "&return=" + returnUrl;
                }

                geo = google.gears.factory.create('beta.geolocation');
                positionOptions = {enableHighAccuracy:true, maximumAge: 600000};
            }
            geo.getCurrentPosition(GetMap, handleError,positionOptions);
        }
        function GetMap(position) {

            latitude = position.coords.latitude;
            longitude = position.coords.longitude;
            map = new VEMap('myMap');
            map.HideDashboard();


            map.LoadMap(new VELatLong(latitude, longitude), 15, 'h', false);
            var shape = new VEShape(VEShapeType.Pushpin, map.GetCenter()); shape.SetTitle('You are here!'); map.AddShape(shape);
        }
        function handleError(positionError) {
            alert('Attempt to get location failed: ' + positionError.message);
        }      
    </script>
</head>
<body onload="SetLocation();">
    <div id='myMap' style="position: relative; width: 1000px; height: 1000px;">
    </div>
</body>
</html>

posted @ Monday, October 12, 2009 9:56 PM | Feedback (1) |

Sunday, September 13, 2009

ServerControlWithSprinklesAndBacon Part 1.

It seems to many that the days of ASP.NET Web Form Server Controls have just about come to an end. The problem many people have with Server Controls is that they have a pretty high ‘concept count’ in their construction. The arrival of ASP.NET MVC with it’s deliberate decision to eschew Server Controls in favor of very simple ‘HTML Helpers’ and simple inline markup generation seemed to put the final nail in the coffin of these old war-horses of ASP.NET.

Whilst writing a chapter in a book I’m working on about ASP.NET 4, I got to thinking about a Server Control prototype I worked on last year…the ServerControlWithSprinklesAndBacon. In essence the SCWSB (as it will hereon be called) was a vastly over engineered attempt to answer a simple requirement:

Is it possible for a Server Control to change it’s design & run time behaviors based on the adapter the user has selected.

Why on earth would I want to do this? Well, one of the changes we made for ASP.NET 4 is in improving the CSS and Rendering behaviors of a number of our Server Controls which ship with ASP.NET. In an earlier incarnation of this work, we looked at doing this using Control Adapters; a feature which switches out the runtime code used to generate the HTML for a control. As it turned out, we decided to go in a different direction for achieving this (which does not use control adapters) as it was felt that Control Adapters introduced too much complexity into the mix.

Originally, you would have been able to specify the Control Adapter to use for a specific control within a page at runtime (in fact, you could use another feature called ‘Adapter Groups’ to define them at almost any level from app right through to control…but I digress). The main use for Control Adapters was to be in allowing you to use alternate rendering behaviors, think, more CSS Friendly HTML or even totally different markup…. A problem with this was that one of the most fundamental features of Server Controls, Designer support would be lost when taking this approach. In fact in ASP.NET 4, you *do* lose a lot of designer functionality when using the controlRenderingCompatabilityVersion=”4.0” setting, for example, AutoFormats no longer work for most controls.

Anyway, this lack of designer support for changing control adapters on a specific control bugged me to no end…and I spent a number of sleepless nights working on the problem. In the end here’s what I wanted to achieve:

  1. Add support for ‘designer’ control adapters, so rather than just runtime, control adapters also changed design time HTML.
  2. Work out how to change the items in the Property Grid for a control based on the selected control adapter (so, hide some that no longer apply and add some new ones)
  3. Make them easy to use…
In looking at these requirements, I needed to plumb the (relatively unexplored) depths of the ASP.NET Server Control framework, use some features in ways they were never really supposed to be used and discover way more about how Server Controls interact with the VS.NET design surface than anyone really should…in Part 2 I’ll take you on that journey and show you how I came up with the ServerControlWithSprinklesAndBacon… 

posted @ Sunday, September 13, 2009 5:20 PM | Feedback (0) |

Tuesday, May 19, 2009

ASP.NET 4 Beta 1: WebForms Routing extension methods

When we were adding WebForm routing to ASP.NET 4 Beta 1, we didn’t have a chance to add in a couple of methods which make working with Routes and Web Forms a lot easier!

Note: the code below is ‘similar’ to that which we’re adding ASP.NET 4 Beta 2, *that* code will be of much higher quality however, this is a sample only!

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using System.Web.Routing;
   6:  
   7:     public static class RouteExtensions
   8:     {
   9:         public static string GetUrlForRoute(this System.Web.UI.Page page, string routeName, RouteValueDictionary parameters)
  10:         {
  11:             VirtualPathData vpd= RouteTable.Routes.GetVirtualPath(null, routeName, parameters);
  12:             return vpd.VirtualPath;
  13:         }
  14:         public static string GetUrlForRoute(this System.Web.UI.Page page, RouteValueDictionary parameters)
  15:         {
  16:             VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(null, parameters);
  17:             return vpd.VirtualPath;
  18:         }
  19:  
  20:         public static void IgnoreRoute(this RouteCollection routes, string url)
  21:         {
  22:             routes.IgnoreRoute(url, null);
  23:         }
  24:  
  25:         public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
  26:         {
  27:             if (routes == null)
  28:             {
  29:                 throw new ArgumentNullException("routes");
  30:             }
  31:             if (url == null)
  32:             {
  33:                 throw new ArgumentNullException("url");
  34:             }
  35:             IgnoreRouteInternal internal3 = new IgnoreRouteInternal(url);
  36:             internal3.Constraints = new RouteValueDictionary(constraints);
  37:             IgnoreRouteInternal item = internal3;
  38:             routes.Add(item);
  39:         }
  40:  
  41:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile)
  42:         {
  43:             return routes.MapRoute(name, url, physicalFile, null, null);
  44:         }
  45:  
  46:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults)
  47:         {
  48:             return routes.MapRoute(name, url, physicalFile, defaults, null);
  49:         }
  50:  
  51:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, string[] namespaces)
  52:         {
  53:             return routes.MapRoute(name, url, null, null, namespaces);
  54:         }
  55:  
  56:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, object constraints)
  57:         {
  58:             return routes.MapRoute(name, url, physicalFile,  defaults, constraints, null);
  59:         }
  60:  
  61:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, string[] namespaces)
  62:         {
  63:             return routes.MapRoute(name, url, physicalFile,  defaults, null, namespaces);
  64:         }
  65:  
  66:         public static Route MapRoute(this RouteCollection routes, string name, string url, string physicalFile, object defaults, object constraints, string[] namespaces)
  67:         {
  68:             if (routes == null)
  69:             {
  70:                 throw new ArgumentNullException("routes");
  71:             }
  72:             if (url == null)
  73:             {
  74:                 throw new ArgumentNullException("url");
  75:             }
  76:             if (physicalFile == null)
  77:             {
  78:                 throw new ArgumentNullException("physicalfile");
  79:             }
  80:             Route route2 = new Route(url, new PageRouteHandler(physicalFile));
  81:             route2.Defaults = new RouteValueDictionary(defaults);
  82:             route2.Constraints = new RouteValueDictionary(constraints);
  83:             Route item = route2;
  84:             if ((namespaces != null) && (namespaces.Length > 0))
  85:             {
  86:                 item.DataTokens = new RouteValueDictionary();
  87:                 item.DataTokens["Namespaces"] = namespaces;
  88:             }
  89:             routes.Add(name, item);
  90:             return item;
  91:         }
  92:  
  93:         // Nested Types
  94:         private sealed class IgnoreRouteInternal : Route
  95:         {
  96:             // Methods
  97:             public IgnoreRouteInternal(string url)
  98:                 : base(url, new StopRoutingHandler())
  99:             {
 100:             }
 101:  
 102:             public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues)
 103:             {
 104:                 return null;
 105:             }
 106:         }
 107:     }
 108:  

So, how do you use these methods?

These extensions add a few simple methods:

MapRoute

Stolen shamelessly from MVC. These methods provide a shortcut to defining Routes for WebForms.

RouteCollection.MapRoute(string name, string url, string physicalFile);

For example to map a simple route you can now do:

RouteTable.Routes.MapRoute(“ProductDetailsRoute”, “products/{productid}”, “~/ProductDetails.aspx”);

Also has a couple of variants allowing for defining defaults etc…

RouteCollection.MapRoute(string name, string url, string physicalFile, object defaults);

RouteCollection.MapRoute(string name, string url, string physicalFile, object defaults, string[] namespaces);

GetRouteForUrl

Page.GetUrlForRoute(string routeName, RouteValueDictionary parameters);

Using a specifically named route and RouteValueDictionary containing a number of parameters will give you back a string containing the Url to use. REALLY useful in databound apps!

e.g.,

<asp:hyperlink ID="HyperLink1" runat="server"  NavigateUrl='<%# Page.GetUrlForRoute("ProductDetailsRoute",new RouteValueDictionary( new {productid= Eval("ProductID")}))%>'  Text="Details"></asp:hyperlink>

Page.GetUrlForRoute(RouteValueDictionary parameters);

Same thing, but auto-matches the RouteValueDictionary parameters against a route…

IgnoreRoute

Allows you to define urls which should not be tried for matches against routes (can be useful for upgrading WebForms apps)

RouteCollection.IgnoreRoute(string url);

RouteCollection.IgnoreRoute(string url, object constraints);

posted @ Tuesday, May 19, 2009 1:23 PM | Feedback (5) | Filed Under [ Code Snippets ASP.NET 4.0 ]

Powered by: