Wednesday, January 14, 2009

Rendering an ASP.Net Page twice???

Not to long ago, I found myself in a peculiar situation which I will explain shortly. Essentially I found myself needing to render an ASP.Net Page, modify an attribute or two and then re-render. I know you must be asking yourself why in the world would you want to do this? Let me explain first why I needed to do this, then we can look at why this doesn't work and how you can hack around the Page model to get it to work.

The web application I've been working is a LOB application, and printing forms is very important. Furthermore these forms will be provided to our customers customers so we needed to have the utmost control over the form. IE doesn't give you much options when controlling the output. For example using standard methods there is no way to tell IE not to print out the url on the page. The user can modify this setting but relying upon the user to modify their page settings on every print reduces the usability of the application.

What's a poor web developer to do? PDF's are a portable format and we can have a lot of control over it. Now the challenge became do we learn a whole new document model or can we find a tool to convert Html to PDF's. We opted for converting HTML to PDF for a couple of reasons including:
  1. We is actually I, and I didn't want to waste any time learning a new document model. My current employer is a small start up and we just don't have the time to waste.
  2. Many of our forms are displayed to the user in a preview mode. So had we gone with not converting the Html we'd have to synchronize our changes, again the "we" is actually "I"...

We create the PDF's by overriding the page's Render method. We call a method RenderHtml() which looks like:


protected virtual string RenderHtml(string baseUrl)

{

RemapImageUrl(baseUrl);

StreamWriter sw = new StreamWriter(new MemoryStream());

HtmlTextWriter writer = new HtmlTextWriter(sw);

base.Render(writer);

writer.Flush();

StreamReader sr = new StreamReader(sw.BaseStream);

sr.BaseStream.Position = 0;

return sr.ReadToEnd();

}


The RemapImageUrl() provides absolute paths to the images in the document. This could also be done by setting a base; however, in my exact example I had to change the actual URL to get some dynamic images. After we call this method we take the Html and send it to a Html to Pdf converter which then in turn send to the client.

This was working without any issues until like all solutions, a new business requirement came in. We had a form that we sometimes wanted to print a single copy, but in other cases we wanted to print two copies. The second copy would be identical to the first save for a watermark.

I had done something simillar by rendering smaller portions of page indepedantly but in this case, I had a Gridview which requires a form, and needless to say I was trying to jump through so many hurdles guessing at a magic combination of controls and method calls to make this happen

The solution I decided upon was to render my page twice, create two PDF documents then combine them. I know I could have probally done some string parsing, but the documents and format was very fluid at the time, and I simply needed to find a solution and quick.

I figured I could use roughly this code:


string firstDoc=RenderHtml();

EnableWatermark(); //This method turned the water

string secondDoc=RenderHtml();

PdfConvert.ConvertFromHtml(firstDoc)

.AppendPdf(PdfConverter.ConvertFromHtml(secondDoc));


After firing this up, I got an error indicating that a form was already on the page, and there could only be one form. Opening reflector to see what was going on made me cry as I realized what ASP.Net was doing. Now I realize at this point I am far beyond what anyone would consider acceptable use...but lets look real quick why it doesn't work.

Open up reflector and look at the HtmlForm.RenderChildren method this basically calls the Page.OnFormRender then Page.BeginFormRender followed by rendering it's children. It finishes by calling Page.EndFormRender and Page.OnFormPostRender. So let's start with Page.OnFormRender.

This method looks like:

internal void OnFormRender()

{

if (this._fOnFormRenderCalled)

{

throw new HttpException(SR.GetString("Multiple_forms_not_allowed"));

}

this._fOnFormRenderCalled = true;

this._inOnFormRender = true;

}



This is where we erroring on our second call to the Page's render method. This method makes sense, essentially it ensures there is only form by tracking this at the page level in the _fOnFormRenderCalled variable. Now in the FormPostRender event they are reseting the flag which tracks if a form is activley being rendered; however they do not reset the _fOnFormRenderCalled which is correct. The problem is this variable is never set back to false. Even when the page is finished rendering.

So our solution is to manually reset this private variable using reflection:


FieldInfo fi = typeof(Page).GetField("_fOnFormRenderCalled", BindingFlags.NonPublic BindingFlags.Instance);

if (fi == null)

{

LogWriter.LogError("FieldInfo is null verify _fOnFormRenderCalled still exists on the Page object.");

}

fi.SetValue(this, false);

I highly recommend avoiding this, and if anyone has a better solution feel free to share. I'd also write your code defensivley. Well I hope at least someone somwhere finds this helpful, and if you happen to be a dev at Microsoft in the ASP.Net team why not reset these variable before the Page's render method exits.

Thursday, January 8, 2009

Uploading attachments for blog entries on blogger

A request was made to upload some of the source code for a previous blog post. Is this possible to do with blogger? If not where is a good place to do this at? Anyone have any suggestions? Free is critical.

Friday, December 12, 2008

Logging to Database With Enterprise Library Part IV - Tieing it all together

In my last post we created all the plumbing that we needed to configure our logger. The final step is to create the actual logger. As a side note I want to say that tried to use TDD development for this piece; however, I have learned that TDD is not the greatest choice when your building plugins. I might elaborate on this in the future but anyways.

To create a custom trace listner for the Enterprise Library you need to derive from CustomTraceListener. You will then need to implement the TraceData function.

    [ConfigurationElementType(typeof(CustomTraceListenerData))]

    public class DBAuditLoggerTraceListener :

        Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.CustomTraceListener

    {

 

        public override void Write(string message)

        {

            throw new NotImplementedException("This method is not supported");

        }

 

        public override void WriteLine(string message)

        {

            throw new NotImplementedException("This method is not supported");

        }

 

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)

        {

 

            AuditLogEntry ale = data as AuditLogEntry;

            if (ale == null)

            {

                //Not an audit log entry

                return;

            }

            IAuditLoggerExpressionGroup logGroup;

            if (!AuditLoggerExpressionCache.Instance.TryGetValue(ale.AuditCategory, out logGroup))

            {

                //Category not registered

                return;

            }

 

            using (StoredProcedure sp = new StoredProcedure(logGroup.Procedure))

            {

                foreach (string key in logGroup.Keys)

                {

                    sp.AddParameter(key, logGroup.GetValue(key, ale));

                }

 

                sp.Execute();

            }

 

 

        }

 



A couple of things to note, The Stored Procedure class is my own class which I am unable to share; however, all it does is wrap up all the ADO.Net Goodness. It also handles my connection strings which is why I am not passing the connection string in. You should be able to quickly whip up your own ADO.Net code. If you run into issues let me know and I will be glad to help out.

Basically all we are doing is making sure we have an AuditLogEntry object, then we check if we have created an ExpressionGroup for this AuditCategory. If we have we simply loop through each of the keys in the ExpressionGroup, calling the GetValue method.

GetValue was needed to ensure that this class didn't have to know about the type parameter in the Expression Group. I'm hopping C#4 with co/contra variance will remove this requirement. Finally we execute the stored procedure.

The next step is to configure this. If your using an ASP.Net app I'd recommend that you configure your expression Groups in the Global.asax. Also remember to call the Cache() method. If you forget this you will not save the expression group you have created.

The final step it to add your logger to the EntLib configuration for example:

<loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="Error" logWarningsWhenNoCategoriesMatch="true">

    <listeners>

        <add  listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging,  Version=3.1.0.0"

              traceOutputOptions="None" type="DBAuditLoggerTraceListener, YourAssembly" name="AuditLogger" initializeData="" formatter="Text Formatter"/>

    </listeners>

    <formatters>

        <add template="Timestamp: {timestamp}&#xA;"

            type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging"

            name="Text Formatter"/>

    </formatters>

    <categorySources>

        <add switchValue="All" name="Audit">               

            <listeners>

                <add name="AuditLogger"/>                   

            </listeners>

        </add>   

    </categorySources>

</loggingConfiguration>



I just noticed I had left the text formatter in there which I have not used. I am not sure if this is required or not.

So after all of this what do we have? A logger that will take any AuditLogEntry and execute a stored procedure, with any number of parameters. I hope you find this useful. Please feel free to provide any feedback positive or negative.

Enjoy!

Update

I have extracted the source and posted online. Please note that this is not tested, so if you run into any issues let me know. When I get caught back up with my day job I will try and finish rounding this out.

Let me know if this link doesn't work. First time I've used skydrive so I'm not positive I have it all setup right.

Logging to Database With Enterprise Library Part III - Expression Cache

In my previous post we started to explore a solution for logging any arbitrary data to the database using the Enterprise Library.

Next on our list of things we need to create our Expressions. Not wanting to create the expressions each time we need to log the data, and not wanting to tie our method for extracting data from the log entry to the log entry, I opted to create what I am calling the AuditLoggerExpressionCache or Expression Cache for short. The expression cache uses the singleton pattern to ensure we only have one cache for our process.

The cache doesn't actually store the expressions directly but it stores a group of expressions, keyed off the AuditCategory.

public class AuditLoggerExpressionCache

{

    private Dictionary<string, IAuditLoggerExpressionGroup> _expressionGroups =

        new Dictionary<string, IAuditLoggerExpressionGroup>();

    private AuditLoggerExpressionCache() { }

 

    public void Add(IAuditLoggerExpressionGroup group)

    {

        this._expressionGroups.Add(group.Name, group);

    }

 

    public IAuditLoggerExpressionGroup this[string key]

    {

        get

        {

            return this._expressionGroups[key];

        }

    }

    #region Singleton Pattern

    public static AuditLoggerExpressionCache Instance

    {

        get

        {

            return singleton.Instance;

        }

    }

    private class singleton

    {

        internal static readonly AuditLoggerExpressionCache Instance = new AuditLoggerExpressionCache();

    }

    #endregion

 

    public AuditLoggerExpressionGroup<T> CreateGroup<T>(string category, string connection, string procedure)

    {

        return new AuditLoggerExpressionGroup<T>(category, connection, procedure);

    }

 

    public bool TryGetValue(string category, out IAuditLoggerExpressionGroup logGroup)

    {

        return this._expressionGroups.TryGetValue(category, out logGroup);

    }

}



I have defined an IAuditLoggerExpressionGroup interface which allows us to access the groups without having to know the generic parameter type. I am using the Generics to improve the syntax since you won't have to cast the AuditLogEntry in your lambda.

public interface IAuditLoggerExpressionGroup

{

string Name { get; set; }

string Connection { get; set; }

string Procedure { get; set; }

Delegate this[string key] { get; }

IList<string> Keys { get; }

object GetValue(string key, object param);

}



A couple of key properties. The name here is the audit category, the Connection is intended to be used to store the connection string, the Procedure is the name of the stored procedure to execute. Keys is the keys to the expression dictionary itself. I am using the keys also as the stored procedure parameter names.

Let's look at the ExpressionGroup:

public class AuditLoggerExpressionGroup<T> : IAuditLoggerExpressionGroup

{

 

    private Dictionary<string, Func<T, object>> _expressions =

        new Dictionary<string, Func<T, object>>();

 

    public AuditLoggerExpressionGroup(string name, string connection, string procedure)

    {

        this.Name = name;

        this.Connection = connection;

        this.Procedure = procedure;

    }

 

    #region IAuditLoggerExpressionGroup Members

 

    public string Name { get; set; }

    public string Connection { get; set; }

    public string Procedure { get; set; }

 

    public Func<T, object> this[string key]

    {

        get

        {

            return this._expressions[key];

        }

        set

        {

            this._expressions[key] = value;

        }

    }

    public IList<string> Keys

    {

        get

        {

            return this._expressions.Keys.ToList();

        }

    }

 

    public AuditLoggerExpressionGroup<T> Add(string key, Func<T, object> expression)

    {

        this._expressions.Add(key, expression);

        return this;

    }

 

    public AuditLoggerExpressionGroup<T> Cache()

    {

        Check.Require(!string.IsNullOrEmpty(this.Name), "You cannot cache something without a name");

        AuditLoggerExpressionCache.Instance.Add(this);

        return this;

    }

    public AuditLoggerExpressionGroup<T> Cache(string name)

    {

        this.Name = name;

        return this.Cache();

    }

 

    public object GetValue(string key, object param)

    {

        return this[key]((T)param);

    }

    Delegate IAuditLoggerExpressionGroup.this[string key]

    {

        get

        {

            return this[key] as Delegate;

        }

    }

    #endregion

 

}



One thing to note I am using the Design By Contract class. This is however optional but if your not using it, be sure to go have a look its a great additional to your programming toolbox. The expression group as you see in the Add method takes a key, and a Func. T is your specific AuditLogEntry class, and we are returning an object.

At this point your unit test should work, and the vast majority of the work has been done. The only thing left is to define and configure the actual trace listener. I will cover this in Part IV of this series.

Logging to Database With Enterprise Library Part II

I've been surprised at how much traffic my little insight into the lack of functionality that the out of the box database log listener has in the Enterprise Library. So much that I figured it was time to share my first pass on creating a log listener that can dynamically pull attributes from a LogEntry and call any stored procedure.

To set expectations this is a first pass solution which took me a couple of hours to come up with. It is not complete by any means, and there is plenty of places that it could use work; however, it is 100% functional and solves the problem I was setting out to solve. I appreciate any and all feedback. If you take this code and improve upon it let me know I'd love to know!

I am currently using Enteprise Library 3.x, so this might have changed with their more recent 4.0 release.

When building this I had a critical to make which was how do we configure this? I wanted to be able to specify two values a parameter for the stored procedure, and a property / method to be invoked on the LogEvent object. I could have used reflection, LCG, code dom...there are lots and lots of options; however, I wanted to choose something that would be fast at runtime (Reflection is out), and that would be quick for me to code (LCG and Code DOM are out).

I opted for using Lambda Expression, which are very efficent at runtime, and which I could code to very very quickly. This is one area for improvement, right now the expressions can be defined in a configuration file; however, this is definetly something that is possible, and that I would love to add.

I built this so that defining the properties via configuration are optional. I've been using Fluent nHibernate lately and I've learned to enjoy the refactoring support that exists in defining your configuration in code. So the pieces that we need to implement are:
  1. A dictionary of Expressions that call properties or methods on a LogEntry object
  2. A trace listener that uses the dictionary to construct a stored procedure call
  3. A log entry object that can tie the trace listener to the expressions

So let's get started. First thing I did was created a unit test to see what kind of interface I needed for my Expression dictionary. This looked like :


[TestMethod]

public void InitalizeExpressionCache()

{

    var cache = AuditLoggerExpressionCache.Instance;

    string category = "UnitTest";

    string connection = "[CutterMain]";

    string procedure = "sp_InsertUnitTest";

    cache.CreateGroup<AuditLogEntryUnitTest>(category, connection, procedure)

        .Add("MyProperty", ale => ale.MyProperty)

        .Cache();

 

    AuditLoggerExpressionGroup<AuditLogEntryUnitTest> grp = cache["UnitTest"] as AuditLoggerExpressionGroup<AuditLogEntryUnitTest>;

    AuditLogEntryUnitTest entity = new AuditLogEntryUnitTest();

    Assert.AreEqual(entity.MyProperty, grp["MyProperty"](entity));

}



AuditLogEntryUnitTest is a simple fake class to test the framework. I suppose you could substitute your own Mock using your favorite mocking framework:

public class AuditLogEntryUnitTest : AuditLogEntry

{

    public AuditLogEntryUnitTest()

        : base("Unit test", "UnitTest")

    {

    }

    public string MyProperty { get { return "MyProperty Called"; } }

}



Next we are going to define an AuditLogEntry class. This should be the base class for all of your auditing log entries. This class is defined as:

public class AuditLogEntry : LogEntry

{

    public AuditLogEntry(string auditMsg, string auditCategory)

    {

        //if (MembershipService

        this.Categories.Add("Audit");

        this.Categories.Add(auditCategory);

        this.Message = auditMsg;

        this.AuditCategory = auditCategory;

    }

 

    public string AuditCategory { get; set; }

}



This class adds an AuditCategory property, which is also added to the LogEntries categories so you can do filtering on it. The AuditCategory is important as it is is used to tie our LogEntries to the Expression Dictionary.

The next post in the series will explore the Expression Cache.