|
8:56 AM Monday Dec 31, 2007
Comments: 0
Motivation
I've been wanting to use Castle Monorail for a real project, so last Friday I finally decided to rework my blog to use Monorail and start moving blog content away from Subtext.
Presenting the Content
I wanted to use Monorail but I didn't want to have to rewrite everything I was already getting from Subtext. The blog look and feel was what I really wanted Monorail to take over, so I started researching how the Subtext database schema worked. It's pretty simple, subtext_Content and subtext_Config are the two tables I'm concerned with for presenting the blog content. ActiveRecord is my choice for data access, and HyperActive did the trick in generating all of the ActiveRecord classes needed.
I chose Brailfor the view engine. It uses the Boo language which I'm starting to really like. One of the coolest features is that you can do something like this in the controller:
SubtextContent[] posts =
SubtextContent.SlicedFindAll(0, 10,
new Order[] { Order.Desc("Datesyndicated") },
Expression.IsNotNull("Datesyndicated"));
PropertyBag.Add("posts", posts);
Then in the view you can do this:
for post in posts:
# do stuff here
end
I like how adding it to the property bag doesn't require explicit extraction from the property bag plus casting, you just start using the variable you added.
URL Rewriting
Subtext uses friendly urls through url rewriting. In order to use Monorail to handle the page requests, I needed to intercept the same style of urls. Luckily, Monorail comes ready to rewrite through routing. I figured it would be easy to grab the existing Subtext regular expressions for the urls and plug them into the Monorail configuration but that didn't work for me right away. Using Roy Osherove's Regulator to tweak the regex and got the routing to work like a champ.
log4net Conflicts
I'm not sure which one of these is the imposter, but log4net version 1.2.10.0 in Subtext is different then log4net 1.2.10.0 in the Castle stuff I've pulled off of the build server. Not there's 24k difference in the size of the assemblies. Using the Subtext version of log4net broke the Castle stuff and vice versa. Here are the two assemblies:
log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=227c66cf5503649a //Subtext
log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821 //Castle
I ended up pulling down the last tag of Subtext 1.9.5, adding the log4net from Castle everywhere and recompiling. That did the trick, now they both play well together.
RSS - Let's Use Subtext...
The RSS for this site needed to remain the same for now, I didn't feel like rewriting that for now. This was not as easy as I was hoping. It required modifying Subtext code and recompiling and then making sure the right config settings were in place. The changes I made were to the BaseRssWriter class and the UrlBasedBlogInfoProvider class. I the BlogInfo result of the GetBlogInfo method in the UrlBasedBlogInfoProvider:
public virtual BlogInfo GetBlogInfo()
{
BlogInfo info = new BlogInfo();
info.Author = "Chris Carter";
info.Email = "chris at panteravb dot com";
info.Host = "panteravb.com";
info.Title = "chris carter's web log";
HttpContext.Current.Items[cacheKey] = info;
return info;
}
And in the BaseRssWriter I changed the spot where it writes the description to this:
this.WriteElementString("description", GetBodyFromItem(item));
There was a lot of other code on the original line, something in there was throwing a null reference exception. I didn't feel like figuring out what the problem was so I just removed the offending code. I'm sure that'll bite me back at some point.
Now What
Currently, the state of my blog is that it's pretty much read only. No comments, actually no admin either aside from some pages I'm testing locally. Next will be trying to get the interaction a little more consistent and clean, I'm very much a noob to Monorail, so I'll need to dig deeper into the framework, but so far me likes.
12:35 PM Wednesday Dec 26, 2007
Comments: 0
We had an email thread going on the quality of queries that ActiveRecord and NHibernate can produce. It had some good info it so I figured I'd post it. Start at the beginning.
From: Carter, ChrisJ
Sent: Thursday, December 13, 2007 11:33 AM
To:
Subject: RE: SQL question
It uses a current_timestamp when the end user(me) does a lame query that's similar to select * from [some huge table].
Currently there are 699,463 records in fundmanger.contract_payment.
Let's say i want only results 100 through 300 of all participants, excluding owners, with business type code of 02. I write this C# code:
ContractPayment[] contractpayments = ContractPayment.SlicedFindAll(100, 200,
new Order[] { Order.Desc("Id") },
Expression.Eq("BusinessTypeCode", "02"),
Expression.EqProperty("ParentTaxIdentification", "TaxIdentification"),
Expression.EqProperty("ParentTaxIdentificationTypeCode", "TaxIdentificationTypeCode"));
It produces this SQL(extra columns were removed for clarity):
WITH query AS (
SELECT TOP 300 ROW_NUMBER() OVER (ORDER BY this_.contract_payment_id desc) as __hibernate_row_nr__
FROM contract_payment this_
WHERE this_.business_type_code = '02'
and this_.parent_tax_identification = this_.tax_identification
and this_.parent_tax_identification_type_code = this_.tax_identification_type_code
ORDER BY this_.contract_payment_id desc)
SELECT *
FROM query
WHERE __hibernate_row_nr__ > 100
ORDER BY __hibernate_row_nr__
It orders by the clustered index, and look, no TOP performance hit. All of this for free.
Normally a dba is not going to see a developer's inline queries unless there's an issue, such as performance, or if the dba wrote the query in the first place. So looking this closely to queries coming out of an application will almost never happen.
However, this is pretty good sql code being generated. I'm pretty sure most devs on this list haven't messed around with Common Table Expressions(the WITH clause), so it seems like a pretty big win if you get that for free behind the scenes.
I can write a crappy ActiveRecord query just as easily as I can write a crappy SQL query. Why I'm a fan of ActiveRecord and NHibernate, and other similar tools(SubSonic, LLBLGEN Pro), is it takes me out of the equation of writing sql, executing a query, and then mapping up the results to whatever I'm doing on the backend. Well tested mature tools like NHibernate, are way better at writing sql and mapping up results than I am. And when they're not, I step in and write SQL appropriately to solve the problem.
Also, you should do the order by on a clustered key. Do you know why it uses current timestamp?
From: Carter, ChrisJ - Fort Collins, CO
Sent: Wednesday, December 12, 2007 3:57 PM
To:
Subject: RE: SQL question
It's the top 300, regardless of the rows in the table since in the example I'm looking for everything between 100 and 300. Actually, without putting in that top clause, like in the BOL example, the WITH block selects the entire contents of the table rather than just what you need.
Here's the query plan using TOP
Here's the one using the between:

-----Original Message-----
From:
Sent: Wednesday, December 12, 2007 3:20 PM
To: Carter, ChrisJ
Subject: RE: SQL question
I think TOP is more expensive to use. What will it look like for 100,000 rows? Will it be TOP 100,000?
-----Original Message-----
From: Carter, ChrisJ - Fort Collins, CO
Sent: Wednesday, December 12, 2007 3:16 PM
To:
Subject: RE: SQL question
For those using Castle ActiveRecord, SlicedFindAll does this for you:
GrantAgreement[] agreements = GrantAgreement.SlicedFindAll(100, 200);
Generates sql that looks like this:
WITH query AS (SELECT TOP 300 ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__,
this_.agreement_id as agreement1_2_8_
FROM grant_agreement this_)
SELECT *
FROM query
WHERE __hibernate_row_nr__ > 100
ORDER BY __hibernate_row_nr__
________________________________
From:
Sent: Wednesday, December 12, 2007 2:53 PM
To: Carter, ChrisJ
Subject: FW: SQL question
I think Tham's idea is the prevalent one. New in SQL 2005. The WITH syntax makes it look pretty simple.
The following example from BOL returns rows with numbers 50 to 60 inclusive in the order of the OrderDate.
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber BETWEEN 50 AND 60;
-----Original Message-----
From:
Sent: Wednesday, December 12, 2007 1:32 PM
To:
Subject: RE: SQL question
Thanks!
-----Original Message-----
From:
Sent: Wednesday, December 12, 2007 1:26 PM
To:
Subject: RE: SQL question
You should check the article below. Don't know it will help or not.
Paging Records Using SQL Server 2005 Database - ROW_NUMBER Function
-----Original Message-----
From:
Sent: Wednesday, December 12, 2007 1:24 PM
To:
Subject: SQL question
Someone asked me this, and I don’t think it is possible, but told him I would ask around.
Is it possible in SQL to return a subset of data from a query, like row 100 to row 200? Know you can use the TOP statement to get the first x records, but they want to "page" through the results 100 rows at a time, first 100 rows first time, second 100 rows second time, etc.
10:15 AM Sunday Dec 23, 2007
Comments: 2
Many times I put together some quick and dirty solutions where I need to hit a database for a few queries, simple stuff where NHibernate and/or ActiveRecord are overkill. I came up with a utility that encapsulated many of the common ADO stuff I was doing. I did some refactoring and pulled the methods out into an interface, IDbHelper. Yesterday I took some time to document all of the methods on the interface. I also refactored the interface to use the interfaces in ADO rather than the sql server specific classes I was using(cuz pretty much everything I do is done in sql server). Here's the interface:

This is not ground breaking in any way, shape, or form. But it is simple which is why I use it often for quick and dirty stuff. Essentially it aleviates having to write a bunch of the same code I commonly write.
One thing I find myself doing a lot of, are queries that involve getting the values from a single column and converting them to an array. This usually involves iterating over a resultset or using an IDataReader and spinning through that. Anyway, that's where ExecuteArray comes in. It let's me do this:
string[] firstNames = _dbhelper.ExecuteArray<string>("select first_name from customer");
One thing I see at work from other devs is when a single value needs to be retrieved from the database, either a SqlDataReader is used and the first value returned is plucked out; or better yet, an entire DataTable is filled, and the value is manually pulled out of the first column of the first row. Using the IDbHelper interface this is achieved like this:
DateTime birthday =
_dbhelper.ExecuteScalar<DateTime>(@"
select birthday from customer where first_name = 'Chris'
");
Filling a DataTable should be a one liner, I don't want to know about a connection or data adapter, just get my DataTable, like this:
DataTable data = _dbhelper.ExecuteDataTable("select * from customer");
Anyway, again, it's not meant to be anything than a utility class when the database interaction in an application is minimal, and I just need to execute some one-off type queries.
Click here to download the solution.
6:3 PM Tuesday Dec 18, 2007
Comments: 0
We have a config tool that has a database backing. Essentially we need the basic functionality of Configuration.AppSettings but need to manage the settings in a database with a little web front end. This need is due to the fact that we will not have production access soon, so changing settings like that will not be possible.
The interface for working with appsettings is based around a component and it's settings. So for example, if you have a web app, we consider that a component at a high level. That component may need a setting like a connection string. We would get the settings for the web site like this:
ConfigManager configs = Container.GetService<ConfigManager>();
string setting = configs.Components["mywebsite"].Settings["connectionString"];
Notice that I set a string equal to the Settings indexer. The Settings property above is of type Dictionary<string, ComponentConfigSetting> and component config setting contains the value of a setting among some other stuff. This works because of the use of implicit:
public static implicit operator string(ComponentConfigSetting setting){
return setting.Value;
}
I haven't used the implicit(or explicit) keyword for at least 2 years, probably longer, almost forgot about it, but that came in very handy for this situation.
8:45 PM Sunday Dec 16, 2007
Comments: 0
It's funny. I have very few ideas for solutions to build that would exercise different technologies. I've done some customer applications, some storefront type sites, but none are that good, and usually don't represent a real world example. I'm pushing a lot of different technologies in my latest project and having a blast, it's a good way to exercise different styles. But I need a good reference example that let's me plug and play different technologies to see how they behave.
I almost always end up thinking about how to write a "cool" blog engine as opposed to all of the not cool ones out there(read: sarcasm, a lot of sarcasm). Anywho, this brings up a good point. I like to develop. I like to type code into an editor and see things happen, cool or not, I like it.
I'm not entirely sure where I'm going with the next train of thought, but if you've read this far in this blog then you probably have bigger problems. I like to post code. Subtext and dasblog were not good at posting code snippets the way "I" wanted them posted. That's fine, they weren't built for those needs, and I could build my own extension to do what I want but that's not that fun, remember I like to type the code in the editor and watch it compile :)
Building a blog can be an interesting task. Why? You type your brilliant or not so brilliant ideas into a little textbox, give it a title and/or description and you're done. Right? Not really.
I'm still a noob in the blog-o-sphere. That said, I'm sure I have not pushed the limits of blogging but I have one requirement that would be really cool to have implemented. I like to post code and a simple commentary. I usually have to log into a website, type in my blog post and do some sort of manual intervention, in order to post code on a blog. I don't like opening another app, copying and pasting code, and then having to format it so it looks right on the blog. The other app could be Windows Live Writer or a web browser, it doesn't matter. As a matter of fact, I think I'd like it integrated right into Visual Studio.
I like the existing blog engines, but I don't love them. I need a better one. One that I wrote, that uses Castle ActiveRecord and the rest of the stack. One that uses prototype and scriptaculous.
This post has had a bigger impact on me than I originally thought. Starting a project with the thought of how to finish the project by writing the fewest lines of code. It's actually a good methodology to keeping your code simple, and drammatically reduces the amount of code duplication and unnecessary code.
Anyway, I have no time for this but I'll come up with something, that's my plan
10:28 AM Saturday Dec 8, 2007
Comments: 0
Woo Hoo! Jeff Key has done it again, Snippet Compiler is on CodePlex and supports .NET 3.5. Thanks Jeff.
|
|