blog.mha.dk
The on-line blog of Michael Holm Andersen

LINQ to SQL – Group 2 rows into 1

Thursday, 12 August 2010 20:55 by mha

Jacob and I have been struggling with some “nasty” LINQ today where we need to get the aggregated value of a Quantity column from a transaction table, however a little “twist” was that we need to sum all the positive values into one group and all the negative values into another, and in the result set end up with only ONE row containing a PositiveQuantitySum and a NegativeQuantitySum … at first it sound pretty easy, but turned out not to be the case.

After a few attempts we ended up with this LINQ which actually gives the correct result, but we have 2 rows (one “positive”, one “negative”) – here is the LINQ:

from test in
(from t in AxInventTransactions
where t.Quantity >= 0 &&
              t.DateFinancial >= DateTime.Parse("01-04-2010") &&
              t.DateFinancial <= DateTime.Parse("31-08-2010") &&
              t.Customer.StatisticsGroupName.Contains("S")
              orderby t.ItemNumber, t.CustomerAccountNumber
              group t by new { t.ItemNumber, t.CustomerAccountNumber, t.VendorAccountNumber, t.Customer.StatisticsGroupName, CustomerName = t.Customer.Name, ProductName = t.Product.Name }
              into myGroup select new { myGroup.Key.ItemNumber,
              myGroup.Key.CustomerAccountNumber, 
              myGroup.Key.VendorAccountNumber, 
              myGroup.Key.StatisticsGroupName, 
              myGroup.Key.ProductName, 
              myGroup.Key.CustomerName, 
             Quantity = myGroup.Sum(i => i.Quantity)
})
.Union(from t in AxInventTransactions
where t.Quantity < 0 &&
              t.DateFinancial >= DateTime.Parse("01-04-2010") &&
              t.DateFinancial <= DateTime.Parse("31-08-2010") &&
              t.Customer.StatisticsGroupName.Contains("S")
              orderby t.ItemNumber, t.CustomerAccountNumber
              group t by new { t.ItemNumber,
                               t.CustomerAccountNumber,
                               t.VendorAccountNumber,
                               t.Customer.StatisticsGroupName,
                               CustomerName = t.Customer.Name,
                               ProductName = t.Product.Name} 
              into myGroup select new { myGroup.Key.ItemNumber,  
                             myGroup.Key.CustomerAccountNumber, 
                             myGroup.Key.VendorAccountNumber, 
                             myGroup.Key.StatisticsGroupName, 
                             myGroup.Key.ProductName, 
                             myGroup.Key.CustomerName, 
Quantity = myGroup.Sum(i => i.Quantity) } ).OrderBy(x => x.ItemNumber).ThenBy(x => x.CustomerAccountNumber)
orderby test.ItemNumber, test.CustomerAccountNumber
select new
{
            ItemNumber = test.ItemNumber,
            CustomerAccountNumber = test.CustomerAccountNumber,
            VendorAccountNumber = test.VendorAccountNumber,
            QuantityPlus = test.Quantity >= 0 ? test.Quantity : 0,
            QuantityNeg = test.Quantity < 0 ? test.Quantity : 0,
            StatisticsGroupName = test.StatisticsGroupName,
            ProductName = test.ProductName,
            CustomerName = test.CustomerName,
}

The above LINQ which uses UNION to merge the two result set (positive / negative)produces a result set something like this:

result1

We would like to have this result as ONE row, containing a column for the positive sum (6) and a column for the negative sum (-3).

While eating dinner tonight (I now, geeky!), I realized that we had to skip UNION and do it all “in one go”. The problem however is that (as you probably already know): Anonymous types can't be null

… and grouping all the positive and negative values of Quantity “in one go” would result in a NULL value for each “alternate” value (or whatever we should call it), causing an InvalidOperationException in this case: “The null value cannot be assigned to a member with type System.Int32 which is a non-nullable value type.”

So .. we need to make sure that IF the value returned by Sum() is actually NULL (which does not play together with System.Int32 :-) we instead return 0 (as it’s harmless to either add/subtract zero, this will of course not affect the result).

I ended up with this LINQ:

from t in AxInventTransactions
where     t.DateFinancial >= DateTime.Parse("01-04-2010") && 
          t.DateFinancial <= DateTime.Parse("31-08-2010") && 
          t.Customer.StatisticsGroupName.Contains("S") 
          orderby t.ItemNumber, t.CustomerAccountNumber 
          group t by new {
t.ItemNumber,
                           t.CustomerAccountNumber, 
                           t.VendorAccountNumber,
                           t.Customer.StatisticsGroupName,
                           CustomerName = t.Customer.Name, 
                           ProductName = t.Product.Name } 
          into myGroup select new { myGroup.Key.ItemNumber,
                         myGroup.Key.CustomerAccountNumber, 
                         myGroup.Key.VendorAccountNumber, 
                         myGroup.Key.StatisticsGroupName, 
                         myGroup.Key.ProductName, 
                         myGroup.Key.CustomerName, 
QuantityPlus = myGroup.Where(x => x.Quantity >= 0).Sum(i => i.Quantity) != null ? myGroup.Where(x => x.Quantity >= 0).Sum(i => i.Quantity) : 0, 
QuantityNeg = myGroup.Where(x => x.Quantity < 0).Sum(i => i.Quantity) != null ? myGroup.Where(x => x.Quantity < 0).Sum(i => i.Quantity) : 0 }

Which produces a result set like this:

result2

As you can see we now have ONE row with a column for “all positive values aggregated” and column for “all negative values aggregated”, which is exactly what I want.

The ‘trick’ is to make sure you don’t get any NULL values into the quotation, which is done using the ternary operator (very useful in LINQ also!), like so:

QuantityPlus = myGroup.Where(x => x.Quantity >= 0).Sum(i => i.Quantity) != null ? myGroup.Where(x => x.Quantity >= 0).Sum(i => i.Quantity) : 0

Which “translates” to something like this:

If Quantity is equal or larger than 0, add the value of Quantity to the aggregated “QuantityPlus” (System.Int32), however if the Quantity value does NOT meet the criteria (aka is a negative value) then NULL would be added (which of course will cause an exception!), SO INSTEAD we need to add the value 0 to the aggregated “QuantityPlus” (using the ternary operator takes care of this).

Hope the above makes sense and helps anyone which is facing similar problem finding the solution a bit easier :-) .. if anyone knows an alternate way of solving this issue, please feel free to write me an email.

Android + Evernote == perfect combination

Friday, 18 June 2010 13:00 by mha

If you own an Android (or iPhone for that matter) you owe it to yourself check out Evernote (http://www.evernote.com/)

Evernote makes it extremely easy to save your ideas, things you see, things you like etc., and find them all on any computer, phone or device you use.

I use Evernote to:

  • Take pictures of business cards and make these searchable via the awesome OCR functionality of Evernote
  • Mark text (e.g. information about conferences, address information etc.) in Firefox and with a single click create a note which I can see on my Android phone
  • Using the Windows application easily tag, sort and organize notes into Notebooks
  • Using the “folder watch” feature (Windows application), drop a file into a folder and auto generate a note (in my “Imported” notebook) containing all the information, and having it available on my Android (e.g. PDF files, .txt files etc.)
  • Take images of ‘everything’ for later use, e.g. wine labels, recipes, signs and more – all searchable through the OCR feature of Evernote
  • Store important information in Emails on my phone, simply marks the text in Outlook and with a single click create a note.

ASP.NET MVC and ASCX

Tuesday, 9 February 2010 14:51 by mha

Jeffrey Palermo has an inspiring article about templating and (why not) to use .ascx aka UserControls in ASP.NET MVC – I’ll definitely be using this approach:

http://jeffreypalermo.com/blog/asp-net-mvc-and-the-templated-partial-view-death-to-ascx/

jQuery IntelliSense on ASP.NET MVC

Tuesday, 9 February 2010 12:51 by mha

If you’re using ASP.NET MVC, you’re probably also using jQuery and hence would like to have IntelliSense support in your ViewPages. According to Scott Gu all you need to do is to make sure you’re having the “-vsdoc.js” file in your script folder (e.g. “jquery-1.3.2-vsdoc.js”) and VS2008 (with the patch installed!) will find this file and jQuery IntelliSense works – however as it turns out if you’re using a path which starts with “/” (as you should!) VS2008 is unable locate the “-vsdoc.js” file and you will not have any jQuery IntelliSense support.

So there’s a little more to know about how exactly jQuery IntelliSense actually works in order to make this work correctly (not to mention IntelliSense support in .ascx files). Luckily Jouni Heikniemi have written a great article about this – check it out:

http://www.heikniemi.net/hardcoded/2009/08/jquery-intellisense-on-asp-net-mvc/

TamperData – security test your web applications

Friday, 5 February 2010 17:26 by mha

Working more and more with ASP.NET MVC I’ve found the Tamper Data Firefox Add-on to be quite useful. With this add-on you can view HTTP/HTTPS headers and browser-requests and (which is really awesome) edit POST parameters.

Use this add-on to security test your web applications by modifying POST parameters and simulate a scenario where a user changes/tampers with information sent to your site, like e-commerce sites where a user tries to buy cheap stuff, hack passwords, etc..

LINQ and PagedList

Tuesday, 26 January 2010 18:04 by mha

If you’re using LINQ you probably know about the .Skip and .Take methods. However often you need to do paging, hence you probably use something similar to this to get around that:

public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        TotalCount = source.Count();
        TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);

        this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
    }


However you don’t need the above code – instead take a look at the CodePlex project called PagedList located at http://pagedlist.codeplex.com/

PagedList allows you to take any List<T> and by specifying the page size and desired page index, select only a subset of that list, there’s also a StaticPagedList if you should need that.

Simply download the source, run “Release.bat” to build a release version and reference the .dll file in your solution, then you can have code like this:

var somePage = list.ToPagedList(4, 50); // fifth page, page size = 50

Using Fiddler with the ASP.NET Development server

Tuesday, 19 January 2010 09:28 by mha

Fiddler does not by default work together with the ASP.NET development server, however you can easily fix this.

In Fiddler press Ctrl+R (Rules) and go to the “OnBeforeRequest” method, here you add a single line:

oSession.host = oSession.host.replace("mhanotebook","127.0.0.1");

The “mhanotebook” is the name of your computer. The problem is that Fiddler works as a proxy and change this settings in IE7 to forward request to fiddler. however IE7 does not forward calls if the url contains localhost.

After applying the above rule, you can now do a request like this: http://mhanotebook:50016/product and Fiddler will “kick in”, nice :-)

Sidebar translation gadget

Monday, 11 January 2010 18:54 by mha

I’ve been looking for a translation gadget and today I (finally) found a good one:

translategadget

It uses Google as the “engine “, supports 43 languages and is free:

http://www.photo-bon.com/en/translator_en.htm

Canon EOS 350D and Windows 7 x64

Saturday, 9 January 2010 19:44 by mha

Having been out a couple of hours today in the snow with my Canon EOS 350D, I discovered that Windows 7 x64 was unable to detect the camera. After some research I found out that if I changed the camera's USB connection mode from "PC" to "Print/PTP", Windows detected the camera without any problems and I had no problem whatsoever copying my images over.

A few of the images…

IMG_3847IMG_3880IMG_3882 IMG_3931 IMG_3946

Enable Hibernate In Windows 7

Wednesday, 9 December 2009 22:05 by mha

My new Windows 7 install would not hibernate (which is really a nice feature, especially when hibernation is awesome fast using a SSD disk).

Anyways, after a bit of fiddling I found the solution – to enable hibernation do the following:

1. Open Command Prompt (with Administrator rights if UAC is enabled).

2. Next, type: powercfg /hibernate on

3. Close the Command Prompt .. you now (maybe!!) is able to hibernate (I was not)

… so if you can’t see the Hibernate option in Start menu then:

4. Launch Power Options from Start menu / Control panel

5. In the left pane, open the link labeled “Change when the computer sleeps” and then open the link “Change advanced power settings”.

6. Under the Advanced Sleep options, expand the Sleep tree and turn off Hybrid Sleep.

… and now in the Start menu you should see the Hibernate entry.