How to do postbuild tasks painlessly in Visual Studio

So, you’d think you’d need to write custom msbuild targets or even learn msbuild syntax. Wrong!

(okay, some of this can be done via the project properties, the “Build Events” tab. The “Build Events” tab hides the fact that you are writing msbuild code and now you have batch file code embedded into a visual property page instead a file that can be check into source control, diffed or run independently.)

You will have to edit your csproj file.

1. Create a postbuild.bat file in the root of your project. Right click for properties and set to “always copy”. A copy of this will now always be put in the bin\DEBUG or bin\RELEASE folder after each build.

2. Unload your csproj file (right click, “Unload project”), right click again to bring it up for edit.

3. At the very end, find this:
<Target Name="AfterBuild">
<Exec Command="CALL postbuild.bat $(OutputPath)" />
</Target>

The code passes the \bin\DEBUG or \bin\RELEASE path to the batch file. You could pass more msbuild specific variables if you need to.

Strangely, the build output window will always report an error regarding the first line of the batch file. It will report that SET or ECHO or DIR or whatever isn’t a recognized command. But the 2nd and subsequent lines of the batch file run just fine.

From here you can now call out to powershell, bash, or do what batch files do.

Javascript Intellisense, Pretending to Compile JS

So Visual Studio 2010 is reporting everything has the same methods, the methods of the JavaScript Object.

Getting Intellisense to work
1) Maybe the Telerik ScriptManager stomped it. The ScriptManager has to be an Asp:ScriptManager and nothing else. (As of VS2010)
2) Maybe VS wants a Ctrl-Shift-J (manually force a JS intellisense update)
3) Maybe there is a “compile” error.
– Check the Error List tab and look at the yellow warnings.
– Check the General output window, especially after doing a Ctrl-Shift-J
– Try to reformat the code. If it doesn’t reformat, Visual Studio probably can’t “compile” and doesn’t know what to do
– Look for green squiggles
4) Maybe the JS wants to be in its own file. I’ve seen broken intellisense start working after the code was moved from an aspx to a .js file
5) Maybe the the annotations (the fake reference at the top of a JS page) are in the wrong order. For example, if you are working with Telerik, the Ajax reference should be first, Telerik stuff next, your own code later and it should be in order of dependency.
6) Maybe you haven’t added enough annotations (especially the fake “references”, but also summary, param, returns annotations)
7) Maybe you used a golden nugget i.e. <%= Foo() %> and put it in a JS block (on an ascx or aspx page of course.) The VS Javascript parser see this as JS and tries to treat it as malformed JS. When you can cheaply quote it, quote it.
- e.g. var foo =”<%= Foo() %>“; // Just a string.
- e.g. var bar =parseInt(“<%= Bar() %>“,10); //Convert to int
- e.g. var bar =”<%= TrueOrFalse().ToLower() %>“===”true”; //Convert to bool
- but maybe/maybe not e.g. eval(“<%= GenerateJS() %>“); //This isn’t a nice solution because you are doing an unnecessary, expensive, logic changing eval just to keep intellisense from breaking.
8) Watch this space, I still haven’t gotten page method intellisense to show up reliably.
9) ScriptMode appears to affect intellisense. ScriptMode=”DEBUG” has better intellisense, but literally 1000x worse performance for browser execution, especially on IE.

And an mistake to avoid especially for ASP.NET developers
<%= Foo() %> syntax does not work in a .js file. .js files are static and not processed by the ASP.NET templating engine.
JS values written to the screen are initial values. Once they are written, they might as well be static. The JS is code generated on the server, but executed on the client.
var now = ‘<%= DateTime.Now.ToString() %>‘ ; // This isn’t going to change.
If you call page methods, they return immediately, the call back happens a few seconds later.
If you page methods blow up, Global Asax will not get an error event, so you have to use try/catch in your Page Method.
If a page method blows up, it may start erroneously reporting “Authentication Failed” errors. I think this is some version of a WCF style logic, where a client can go into a “faulted” state and just refuse to behave there-after. Still a theory.
On a single page application (SPA), var === Session. In a multi-page ASP.NET application, you constantly store state in Session because values don’t live past the life of a page. In a single page application, your user doesn’t change pages. So a page variable is Session. It never times out.
All parameters of your page methods are user input. In server side programming, you might grab a value from the database, store it in Session and use it later to save a record. In the SPA scenario, that value is handed over to the user and they can change it before it is submitted back to the page method. The level of difficulty is not especially high. So as values pass from server to JS page and back, they will have to be re-validated. Even if you try to keep the values on the server alone, eventually the user will be given a choice of values, and on the page method, you’d want to validate that these values were on the list.

Mercurial and .hgignore

The .hgignore file must exist before the /bin/ /obj/ etc files exist. If you edit the .hgignore file after the /bin files have been created, you have to tell tortoise to forget the files. In my case, it was easier to completely empty out the folder by moving all the files outside of the repository folder & then re-adding them– this time with the .hgignore file in place.

The file needs to be at the root of the repository. This is an unlikely cause of the problem. It isn’t clear if putting the .hgignore file in every @#$@%^ folder helps.

There are about six ways to exclude /bin/,

glob:/bin
glob:*/bin/*
glob:bin/
regex:\/bin\/
glob:\/bin\/

And so on. God knows which one actually works, it isn’t clear if putting every @$!@% version in the .hgignore file will trigger an ignore.

Anyhow, I never had this problem with Ankh and Tortoise SVN. Tortoise SVN understood windows and Ankh understood .NET projects. The corresponding mercurial products don’t seem to understand either, and seem reluctant to ignore.

Personally, I’d rather a version control tool that was reluctant to ‘add’.

Sign the Pledge! Sign the Petition! Down with #region!

I beseech the developer community to swear to never use #region or #endregion blocks again, except for possibly machine generated code.

The first step is to stop typing #region and #endregion. This will greatly improve the transparency of your code. Next, because you are the sort of person that uses #region and #endregion to hide the code you are ashamed of– go find all those HACK:, BUG:, and CRAP: comments.

Targeting 1.1 with VS2005

Why would we want to? All truly difficult questions will at first immediately ellict dismissals and unhelpfully overly simple advice.

Why not just move to 2.0? Upgrading to 2.0 usually is effortless, unless: your organization has burdensome rules about doing anything, like upgrading to 2.0.

Embedded databases for speed and simplicity

(blog entry brought to you by time snapper.  This entry would have been lost to an electricity outage if not for my screen recorder.)

When I want a relational database, I might want something that can handle a billion transactions per second, never loses data, and can be updated by 1000 users simutaneously without corrupting data.  If my application just needs a single small table, the overhead of a relational DB is overkill.

[likewise for ETL scenarios-- writing SQL based ETL code in a full-blown RDBMS for an ETL process incurs a lot of overhead that just slows data processing down.  On the otherhand, dropping back to using text isn't very good either as you forego the power of SQL]

In particular I want an embedded database that is:

  • free
  • works in ASP.NET & ADO.NET
  • supports all important SQL statements
  • is very fast
  • does’t require setting up a secure service (i.e. daemons or windows services)
  • Support for bi-directional databinding in .NET
  • Doesn’t require an installer/COM component registration, etc. i.e. nothing that would be a barrier on a hosted account
  • Runs in medium trust
  • Secure against executable code (should be able to call OS shell functions like one can in MS-SQL, MS-Access and the like)
  • Can zip up the data with the source code and send in an email

MS-SQL 2005, MySQL, etc

These require an install, administrator rights to set up, some non-trivial know how to configure the database and users.  Nope.  Too much work.

Text and Excel

Database drivers for text files tend to be pretty primitive, low performance and often don’t support updating.  Excel has limitations on the number of rows you can deal with.

MS-SQL SQL Compact/Mobile Etc

Doesn’t allow for hosting inside of an ASP.NET worker process. End of story.

XML

Not supported for bidirectional databinding with default ASP.NET controls.  I’m pretty sure this works in medium trust

MS-Access/Fox Pro

If MDAC is available, you probably can use the Microsoft.Jet.OLEDB4.0 driver. 

Berkley DB

Berkley DB was bought by Oracle.  Oracles distribution is slanted towards support of the Java world, but a .NET driver does exist.

SQLite

See my blog entry on SQLite.  Of all my options, this is the one I liked the most.  It runs well in ASP.NET, can support large numbers of users, has Visual Studio support (better in the full version than the express versions).