Monthly Archives: July 2008

Create and delete cookies in an iframe with IE

Internet Explorer shines yet again… I had to write a login sync for two sites (Single sign-on).  So that you can log into one site and (behind the scenes) get logged into the other site.  I chose to do this with iframes and all was happy.  Until I had to verify that it worked in IE.  IE chooses to dismiss cookies set/removed through the iframe.  Setting a header on the page that creates/deletes the cookies will remedy this, though. 

Add this to your pages that create/delete the cookies:

HttpContext.Current.Response.AddHeader("p3p", "CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"");

It creates a p3p "compact policy" that gets sent to the client. This convinces IE to accept the cookie in some magical way…

Via articles on Asp.Net Resources and SalesForce

BinaryResult for Asp.Net MVC

I was working with the latest drop of the asp.net mvc framework and had a need to stream a pdf back to the user… A quick glance around the System.Web.Mvc namespace only yielded ContentResult.  While that’s cool for string content, I needed something more flexible for binary data.  So I wrote the BinaryResult:

public class BinaryResult : ActionResult
{
    public byte[] Data { get; set; }
    public bool IsAttachment { get; set; }
    public string FileName { get; set; }
    public string ContentType { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.Clear();
        context.HttpContext.Response.ContentType = ContentType;
        if (!string.IsNullOrEmpty(FileName))
        {
            context.HttpContext.Response.AddHeader("content-disposition",
                ((IsAttachment) ? "attachment;filename=" : "inline;filename=") +
                FileName);
        }
        context.HttpContext.Response.BinaryWrite(Data);
    }
}

You can use this in your controllers to stream any type of binary data as the ActionResult for that controller.  So for my example, I call it like:

public ActionResult Download()
{
    Doc doc = new Doc();
    doc.Read(Server.MapPath("~/Sample.pdf"));
    using (MemoryStream stream = new MemoryStream())
    {
        doc.Save(stream);

        return new BinaryResult
                   {
                       ContentType = "application/pdf",
                       FileName = "Sample.pdf",
                       IsAttachment = true,
                       Data = stream.ToArray()
                   };
    }
}

btw, sorry if I’m reinventing the wheel with this, but Google gave no love when I tried searching for it

Complex SQL conditional statements with SubSonic 2.1

Have you needed to do a complex where clause with a query, but didn’t want to write raw SQL?  That’s where SubSonic expression constraints come in handy.  Say you want to do something like:

select * from Product where IsActive = 1 and (ExpiredOn is null OR ExpiredOn <= ’01/01/2020′)

Using SubSonic 2.1, you can do the following:

DateTime futureDate = new DateTime(2020, 1, 1);

List<Product> products = DB.Select().From<Product>()
.Where(Product.Columns.IsActive).IsEqualTo(true)
.AndExpression(Product.Columns.ExpiredOn).IsNull().Or(Product.Columns.ExpiredOn).IsLessThanOrEqualTo(futureDate).CloseExpression()
.ExecuteTypedList<Product>();

Basically, the AndExpression part translates to a SQL "and" operator followed by a beginning parentheses.  The CloseExpression translates to the closing parentheses.  Theoretically, you could nest these bad boys as deep as you’d like.  Note: SubSonic also comes with an OrExpression as well.

 

Hope this helps

Archiving CMS type data using SubSonic

Rob Conery asked that we share some of the things that we’ve done with the new Subsonic, in celebration of its 2.1 release.  I’ve been using various svn versions of Subsonic for some time now, and feel that I have a decent grasp of its new features. 

One thing that seems to keep coming up is the need to implement an archive table for CMS type data.  So I whipped up some generic utility functions, that make use of the new query engine api, to handle the archive process automatically.  If the latest archive object does not match the object specified, these helpers will automatically rip through all the columns of the object and set the values for a new archive type.  To use this properly, you’ll want to have the same column names in the archive table that you do in your original table.

There are a few assumptions with these utility helpers:

  1. The archive table needs a column CreatedOn that is a datetime – probably could be modified to sort by primary column, but I don’t think this is a major issue
  2. Your model needs to be based on RepositoryRecord – could be modified easily to support ActiveRecord

Hope this helps and if you use this, please include a note for where you found it…

 

To make use of the magic, you would do something like:

DB.Save(somePost, User.Identity.Name); // Save the post as normal
ArchiveHelper.CheckAndCreateArchive<Post, PostArchive>(somePost, User.Identity.Name)

 

And now the helper methods (I put in a sealed class called ArchiveHelper)…

/// <summary>
/// Checks to see if changes have been made and if so, creates an archive.
/// </summary>
/// <typeparam name="T">Type of the item to archive</typeparam>
/// <typeparam name="TArchiveType">The type of the archive type.</typeparam>
/// <param name="item">The latest item</param>
/// <param name="modifiedBy">Username doing the modifications</param>
public static void CheckAndCreateArchive<T, TArchiveType>(T item, string modifiedBy) where T : IRecordBase where TArchiveType : RepositoryRecord<TArchiveType>, IRecordBase, new()
{
    if (HasChanged<T, TArchiveType>(item))
    {
        TArchiveType archive = CreateArchive<T, TArchiveType>(item);
        DB.Save(archive, modifiedBy);
    }
}

/// <summary>
/// Determines whether the specified item has changed since its last archive.
/// </summary>
/// <typeparam name="T">Type of the item to archive</typeparam>
/// <typeparam name="TArchiveType">The type of the archive type.</typeparam>
/// <param name="item">The latest item</param>
/// <returns>
///     <c>true</c> if the specified item has changed; otherwise, <c>false</c>.
/// </returns>
public static bool HasChanged<T, TArchiveType>(T item) where T : IRecordBase where TArchiveType : RecordBase<TArchiveType>, IRecordBase, new()
{
    TableSchema.Table itemSchema = item.GetSchema();

    SqlQuery latestArchiveQuery = DB.Select().Top("(1)").From<TArchiveType>().Where(itemSchema.PrimaryKey.ColumnName).IsEqualTo(item.GetPrimaryKeyValue()).OrderDesc("CreatedOn");
    List<TArchiveType> archives = latestArchiveQuery.ExecuteTypedList<TArchiveType>();

    if (archives.Count == 0)
        return true;

    TArchiveType latestArchive = archives[0];
    TableSchema.Table archiveSchema = latestArchive.GetSchema();

    foreach (SubSonic.TableSchema.TableColumn column in itemSchema.Columns)
    {
        if (IsReservedColumnName(column.ColumnName))
            continue;

        bool containsColumn = false;
        foreach (SubSonic.TableSchema.TableColumn c in archiveSchema.Columns)
        {
            if (c.ColumnName.Equals(column.ColumnName, StringComparison.OrdinalIgnoreCase))
            {
                containsColumn = true;
                break;
            }
        }

        if (containsColumn)
        {
            if (!item.GetColumnValue(column.ColumnName).Equals(latestArchive.GetColumnValue(column.ColumnName)))
                return true;
        }
    }
    return false;
}

/// <summary>
/// Creates an archive of the specified item.
/// </summary>
/// <typeparam name="T">Type of the item to archive</typeparam>
/// <typeparam name="TArchiveType">The type of the archive type.</typeparam>
/// <param name="item">The item used to create the archive</param>
/// <returns></returns>
public static TArchiveType CreateArchive<T, TArchiveType>(T item) where T : IRecordBase where TArchiveType : IRecordBase, new()
{
    TableSchema.Table itemSchema = item.GetSchema();

    TArchiveType archive = new TArchiveType();
    TableSchema.Table archiveSchema = archive.GetSchema();

    foreach (SubSonic.TableSchema.TableColumn column in itemSchema.Columns)
    {
        if (IsReservedColumnName(column.ColumnName))
            continue;

        bool containsColumn = false;
        foreach (SubSonic.TableSchema.TableColumn c in archiveSchema.Columns)
        {
            if (c.ColumnName.Equals(column.ColumnName, StringComparison.OrdinalIgnoreCase))
            {
                containsColumn = true;
                break;
            }
        }

        if (containsColumn)
        {
            archive.SetColumnValue(column.ColumnName, item.GetColumnValue(column.ColumnName));
        }
    }

    return archive;
}

private static bool IsReservedColumnName(string column)
{
    return (Utility.IsAuditField(column) || Utility.IsLogicalDeleteColumn(column));
}