Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Sunday, January 3, 2010

Linq Queries against most collections including ListView, ListViewItemCollection, ControlCollection or anything IEnumerable

The Problem:


You can not run Linq queries against many Framework collections such as ListViewItems and Controls in an immediately obvious manner.

if (list.Items.All(item => item.Checked))
    return true;

The previous code snippet won't compile, won't work and won't shove you in the right direction via IntelliSense, Online Help, or by performing a super-quick Google for the hopelessly attention-deficit disordered such as myself. (more on the Googling later)

The Fix:


Introducing the Enumerable .Cast (TResult) Method

if (list.Items.Cast<ListViewItem>().All(item => item.Checked)
    return true;

Using this method you can rewrite the original code quite easily to this code and Linq query the collection to death with all of your favorite little Linq sledgehammers...

The Why:


Background story for those of you who didn't jump ship to go off using the solution...

I was trying to something I thought would be very straight-forward using Linq. I wanted to sync a tri-state "parent-relationship" CheckBox control with a ListView control that contained "child-relationship" items that had their own respective item specific checkboxes. The parent to child relationship here is conceptual and not baked into the controls, we're talking a simple CheckBox and ListView here.

You've all probably done something similar whether it be with a TreeView using checkboxes containing items with checkboxes. You've certainly seen this behavior if you've ever done a backup and selected what you want backed up on a hard drive. The concept is simple: if all child items are checked or unchecked, the parent CheckBox should be Checked or Unchecked accordingly. If some of the child objects are checked and some aren't, the parent CheckBox should be Indeterminate.

(The backup then flubs your selection, doesn't back up your nicely selected SQL database, and can't seem to adequately backup to your 1TB external HD popping up endless dialogs to let you know how inefficient it truly is but that's a whole different post altogether.)

All fine and dandy, this will be easy, right? Well, the first thing I wanted to do is say something like...

if (listEmployees.Items.All(λ => λ.Checked))
    checkEmployees.CheckState = CheckState.Checked;
else if (listEmployees.Items.All(λ => !λ.Checked))
    checkEmployees.CheckState = CheckState.Unchecked;
else
    checkEmployees.CheckState = CheckState.Indeterminate;

Obviously, my ListView is a list of Employee business objects and my CheckBoxes on my list items are per employee item that I'm displaying. Nothing fancy here.

Note: I use the λ character now for a lot of my Linq statements after reading this question about LINQ to SQL business object creation best practices. I agree that it's technically not the most accurate usage of the Lambda characer however it does clarify the code (to me) and reduces the chance of variable declaration conflicts with Linq queries.

So, I'm ready to compile, skip testing, deem this code ready for production and ship it to my hungry client when this little nastygram pops up during compile...

'System.Windows.Forms.ListView.ListViewItemCollection' does not contain a definition for 'All' and no extension method 'All' accepting a first argument of type 'System.Windows.Forms.ListView.ListViewItemCollection' could be found (are you missing a using directive or an assembly reference?)

Grr. OK, so off to Google I go (oh you do it too!)... I land on
LINQ on ListView.Items (ListViewItemCollection) telling me in no uncertain terms, with an accepted answer, that this just can't be done. Piffle! No way! I'm outraged! Linq can do anything!

So, I typically look at one Google answer like this and go back to the code to see what I, with all my omnipotent developer powers, can figure out. (Usually I'm humbled to admit the same defeat as the previous blog poster but not this time!

So, instinct and too much coffee tells me to look up 2 things...

1.) What are the requirements, more specifically, the "where" clause, if any, of the Linq method "All"
2.) If the ListView.Items property doesn't meet this requirement, then why doesn't it dangit!?!

So, doing a "Go to definition" on the All Linq Extension method I come up with the following in my sweet little meta data viewer.

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

The Linq All entension method requires an IEnumerable<TResult> as the source of the extension meaning that only objects that expose IEnumerable<TResult> as part of their class definition will get picked up, and be extensible using the Linq system. There's no where clause, it must be an IEnumerable<TResult> supported object whether by inheritance or interface support.

OK, I was thinking just IEnumerable personally but actually IEnumerable<TResult> makes sense since Linq has to perform anonymous queries using properties of the object that's being extended. Meaning if Linq extended IEnumerable (pure) that's great but when I went to do my neat little .Checked (is true) statement Linq wouldn't know what the heck a .Checked was because IEnumerable would be based on an enumerable object collection.

Fine, makes sense, so what the heck is a ListView.Items collection then? Back over to the "Go to definition" meta lookup for that guy...

[Localizable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.ListViewItemCollectionEditor, System.Design, Version=2.0.0.0, 
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof (UITypeEditor))]
[MergableProperty(false)]
public ListViewItemCollection Items { get; }

Uh...OK, so the Items property is this ListViewItemCollection class, but what is that? AGAIN with the lookup I come to...

public class ListViewItemCollection : IList, ICollection, IEnumerable

Aha! It's the problem I just defined in my mind. ListViewItemCollection is just an IEnumerable collection (of objects). The indexer...

public virtual ListViewItem this[int index] { get; set; }

.. is the reason we can easily deal with ListViewItems when we're doing foreach loops on the ListView.Items property. It does the boxing for us. Interesting performance hit, will have to check that out later as Microsoft is probably (hopefully) doing something to defer the boxing cost of each item in the List.

OK, so problem identified but what could I do about it. I'd love to say it was some methodical deduction that brought me to the Cast method but I just did an IntelliSense on the ListView.Items property to see what Linq extensions were available to me.

I started looking into AsQueryable() but I couldn't quite achieve what I wanted. Then, I noticed the little Cast beneath it (in IntelliSense). Poof, that worked.

Voila, now you can run your handy Linq queries against most anything IEnumerable as long as you can, with some accuracy, cast each member to a specific type.

On the Googling: I thought to myself cool! I figured out a problem everyone can use everywhere at all times. Then that creeping feeling overcame me that, "Nah, that was too easy" so I went back to Google. I wasn't a pioneer after all, drat. A Good, practical LINQ example shows you the same technique with a bit more detail and insight (but less comedy) than I came up with. I didn't realize that Linq works on Sequences so you should check this post out as well for a better understanding. Our similarities in deducing the same conclusion was a bit eerie however but I'll let that slide and drink more java (coffee not the language).

I will post my final solution to the checkbox issue itself because it screams reusable code to me. I will refactor my solution into something that isn't bound to specific controls, maybe not even Windows.Forms controls and post it here later.

I made a New Years Resolution to blog at least 4 times a week and mean to keep it up so this is the first installment (late already).

That's all for today and happy Linq'ing...

Friday, October 23, 2009

Using the ResolveUrl method inside your class library Business Logic Layer (BLL)

Getting around passing HttpContext.Current to your business logic layer, or BLL.


The Problem: 
You want to use handy methods like ResolveUrl inside your Business Logic Layer (BLL) which is a class library. You don't want to reference System.Web or any .Net framework library beneath the System.Web subsystem inside your BLL libraries.

The Fix:
Create a small Utility class inside your class library and pass it a delegate. I know, I'm not a big fan of Utility (or similar) classes but sometimes you need a little tool-belt class for miscellaneous utilitarian methods. I actually have a Common class library that has even less dependencies than my typical BLL library which is where I stuck this class. Obviously, place the code where appropriate for your solution.



using System; 
namespace Common 
{ 
    public static class Utility 
    { 
        public static Func<string, string> ResolveUrl; 
    } 
} 

Now in your global.asax place the following.

private void Session_Start(object sender, EventArgs e) 
{ 
    AssignBusinessLogicLayerHelpers(); 
} 

private static void AssignBusinessLogicLayerHelpers() 
{ 
    var context = HttpContext.Current; 
    if (context == null) return; 

    var page = context.Handler as Page; if (page == null) return; 

    // assign the resolve url method 
    if (Utility.ResolveUrl == null) 
    { 
        Utility.ResolveUrl = page.ResolveUrl; 
    } 

    // assign other helper methods here... 
} 


Here we've elegantly passed a generic delegate to our Utility class that will reference back to the provided ResolveUrl method. The method stamp deals with primitive types only (strings) so we have no dependencies passing this function pointer across the domains.

The Why:  

You do not want to have a BLL or similar class library system utilizing System.Web, or System.Windows.Forms for that matter. If you have a solution with a web service, a web site and a common class library (BLL or other) and you start using methods dependent on HttpContext.Current you are going to run into issues as this context is not the same between the web service and your web site. A common scenario might be a LINQ2SQL Business Object Layer on top of your SQL Database which has effectively become your BLL. You may have classes that need to store virtual, resolved paths into the database but you want to place the resolving of the URL inside your business object classes to enforce a  strong encapsulation of the class. Here's an easy, 5 minute way to provide such features to your business objects without breaking your nTier application model. Now you can happily store the AvatarUrl of your users in the database and have your aspnet_User class do the resolution on the fly!

Thursday, September 25, 2008

Hermes Java File Uploader for Asp.Net Multiple Large File Uploads with Resume Capability



I needed a control that would upload multiple, large files (100+ MB), resume on error AND allow me to "catch" the uploaded files in C# code so I could perform my own manipulations, and I needed it fast. Hermes Java File Uploader was the answer, the only answer.

Here I am working on this web site for my current contractor and we determine that we have a need for uploading large files to the web server via. a public web page. In years of Asp.Net development I have actually never ran into this requirement on a website. I was lucky. This sounded to me like a very common task requiring a simple solution even though I had never come across the need in my development endeavors.

I was wrong.

The Asp.Net built-in methods for uploading large files is lacking at best. While the provided FileUpload may be sufficient for small uploads and certainly simplifies uploads from previous .Net versions it's not suitable for large file uploads, resuming interrupted uploads, or managing multiple uploads effectively.

The age old question comes "to develop, or not to develop". I started reading into the problem, and concurrently pricing 3rd party controls. To my dismay, while researching pure Asp.Net upload controls that would effectively solve all of my requirements I determined only one thing - such a thing doesn't exist! Well, that put quite a damper on my hopes of writing some quick elegant control in C#/Asp.Net that would handle all of my needs. Time is always of the essence and I just don't have the time to pull off a RYO control of this scale on the fly, especially with no samples of other people's working controls on CodeProject to get me started.

I swallow hard and realize I'm going to have to enter that scary untrustworthy world of JavaScript for my solution. I tried a few controls of varied prices and nothing really sat very well with me. Then I came across the Hermes Java File Uploader control.

I was instantly a little more comfortable with a Java control vs. a JavaScript control for obvious reasons.

The requirements I discovered I actually needed as I tested various controls were the following.
  • Be able to browse the user's local file system and select multiple files easily.
  • Manage selected files easily - add to, remove, etc. before clicking the upload button.
  • Resume a broken upload no matter what happened, user clicks the back button AND says "Yes, I'm absolutely sure I know exactly what I'm doing even though we're at 99% complete on my upload" -or- my dedicated server which I manage that NEVER goes down because of an oversight on my part spontaneously combusts thus breaking the download through no fault of my own.
  • Handle very large files up to a possible 100 MB.
  • This is the kicker - be able to upload the file through the control but pass the handling off to C# code where I can further massage the upload.
I've got to admit by the time I found the Hermes control I was a bit jaded and getting that distant churn in my stomach that this wasn't going to be possible without some kind of Enterprise Solution intervention. We all know if you slap the word Enterprise on something the price jumps $10,000. So I start fumbling around with the Hermes control and instantly I'm uploading multiple files, breaking connection, resuming, handling large files and then I get to the killer. I need to catch the upload in C# code. I found Hermes because of this capability while Googling so I start looking at their provided Asp.Net page.

It didn't do what I wanted right away. I should preface this with the fact that I am Java retarded and have about 2 hours of Google knowledge on how uploads work behind the scenes in Asp.Net. Which, in reality, isn't Asp.Net specific at all and is pretty much the same old HTML way of doing things with not much wrapping going on.

I try to smash round peg into square hole for a while and realize I don't know what the heck I'm doing so I contact the guys over at Hermes and this is where I'm blown away. I explain what I'm trying to do and the crunch I'm under, "please help me asap". I get a reply immediately telling me to try a couple of things so I do and it's still not working. I eat a big fat slice of humble pie and email back explaining how Java and Upload challenged I am and that I'm really in a bind and this control just has to work because there's nothing else even close, it just HAS TO.

Their team emails me back and suggests this: We open up an FTP account for them to access my server and they will take a look at what's going on, find the issue, fix it, upload it, solve it, and email me back when they're done. My jaw hit the floor. In 12 years of software development I can honestly say: never. Not only did they do exactly that but it was done that day!

You know what, this is a shameless plug for the Hermes team and their control. It's the least I could do for the unbelievable support and solution they provided. It's also not hard to get behind a control I'm using and very happy with. Kudos to Marek and the development and support teams over at Hermes for going light years above and beyond the call of duty to help out a single customer who incidentally was purchasing the cheapest version of their control.

A note for Asp.Net web developers. One thing that we did run into that I was unaware of... If you're going to be uploading large files to your website you will need to change a setting in your web.config to account for this.

HttpRuntimeSection..::.MaxRequestLength Property

I would suggest setting this in the code behind file where you "catch" your uploads.

The alternative is setting the value in your web.config file as found on MSDN. Making this a global setting in your web.config is probably not wise however and you should at least constrain the setting to a specific location on your website as a precaution.

Thanks and happy uploading.