By popular demand...
There have been a couple of items that people kept pinging me about. Here they are:
Cuyahoga
The Cuyahoga Project is a .Net CMS based on NHibernate. It used to be the best application to look for NHibernate patterns for beginners. Now I suggest people should look at this as a great sample app in general, not just for NHibernate patterns.
Among other things, it has been the engine behind ayende.com for the last four years or so. Along the way, it gave me very little trouble, and allowed me to manage my site without much trouble. A CMS, by nature, is an extensible system. But it is a tribute to Cuyahoga (and to what I am doing in the main site) that up until now, I didn't actually have to go into the code to make it do what I want.
I did my usual read-the-code-before-use, of course, but that was about it.
Today, I spent several hours implementing a very simple module in Cuyahoga. This module simply displays and redirect links. You can access the source for this module here, and general instructions about how to build Cuyahoga modules can be found here.
The reason for this post is that I had a change of mind regarding Cuyahoga. Before, I thought about it just as a good application. After, I think about it as a great framework. There is very little need for me to actually do anything in the module except what I actually want, and the overall design is clean, easy to understand and easy to follow. The hardest part was doing the UI, and that is as it should be.
But I do wonder about the name, why Cuyahoga?
The bandwidth problem
Well, looks like I don't have free bandwidth :-) The recent release of Hibernating Rhino #8 has made it clear, since it resulted in a bill.
Now I am looking for a solution for that. We are talking about files on the ~100 - 200 MB range, downloaded tens of thousands of times, over long period of time.
I thought about setting up a torrent server, but I can't find any viable torrent server for Windows, and I don't want to run the torrents from my machine. (I have a server available, if needed)
The other option is hosting it someplace that doesn't mind the bandwidth. Any suggestions?
YSlow: Another Web Developer adding for FireFox
After getting hooked into FireBug, it looks like YSlow it going to be another essential tool. Just the screen shots makes me want to drool...
ASP.Net WTF?! (sorry, blog was down)
All of a sudden, I get an email telling me that my blog is down. I go and check it out, and it is indeed down. With the following error message.
Server Error in '/Blog' Application.
Could not load file or assembly 'App_Web_g5ujsn49, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: Could not load file or assembly 'App_Web_g5ujsn49, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
I tried restarting the appication by renaming web.config, but it didn't work. The only thing that worked was changing the compilation debug mode from false to true, and then to false again.
Search for the error brought this post, and it looks like there my be a hotfix avialable. The problem is that my scenario doesn't match the conditions on the hotfix, so I really don't know what to think. I am going to ping my host and check if there is anything that can cause file locks on the server (anti virus, indexing service, etc), although I would have expect this kind of error to occur much earlier if that was the case.
Code smell
Why does it have to be like this?
if($F('<%= PolicyDescription.ClientID %>')==
'<asp:Literal runat="server" Text="<%$ Resources:PolicyResources, DescriptionNotFound %>"/>')
{
alert('<asp:Literal runat="server" Text="<%$ Resources:PolicyResources, EnterValidPolicyNumberOrDescription %>"/>');
return;
}
Real Developers find the correct abstraction level
Bob Lewis (opps!) Bob Grommes is talking about Real Developers. Since he touches on some of the things that I have talked about recently, I want to make something clear. I don't think that WebForms are bad because you are not manually parsing the query string and form data yourself (to an unprotected buffer, in assmebly).
I like a lot of the things that ASP.Net has to offer. I don't like the attempt to put a Shambling Facade in front of the way things work to make the easy scenarios demoable, and the hard scenarios harder.
Upgrading site
I am going to upgrade my site from Cuyahoga 1.0 to 1.5, the blog should not be affected, but the main site may be down.
Update: I have successfully migrated the site to Cuyahoga 1.5. The process was very painless, and I can see visibile improvement in the performance of the site.
I also fixed the problem with downloads from IE not working correctly. :-)
Moved to Orcs Web
Well, if you are reading this I have successfully moved to orcs web.
The transfer was fairly smooth, except they don't support MySQL. That wasn't an issue, as a matter of fact, since I am using Cuyahoga, it was a simple matter to change the config and make it work with SQL Server. I was worried about transferring the data, but it looks like MySQL has fairly strongly export capabilities, including into formats that SQL Server can consume.
Beyond that, it was a breeze...
Now I only have to wait for the DNS records to update.
Host review: Webhosting4life, Recommendation: AVOID
I am posting this because I am extremely annoyed. I moved from my previous host because of reliability issues, I had to do it fairly quickly, so I didn't have time to really ask around and find out the best host. I went to webhosting4life because it was fast to setup, and I believed that they were big enough to be competent.

I do not have a very big site, or a very complex setup, I run two applications, one needing MySQL, the other needing SQL Server. Neither of which is particularily tasking on the server.
As you can see, I had 6 outages in the last 20 days, some of them lasting mutliply hours. In nearly all cases, the reason that I was given was:
I could accpet it once or twice, but when it go to this level... below is a chat transcript of me and a tech from webhosting4life. It is complete with spelling mistakes and everything. As a result of this, and the evasive answers that I got, I am now on the lookup for a new host.
Notice that the operator simply left the chat when I tried to get additional information about the root cause of the problem. The chat equilent of hanging in my face! They have "Email our CEO" link on the help desk page, which I used to send a question regarding the frequent outages, I never even got a "Thank you for email us".
Evaluating Atlas
Sorry, I meant that I am evaluating Microsoft(R) ASP.Net(TM) Ajax(Patent Pending) Extentions :-).
I mentioned before that I am not really feeling overly excited about that whole Ajax thing. Because of a client constraints, I am unable to use MonoRail (but I use just about everything else from the Castle stack :-) ), so I thought that I would give Atlas a try. This is still very early in the project, but I like to understand how things works so I know why they broke. And for some reason, just about anything breaks when I start playing with it.
At any rate, I took the cascading style list demo for a spin, and managed to hit issues with its error reporting almost immediately. I didn't specify the full path to the service, which meant that it had very helpfully showed me a [method error 500], which I was not pleased to see. Maybe there is a way to get more infomration from it, but I couldn't find it.
Once I got over this hurdle, it was much easier. This is a fairly core scenario, so I wasn't surprised that it was easy, just wire the extenders to the correct controls and write the service methods. Here is my code:
public class NewPolicyRequestService : BaseScriptService
{
private NewPolicyRequestController controller;
public NewPolicyRequestController Controller
{
get { return controller; }
set { controller = value; }
}
[WebMethod]
public CascadingDropDownNameValue[] GetOptionsForPolicy(
string knownCategoryValues,
string category)
{
StringDictionary kv = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
string maybePolicyId = kv["Policies"];
ICollection<PolicyOptions> options = Controller.GetOptionsForPolicy(maybePolicyId);
List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>(options.Count);
foreach (PolicyOptions option in options)
{
values.Add(new CascadingDropDownNameValue(option.Name, option.Id.ToString()));
}
return values.ToArray();
}
[WebMethod]
public CascadingDropDownNameValue[] GetPolicies(
string knownCategoryValues,
string category)
{
ICollection<Policy> policies = Controller.GetPolicies();
List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>(policies.Count);
foreach (Policy policy in policies)
{
values.Add(new CascadingDropDownNameValue(policy.Name, policy.Id.ToString()));
}
return values.ToArray();
}
}
Couple of things to note here, BaseScriptService has all the appropriate attributes [ScriptService], [WebService], [Etc] :-). It is also responsible for injecting the correct controller, which is the one who is responsbile for the actual work. The service is here merely to unpack the data from the request and then translate the returned results to something that Atlas can understand.
So far, very easy. I don't like the fact that I have to duplicate code here, though. There may be a better way, but the only thing that I can think about is to add a template method that accept a couple of delegates, and I don't see that as adding to the maintainability of the system.
The more important concern that I have, though, is what happen when I need to extend this example. Imagine a scenario where I have this:

When a policy is selected, I need to update the child drop down, but I also need to update the policy description. I know how I can do it in MonoRail (easy), and I know how I can do it in using HTML/Javascript (not tough). I have no idea how hard it would be to make it work with ASP.Net/Atlas.*
I figure that I'll probably need to drop the drop down extenders completely (and handle all the updates to the drop downs myself {I have way more than 2, by the way} ), in order to be able to recieve more information than what the extender gives me. More annoying then that, I would have to use the horrible <%= Policies.ClientID %> all over the place, and get the lovely "Control Collection Could Not Be Modified" exceptions when I try to do more advance stuff.**
* I find it sad that I need to seperate ASP.Net from HTML
** Experiance hurts, I suggest avoiding it.
From DasBlog To SubText
My site was down because of DasBlog today. I have no idea why it started to misbehave again, and I don't really care at this point, that was the final reason that I needed to move to SubText. Here is how I did it:
- Put App_Offline.htm page in the blog directory, since otherwise DasBlog would kill the server
- Back up the existing blog.
- Prepare a SubText skin matching my current blog look & feel.
- I then took the DasBlog to BlogML importer here, which is still not ready, and started to play with the code. This is definately something that would require a developer to do, by the way.
I had to give up on the trackbacks because they issues an IsInRole() call and I am not connected to the office at the moment. I took the short way our and sprinkled Thread.CurrentPrincipal = new GenericPrincipal(..) all over the place (seem to reset itself for some reason), and it worked. - I then tried imprted the resulting file to SubText. I have a large blog, and this meant that I had to increase the timeout values. I then discovered that my posts are not legal in SubText, it was warning me about issues with <script> tags in the posts and then failed with some whitespace error that took some time to check in depth. Appernatly I had literal "<<" somewhere, which SgmlReader interpreted as whitespace, and that caused an error when it tried to save that.
- The posts are appearing in reverse order (the oldest are on the top), that is a side affect of the way BlogML worked, by output it to the export file in reverse order, and SubText accepted it in order. The fix:
UPDATE subtext_content
SET DateSyndicated = DateAdded
- The next issues, categories, I am pretty strict about categories, and it looked like the move messed them up, I got mutliply categories with the same name, probably from multiply attempts to import the same set of data, I cleaned it up with:
That still didn't solve the problem, I had an issue with categories that with duplicated names differencing in whitespace, so I found out about them with this query:DELETE FROM subtext_linkcategories
WHERE categoryid NOT IN ( SELECT categoryid
FROM subtext_links )
Note that I only had two duplicates categories overall, so it was easy to find the values. Then, I fixed the duplication with this query:SELECT LTRIM(RTRIM(title)),
MIN(categoryid),
MAX(categoryid),
COUNT(*)
FROM subtext_linkcategories
GROUP BY LTRIM(RTRIM(title))
HAVING COUNT(*) > 1
All that was left was cleaning up the empty categories, using the second query above.UPDATE subtext_links
SET categoryid = ( CASE categoryid
WHEN 452 THEN 460
WHEN 464 THEN 527
WHEN 465 THEN 504
WHEN 477 THEN 508
ELSE categoryid
END )
If you haven't figured it out yet, I really like the ability to run queries on my blog :-) - The next target was the comments, I first run this:
So I could claim ownership of over 600 comments on my blog. Then it was a matter of handling the stats correctly:update subtext_feedback
set isBlogAuthor = 1
where author = 'Ayende Rahien'
One thing that bugs me with the feedback is that there are multiply places that are using "order by id" to sort by date, in my case, this is not the case, it is actually the reverse of what really should happen.UPDATE subtext_config
SET commentcount = ( SELECT COUNT(*)
FROM subtext_feedback
WHERE feedbacktype = 1
),
PingTrackCount = ( SELECT COUNT(*)
FROM subtext_feedback
WHERE feedbacktype = 2
)
- Now, the issues of preserving the old URLs. That one is very important to me, since I have a lot of links directed to my site that I don't want to break. I first had to run a check to see how many duplicate entries name I had, and realized that I had merely 12 duplicate entries, this is something that I can live with. I should explain that the importing process didn't give me a way to correlate between the DasBlog entry and the SubText entry. What I did have was the entry title and date, which should be enough, since I didn't post entries with duplicated names on the same day. I created the following table:
And I run the following little boo script:CREATE TABLE subtext_MappingFromDasBlog
(
Id int identity(1, 1)
primary key
not null,
subtextId int references dbo.subtext_content ( id )
not null,
date datetime not null,
dasBlogId uniqueidentifier not null,
dasblogUrl nvarchar(255) not null
)
Just let me iterate that the above is not production code, it is one off code.import System
import System.Xml
import System.IO
import System.Data
import System.Data.SqlClient
con = SqlConnection("Data Source=localhost;Initial Catalog=SubtextData;Trusted_connection=yes;")
con.Open()
xdoc = XmlDocument()
xdoc.Load("C:\\export.xml")
nsMgr = XmlNamespaceManager(xdoc.NameTable)
nsMgr.AddNamespace("blog", "http://www.blogml.com/2006/09/BlogML")
for node as XmlNode in xdoc.SelectNodes("/blog:blog/blog:posts/blog:post", nsMgr):
id = Guid(node.SelectSingleNode("@id").Value)
url = node.SelectSingleNode("@post-url").Value;
title = node.SelectSingleNode("blog:title/text()", nsMgr).Value;
dateCreated = date.Parse(node.SelectSingleNode("@date-created").Value)
print "Id ${id} url ${url} title ${title} date ${dateCreated}"
using cmd = con.CreateCommand():
cmd.CommandText = """SELECT Id FROM subtext_Content
WHERE Title = @title and year(DateAdded) = @year
and month(DateAdded) = @month and day(DateAdded) = @day""";
cmd.Parameters.AddWithValue("title", title);
cmd.Parameters.AddWithValue("year", dateCreated.Year)
cmd.Parameters.AddWithValue("month", dateCreated.Month)
cmd.Parameters.AddWithValue("day", dateCreated.Day)
subtextId as int = cmd.ExecuteScalar()
cmd.Parameters.Clear()
cmd.CommandText = """INSERT INTO subtext_MappingFromDasBlog
VALUES(@subtextId, @date, @dasBlogId, @dasblogUrl);"""
cmd.Parameters.AddWithValue("subTextId", subtextId)
cmd.Parameters.AddWithValue("dasBlogId", id)
# just the date, not the time
cmd.Parameters.AddWithValue("date", dateCreated.Date)
#trim the http://www.ayende.com/Blog/yyyy-mm-dd/cmd.Parameters.AddWithValue("dasblogUrl", url.Substring(38) )
cmd.ExecuteNonQuery()
con.Dispose()
Now, to get some use out of it...
SubText supports UrlRewriting, with HttpHandlers, so the first thing I needed to do is enable going to a post just by its id. I had to add this to the HandlerConfiguration/httpHandlers section:
Now urls such as /archive/343.aspx are valid.<HttpHandler pattern="(?:/archive/\d+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
Now for the more complex stuff, I wanted to be able to support the following formats.- CommentView,guid,{guid}.aspx
- PermaLink,guid,{guid}.aspx
- NormalizedPostTitle.aspx
- /yyyy/mm/dd/NormalizedPostTitle.aspx
It took a while, but I manage to integrate that into SubText cleanly (an additional handler and changing the web.config), so the old urls should still work. The issue was mainly
Important: If you notice a borkne link please let me know about it.
- Syndication - RSS / Atom - I get a lot more readers on the site than on RSS, but it is obviously very important thing to keep. Nothing is as annoying as a broken link. In this case, dasBlog is using SyndicationService.asmx
- Binaries - I merely kept them in the same place (I am talking about images, attachments, etc.
The reason that I wrote all of that is that I would actaully remember what I did next time that I would have to upgrade / modify the blog.
The last thing that you need to know is how to configure the url mapping:
<HandlerConfiguration defaultPageLocation="DTP.aspx" type="Subtext.Framework.UrlManager.HandlerConfiguration, Subtext.Framework">
<HttpHandlers>
<HttpHandler pattern="login.aspx" handlerType="Page" pageLocation="Login.aspx"/>
<HttpHandler pattern="(?:/admin\/handlers\/BlogMLExport\.ashx)" type="Subtext.BlogML.BlogMLHttpHandler, Subtext.BlogML" handlerType="Direct"/>
<HttpHandler pattern="/Admin/" handlerType="Directory" directoryLocation="Admin"/>
<HttpHandler pattern="/Providers/" handlerType="Directory" directoryLocation="Providers"/>
<HttpHandler pattern="/rss\.aspx$" type="Subtext.Framework.Syndication.RssHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/SyndicationService\.asmx/GetRss$" type="Subtext.Framework.Syndication.RssHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/atom\.aspx$" type="Subtext.Framework.Syndication.AtomHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/SyndicationService\.asmx/GetAtom$" type="Subtext.Framework.Syndication.AtomHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/comments/commentRss/\d+\.aspx$" type="Subtext.Framework.Syndication.RssCommentHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/aggbug/\d+\.aspx$" type="Subtext.Framework.Tracking.AggBugHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="/customcss\.aspx$" type="Subtext.Web.UI.Handlers.BlogSecondaryCssHandler, Subtext.Web" handlerType="Direct"/>
<HttpHandler pattern="(?:/category\/(\d|\w|\s)+\.aspx/rss/?)$" type="Subtext.Framework.Syndication.RssCategoryHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="(?:/ArchivePostPage.aspx)$" controls="ArchivePostPage.ascx"/>
<HttpHandler pattern="(?:/LinkPage.aspx)$" controls="LinkPage.ascx"/>
<HttpHandler pattern="(?:/ArticleCategories.aspx)$" controls="ArticleCategories.ascx"/>
<HttpHandler pattern="(?:/archives\.aspx)$" controls="SingleColumn.ascx"/>
<HttpHandler pattern="(?:/archive/\d{4}/\d{2}/\d{2}/\d+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
<HttpHandler pattern="(?:/archive/\d+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
<HttpHandler pattern="(?:/archive/\d{4}/\d{2}/\d{2}/[-_,+\.\w]+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
<HttpHandler pattern="(?:/archive/\d{4}/\d{1,2}/\d{1,2}\.aspx)$" controls="ArchiveDay.ascx"/>
<HttpHandler pattern="(?:/archive/\d{4}/\d{1,2}\.aspx)$" controls="ArchiveMonth.ascx"/>
<HttpHandler pattern="(?:/articles/\d+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
<HttpHandler pattern="(?:/articles/[-_,+\.\w]+\.aspx)$" controls="viewpost.ascx,Comments.ascx,PostComment.ascx"/>
<HttpHandler pattern="(?:/\d{4}/\d{2}/\d{2}/[\d\w]+\.aspx)$" type="UrlRewriting.For.DasBlog.FromDasBlogToSubText, UrlRewriting.For.DasBlog" handlerType="Factory" />
<HttpHandler pattern="(?:commentview,guid,[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\.aspx)$" type="UrlRewriting.For.DasBlog.FromDasBlogToSubText, UrlRewriting.For.DasBlog" handlerType="Factory" />
<HttpHandler pattern="(?:commentview\.aspx)$" type="UrlRewriting.For.DasBlog.FromDasBlogToSubText, UrlRewriting.For.DasBlog" handlerType="Factory" />
<HttpHandler pattern="(?:/contact\.aspx)$" controls="Contact.ascx"/>
<HttpHandler pattern="(?:/posts/|/story/|/archive/)" type="Subtext.Web.UI.Handlers.RedirectHandler, Subtext.Web" handlerType="Direct"/>
<HttpHandler pattern="(?:/gallery\/\d+\.aspx)$" controls="GalleryThumbNailViewer.ascx"/>
<HttpHandler pattern="(?:/gallery\/image\/\d+\.aspx)$" controls="ViewPicture.ascx"/>
<HttpHandler pattern="(?:/(?:category|stories)/(\w|\s)+\.aspx)$" controls="CategoryEntryList.ascx"/>
<HttpHandler pattern="(?:/comments\/\d+\.aspx)$" type="Subtext.Framework.Syndication.CommentHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="(?:/services\/trackbacks/\d+\.aspx)$" type="Subtext.Framework.Tracking.TrackBackHandler, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="(?:/services\/pingback\.aspx)$" type="Subtext.Framework.Tracking.PingBackService, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="(?:/services\/metablogapi\.aspx)$" type="Subtext.Framework.XmlRpc.MetaWeblog, Subtext.Framework" handlerType="Direct"/>
<HttpHandler pattern="(?:/default\.aspx)$" controls="homepage.ascx"/>
<HttpHandler pattern="(?:/[\d\w]+\.aspx)$" type="UrlRewriting.For.DasBlog.FromDasBlogToSubText, UrlRewriting.For.DasBlog" handlerType="Factory" />
<!-- this needs to be last -->
<HttpHandler pattern="(?:((\/\/default\.aspx)?|(\/\/?))?)$" controls="homepage.ascx"/>
</HttpHandlers>
</HandlerConfiguration>
And you can find the code for the actual URL mapping here.
It is simple, it works, and that is all I care about.
If you haven't guess, this migration was NOT a walk in the park. I hope that I got all the kinks out, but let me know if there is anything that I missed.
Moved to subtext
Around 3AM yesterday I had a nasty surprised when I tried to post something to my site, it was down. Not just the blog, but the whole site.
A quick check with my host turned out that my blog was using 100% CPU for a long period of time, so they had to remove the site. I have some issues with why they didn't bother to notify me when they did it, but that is another matter. After I spoke with them the site went back up, but the blog was down.
That was 10 hours ago or so, and since then I was trying to move everything that I had to SubText as quickly as I can. I have a lengthy post detailing the procedure, but for now all you need to know that I am on SubText, that I think that I like it, and that is it.
Now I am going to do something interesting.
You can expect some problems with the site until I beat it back to working shape.
Wiki is back up
And accessible from http://wiki.ayende.com/, this time on ScrewTurnWiki rather than MediaWiki.
Huge thanks to Papo, which provided me with a nearly complete copy of the old wiki.
The only thing that I am still missing is the NHibernate Course page, now.
2006 site stats

I forgot to mention, this is just for the main site, apperantly the blog is getting 93,358 Pageviews/Month.
Temporarily switching to a new server
Hi,
My hosting company has moved me to a new server, so it might take a while to setup correctly. I am still planning to a new host company, and am probably going to choose webhosting4life.
The move to the new server is a stop gag measure, to bring performance to an acceptable level again. Take into account that this also means that I may not get emails for the next day or so (not a bad thing in itself).
Time to change a host
For the last three years I had my site hosted on Mirshetet, and I couldn't be happier. I had zero headaches from the site, and when I called, I got really good people, who understood what I was talking about. Recently, however, I had a number of serious problems that resulted in unacceptable slow downs and the site being down. (This is disregarding my mistake with no renewing the domain). Just to clarify, I am getting response times from dasBlog in the order of minutes.
The final stroke was trying to go to my website during the DNRTV recording and finding that it was broken. I am currently looking for a new host. My setup is Cuyahoga and dasBlog, which means that I need a .Net (preferably 2.0, but not actually required) and a database server (I am currently running of Cuyahoga on MySQL database, but it is not a problem to change, thanks to NHibernate).
I am currently using roughly 350Mb of diskspace (a couple of MB in the database) and a bandwidth of about roughly 30Gb per month, which is fairly modest.
I have been recommended Reyox, anyone else that I should consider?
Oh, am if you have a good host and a promo code that would give a discount, I would love to hear about it (but I do not promise to do anything about it).
Opps! Site was down
I got a bunch of posts coming, so be ready.
Web Message Box
This is probably a really bad idea, but here it is:
public class WebMsgBox
{
public static void Show(string msg)
{
if(HttpContext.Current==null && HttpContext.Current.Handler is Page)
return;
Page currentPage = (Page) HttpContext.Current.Handler;
currentPage.ClientScript
.RegisterStartupScript(currentPage.GetType(),"msgBox",
string.Format("alert('{0}');", msg.Replace("'","\\'")),
true);
}
}
It can be called from anywhere in the code, not just from a page.
Hidden Network Ad
Check out the right side of the site, there are a couple of job ads there. From Hidden Network (AKA The Daily WTF). At the very least, I would get amusing job descriptions to read.
Hopefully, I'll get paid for it :-)
Where did my file go?
What happen if you are doing something like this in ASP.Net:
On my machine, it goes to: c:\windows\system32\inetsrv\fun.txt
Remember that the current directory in ASP.Net is wherever the server process has decided it should be, if you want the directory that your application is on, use AppDomain.Current.BaseDirectory for it.
Testing ASP.Net UI: WATIR Impressions
I've started looking at writing integration tests for an ASP.Net application. I have a farily complex application with quite a bit happening in the UI layer. Some pages are fairly simple data entry forms, and some contains UI that is scary to just think about (the amount of work it would take). It has long been my belief that it is not worth testing the UI layer. A lot of effort goes into this, and it is usually fragile in the face of changes.
The problem is that without tests is it very easy to break stuff, and I get lost in the amount of pages / controls that I have there already. It took a failure (in the middle of a client demo, of course) to convince me that it is not enough to verify changes manually. I'm currently investigating WATIR, and it looks like it is farily simple to work with it once we learn ruby, the API and the quirks.
Current things that I have issues with are speed, state, enconding, controls naming, popups and alerts.
Speed seems to be an issue, so far I have only tested it in interactive mode, but it looks like it takes quite a bit of time to run. Quite a bit of time means that I can see things happening. Looking at the documentation, I noticed such things as -f (fast), so it may be a debug mode slowdown so I could keep track of what is going on.
State is the issue of what is the current state of the application for the test. For instance, I may want to try updating an entity, and this means that it have to exists, or creating an entity when it has a certain parents, etc. This require a lot more thought than just Unit Tests, since in Unit Tests I either mocks the whole infrastructure layer (and it is not fun, trust me), or I nuke the entire DB between tests. Testing it via ASP.Net is more complex, since I have to take into account such things as caching, etc. This make it a more real test case, but make it harder to write the test. Oh well, at least the secretary wouldn't do it.
Enconding may be a problem. This is still a heresay only, but I understand that ruby has issues with unicode. A lot of the texts that I need to verify in my pages is in Hebrew, so this may be a real problem. We haven't run into it yet, but we are just beginning.
Controls naming is an ugly beast in ASP.Net, you get names like this one "ShowArchive1$dgArchive$_ctl3$_ctl0", and they may change very easily. I really don't like to see them in the tests. I think that using indexes to find the controls is just as evil in any case.
Popups and alerts seems to be a weak point in WATIR, I couldn't get it to work no matter what I did, and eventually I had to resort to this to get a simple confirm dialog to work. Just so you would understand, this opens a whole new process just to send an OK to the window. There doesn't seem to be any way to get the text of the cofirm/alert window. More worrying is the popup functionality, I can't find a way to handle a modal popup nicely. It looks like I would need two tests there, one to handle the code up to the popup and afterward, and another to handle the popup itself.
Some interesting links about WATIR:
- Integrating Ruby and Watir with NUnit - Scott Hanselman getting started on WATIR
- Ruby or Watir tests included in CruiseControl.NET - Setting up CC integration
- New Release of WatirMaker now WatirRecorder - UI to start getting the scripts.
- WATIR Frequently Asked Questions
Don't run websites in IIS from SUBST drives
It looks like this causes an issue. I have a folder that I SUBSTed to another drive, and when I tried to set it up as a virtual directory, IIS just laughed at me, saying that I have no permissions to do that. When I tried it using the real path, it worked like a charm. The permissions were the same in both cases, of course.
Rhino Mocks Mailing List on the site
I just found out that my Rhino Mocks Mailing List (I use google for it) has an RSS feed. It was a matter of a couple of moment to add the RSS to the site, since Cuyahoga has a Remote Content module. This way you can see the last few messages when you are on the page.
XForms
Kurt explains XForms. Check out the examples he gives. This is the first time that I had a look at this, but this is just flat out facinating. Talk about a way to do clean MVC on the web.
I really wish that I could use this to write web applications today (not feasable unless you would like to build site like this cool one).