Wednesday, April 13, 2011

Using Linq to build a complete value for a Flags Enum in C#

Sometimes its useful, when you have a Flags Enumeration to have amember that represents all the possible values, such as this example:

[Flags]
public enum MyEnum
{
Value1 = 1,
Value2 = 2,
Value3 = 4,
All = Value1 | Value2 | Value3
}

But what if if you don't have access to the source code and the enum declaration does not have such a member? It might be part of a library, for instance. You could do something like this:

static readonly MyEnum AllMyEnum = MyEnum.Value1 | MyEnum.Value2 | MyEnum.Value3;

But that has problems, since you've got a bug as soon as the Enum declaration is changed. It is in fact a violation of the DRY Principle, since the list of members of the enum is reapeated at two places. There is a clever solution using Linq though:

static readonly MyEnum AllMyEnum = Enum.GetValues(typeof(MyEnum )).OfType.Aggregate((current, next) => current| next);

What's happening here? We first use Enum.GetEnum to get an array with all the possible values for MyEnum. That right there slays the DRY dragon, since we retrieve the existent list of members rather than redefine it. To OR the list together we first cast it to an IEnumerable and then aggregate it. The Aggregate functions takes a delegate which tells it how to include a new value in the aggregation in process. The starting value for the aggregation is the default for the Type, which is just fine in this case.

I hope this little one-liner is useful for you as it was for me.

Friday, May 21, 2010

Importing Transactions into Mint

Mint is a very nice financial management tool. Unfortunately it lacks an important feature, the ability to import transaction information from a text file. If your financial institution is not supported or if you just want to input a bunch of old transactions you are pretty much screwed, since the only option is to it manually through the UI. Like every good programmer I'm extremely lazy, so there was no way I was going to spend hours and hours mechanically clicking buttons. It took me sometime but I created a way to automate it. It's hacky and ugly, since I put it all together in less than 2 hours, but it works and saved me some time, so I thought I would share it with you guys.

Since the data inputing can only be done though the UI I turned my head to Browser UI Automation. A quick search on stack overflow turned up Selenium, an open source suite of software to record and run Browser UI test cases. It is composed of:
  1. A java server that automates Firefox and can run test cases.
  2. A .Net library that can can communicate with the server, thus automating Firefox from C#. (There are libraries for numerous other languages as well, from Java to Ruby)
  3. A Firefox plugin, that you can use to record a set of browser actions as a test case. It can also export the test case as C# code that uses the library from above.
I came up with the follow strategy to use Selenium to solve my problem:
  1. Record a Selenium test case of me logging into mint and then creating a transaction. I needed to install the Selenium Firefox plugin before doing that.
  2. Export the test case as C# code.
  3. Modify the Selenium code so that the mint user name and password are received as parameters and the transaction create code is in a loop that runs multiple times.
  4. Create a VSTO Excel spreadsheet and embed the Selenium code in it. Add references to it to the Selenium assemblies. Have cells for the username, password and data to be imported into Mint. Have a button that calls code that instantiate the selenium test case class and runs it.
To use the spreadsheet you should do the following:

Prereqs:
  1. Firefox 3.x with the Selenium Firefox plugin installed.
  2. .NET framework 4.0
  3. Excel 2010 (Excel 2007 should work as well but I have not tested it.)

Procedure:
  1. Install the spreadsheet, clicking on setup.exe
  2. Open the spreadsheet and fill the username and password.
  3. Paste the transaction data. The columns here are transaction date, name, category and amount. The values here MUST be exactly what you would type into mint, or else all hell will break loose. The only exception is the date field. If Excel recognizes it as a date, it will work. Notice that this probably mean that you will have to massage you data somewhat.
  4. Open the selenium server. Its a Java executable called "selenium-server", on the same folder as the spreadheet
  5. Click run
The you just have to sit back and watch the thing run. It is not that fast, It creates about a transaction per second or a bit less, but that sure beats entering them manually. I've also seen a selenium call fail for no good reason. If this happens use the Firefox window to look at Mint and figure out the transaction that was being created when the failure happened and restart the process from that transaction.

Here's the "executable". It includes the Excel spreadsheet and everything you need to run it including the selenium-server and dlls, bur excluding the prereqs noted above.

And here's the source code. Once again, includes all the dependencies.


Let me know if you find it useful! :-)

David

Update 1: Updated the executable file with a properly "published" xlsx file. The older file would not run because of security problems.

Wednesday, March 31, 2010

Debuging XSLT from a string in .NET

It seems that the more cool and powerful a language is, the worst are its debugging facilities. JavaScript was like that some years ago and XSLT is like that right now. That you can debug such an exotic thing as XSLT at all is a surprise, but once you get a taste of the F10 fruit, you can never have enough.

Right now if you go into your C# project and do a transform like:

string xslt, xml, html; //Suppose these have meaningful values
XslCompiledTransform tranform = new XslCompiledTransform(true);
tranform.Load(new XmlTextReader(new StringReader(xslt)));
tranform.Transform(
new XmlTextReader(new StringReader(xml)),
new XmlTextWriter(new StringWriter(html)));

You cannot debug it. The debugger wants a file it can look at, and there isn't any, since you are getting the xslt from a string. It's a clearcut debugger limitation, but how can we workaround it?

Well, after much research I think there is no way, unless ... you can change the code. In that case you can write:

string xslt, xml, html; //Suppose these have meaningful values
XslCompiledTransform tranform = new XslCompiledTransform(true);
string tempFileName = Path.GetTempFileName();
File.WriteAllText(tempFileName, xslt);
tranform.Load(tempFileName);
tranform.Transform(
new XmlTextReader(new StringReader(xml)),
new XmlTextWriter(new StringWriter(html)));

Voila, now VS will happily let you debug the xslt, because it has a file to look at - the temporary one. It sucks to create the file and it is certainly impractical to leave it on on a production system. You can use it while debugging and later on remove it, or you can use the code in a #if debug statement, that will revert back to the correct implementation on release builds.

You you have found a better way to debug xslt from a string, let me know, and until then happy xslt-ing.

A diary of problems and solutions

It just hit me. Often times I spend 2, 4 hours or even a day killing myself with one little issue in my programs. Be it a documentation snafoo on the libraries, a problem with an algorithm, or a misconception about a language feature. What's common among those is that they are very obscure corner cases, ones that don't even get solved by the best web search available. If the answers were spelled out in the web somewhere, I could have spent those investigation hours being productive and writing code.

I can't prove it, but I have a feeling that most people have the same problems and they usually solve them - I usually do - , because everything always works out in the end. But that does not help anyone else, because that solution becomes a lunchtime anecdote at best. So here's my tiny contribution to solve that. Every time that I get stuck with something, I'll post here on how I detangled myself.

With enough luck the search engines will do a good enough job of surfacing those little gold nuggets of information to those in need.

I don't expect this post to be particularly useful to anyone, just to give some context, in the case this collection of random problem and solutions ever get some page view love, so let's keep it short and wait for the first nugget :-)