Monday, April 30, 2007

Getting Started with NHibernate

I just found a good video introduction to NHibernate over at dnrTV created by Oren Eini (aka Ayende), who is an active NHibernate contributer. If you're an NHibernate pro, you probably won't find anything you didn't already know. I usually use Castle ActiveRecord instead of just using NHibernate directly in order to get a sizable productivity boost, provided the project is small enough. When a project is sufficiently large or complex (multiple client applications, large model, difficult mapping, etc), I believe that skipping AR and going directly to NHibernate is the way to go. It helps keep your laying cleaner by reducing coupling of your application services and the persistence mechanism.

Saturday, April 28, 2007

Rendering Binary Data with MonoRail

Sometimes you want to make your MonoRail controller render binary data for a download. MonoRail by default is setup to render HTML via the view engine, but you can change that behavior. I usually put a method like this in my base controller:

        protected void SetupDownload(string filename, string contentType)

        {

            CancelLayout();

            CancelView();

            Response.Clear();

            Response.ContentType = contentType;

            Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");

        }



By calling this method inside one of your controllers, you will change the default behavior of the controller to not perform the layout (CancelLayout()) and to not try to render a view via the view engine (CancelView()). The contentType should be something like "application/zip". It's the MIME type that is reported to the browser. This method also tells the browser that the data is not inline, and that the user should be prompted to download the file with a default filename provided by the filename argument.

So you have changed the default behavior of MonoRail to render something other than a plain vanilla view. All that's left is to write the binary data into the response's output stream. If you already have a stream, then you would do something like this:

        private void CopyStream(Stream from, Stream to)

        {

            byte[] buffer = new byte[_bufferSize];

            int bytes = 0;

            while ((bytes = from.Read(buffer, 0, buffer.Length)) > 0)

                to.Write(buffer, 0, bytes);

        }



            CopyStream(System.IO.File.OpenRead(path), Response.OutputStream);



Or, if you have a byte array (buffer in this example) or something similar, you can do something like this.

            Response.OutputStream.Write(buffer, 0, buffer.Length);



Notice that if you want to have the brower render something inline (e.g. an image), than you can just remove line that adds the Content-Disposition header that is in my example SetupDownload function.

Friday, April 27, 2007

Remove All Tables and Constraints from a Database Using T-SQL

You've had this problem before: You don't want to drop a database completely, but you do want to drop all the tables. Try to drop your tables in the wrong order and you're slapped with an error regarding referential constraints. I've created this script to ease the burden. It first drops all the constraints, and then it drops all the tables. Let me know if this doesn't work on your SQL Server database. Here's a warning for those who didn't bother to read this paragraph:

WARNING: The following script will delete all the tables in your database.

On to the script:


DECLARE @TableName NVARCHAR(MAX)

DECLARE @ConstraintName NVARCHAR(MAX)

DECLARE Constraints CURSOR FOR

 SELECT TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE

 

OPEN Constraints

FETCH NEXT FROM Constraints INTO @TableName, @ConstraintName

 

WHILE @@FETCH_STATUS = 0

BEGIN

 EXEC('ALTER TABLE [' + @TableName + '] DROP CONSTRAINT [' + @ConstraintName + ']')

 FETCH NEXT FROM Constraints INTO @TableName, @ConstraintName

END

 

CLOSE Constraints

DEALLOCATE Constraints

 

DECLARE Tables CURSOR FOR

 SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES

 

OPEN Tables

FETCH NEXT FROM Tables INTO @TableName

 

WHILE @@FETCH_STATUS = 0

BEGIN

 EXEC('DROP TABLE [' + @TableName + ']')

 FETCH NEXT FROM Tables INTO @TableName

END

 

CLOSE Tables

DEALLOCATE Tables



Enjoy. Let me know if this doesn't work for you.