Friday, December 04, 2009

Generic entity auditing in Linq to Sql

In many enterprise, or even non-enterprise, applications, there is generally a requirement for data change auditing.

For many applications this will come in the form of adding auditing columns to each table in the database. For example the fields, CreateDate, CreateUser, ModifiedDate and ModifiedUser would audit the username and timestamp of record creation and modification in each table.

Of course there are many ways to implement such auditing. In this article I will focus on an approach that I like when using Linq to Sql.

Overview

In general this approach uses a marker interface to decorate domain entities that require auditing and then uses the extension points of the Linq to Sql generated DataContext to do the auditing of the decorated entities. With the use of generics this can be done very simply with a few lines of code. The main benefit being that other infrastructure code - repositories, business logic etc don't need to worry about entity auditing at all.

Implementation

Let's first define our marker interface, which exposes the four auditing fields.
public interface IEntityAudit
{
    DateTime CreateDate { get; set; }
    string CreateUser { get; set; }
    DateTime ModifiedDate { get; set; }
    string ModifiedUser { get; set; }
}

The marker interface is used by the DataContext to determine which entities need to be audited when they are committed to the database. It also exposes the auditing fields so that the auditing implementation can access the fields generically.

Now let's implement the auditing. Fortunately Linq to Sql provides a very convenient extension point in SubmitChanges. SubmitChanges is called once the DataContext has determined the changeset, but before the entities are persisted.

public partial class MyStoreDataContext
{
    public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
    {
        // Poor separation of concerns here with auditing, we'll revisit this very soon
        var auditDate = DateTime.Now;
        var auditUser = HttpContext.Current.User.Identity.Name;
   
        var changeSet = GetChangeSet();

        foreach (var insert in changeSet.Inserts.OfType<IEntityAudit>())
        {
            insert.CreateDate = auditDate;
            insert.CreateUser = auditUser;
            insert.ModifiedDate = auditDate;
            insert.ModifiedUser = auditUser;
        }

        foreach (var update in changeSet.Updates.OfType<IEntityAudit>())
        {
            update.ModifiedDate = auditDate;
            update.ModifiedUser = auditUser;
        }

        base.SubmitChanges(failureMode);
    }
}

The types and entities of changes to be committed are provided in the changeSet.Inserts and changeSet.Updates IEnumerables.
Using the nice .OfType<>() extension to IEnumerable the entities that have auditing (as indicated by the marker interface, IEntityAudit) are iterated to set the creation or modification audit values.
Lastly we call the base SubmitChanges to do the normal Linq to Sql persistence.

That is it! We have now built a generic auditing mechanism into our DataContext.

So how is it used?


Here is the domain entity in the database and the Linq to Sql designer:



Decorate the Product domain entity using the partial class
public partial class Product : IEntityAudit
{
}

Now whenever a Product is committed to the database as an insert or an update the auditing columns will be set.
public class ProductService
{
    public Product CreateProduct()
    {
        var product = new Product();

        using (var dataContext = new MyStoreDataContext())
        {
            dataContext.Products.InsertOnSubmit(product);
            dataContext.SubmitChanges(); // Here is where the Create audit is triggered
        }

        return product;
    }

    public void SetProductName(int productId, string productName)
    {
        using (var dataContext = new MyStoreDataContext())
        {
            var product = dataContext.Products.Single(p => p.ProductId == productId);
            product.ProductName = productName;
            dataContext.SubmitChanges(); // Here is where the Update audit is triggered
        }
    }
}

In my next blog post I will show how the concern for updating the audit fields (date and username) can be separated from the DataContext and extended to support multiple change processors.

kick it on DotNetKicks.com Shout it

Monday, November 23, 2009

Using an interface to reuse Linq queries

I just read a post about convention based Linq querying and thought I would followup with an alternative method for implementation.

One downside that I see with the implementation of building the expression tree is that the entity may not have an Id property. In this case I would definitely want to see a compile error rather than runtime.

So a simple solution may be to decorate the entities with a marker interface and the place the constraint on the extension method generic, such as

    public interface IEntity
    {}
    
    public partial class Post : IEntity
    {
        // Id property is declared in the Linq to Sql designer file
    }

    public static T GetById<T>(this IQueryable<T> query, int id) where T : class, IEntity
    {
        ...
    }

But why don't we take this a step further and remove a bit of the expression clumsiness? If we were to add an Id property to the interface, then the following would be possible.
    public interface IEntity
    {
        int Id { get; set; }
    }
    
    public partial class Post : IEntity
    {
        // Id property is declared in the Linq to Sql designer file
    }
    
    public static T GetById<T>(this IQueryable<T> queryable, int id) where T : class, IEntity
    {
        return queryable.SingleOrDefault(t => t.Id == id);
    }

This implementation won't compile if the query is on an object that doesn't have an Id. Thus making the interface self-documenting and enforced at compile time.

So as an exercise in understanding expressions this implementation is lacking, but the simplicity is certainly appealing to me.

kick it on DotNetKicks.com Shout it

Saturday, October 31, 2009

iTextSharp PDF rendering in a medium trust ASP.Net environment

Rendering a PDF for download or email is a very common task for an e-commerce website. One thing to consider when deciding on a PDF library for rendering is what environment the site is running in. Most ASP.Net shared hosting solutions will restrict their hosted sites to Medium trust to prevent a rogue site from peeking at other sites on the server.

Medium trust environments can cause funny things to happen between the development environment and the production environment. By default the websites created in Visual Studio have Full trust, this can cause security problems after deployment if you haven't setup your development environment to mimic production. A good first step is to add a trust level to your web.config system.web section.

<system.web>
    <trust level="Medium" />
    ...
</system.web>
This will help to find any trust issues while developing.

So to rendering PDF. After trying ReportViewer 2008 in local mode and PDFSharp (both of which are still very good at rendering PDFs), I have found success with iTextSharp. ReportViewer and PDFSharp both require Full trust mode because they use native COM dlls as part of the GDI rendering process. This makes them unsuitable for shared hosting environments unless you can convince your hoster to raise your site's trust level. The PDFSharp Wiki says that release 1.30 solves most medium trust issues and that full support is in the near future, which is promising. My car also has most of it's wheels, but until I put the fourth one on it isn't going to go far on the road.

iTextSharp appears to have less documentation on the web (one of the best being a fairly comprehensive iTextSharp tutorial), but it is just as powerful as PDFSharp or ReportViewer. The best thing about it though is that it can run in Medium trust mode - once a minor change is made to allow partially trusted callers. To make this change download the iTextSharp source distribution (http://sourceforge.net/projects/itextsharp/files/)
Modify the AssemblyInfo.cs file to add the partially trusted callers attribute.
[assembly: AllowPartiallyTrustedCallers()]
Rebuild the iTextSharp assembly and it should be good to go in a Medium trust environment.

kick it on DotNetKicks.com Shout it

Tuesday, May 19, 2009

Unit Testing with Typemock

Unit Testing ASP.NET? ASP.NET unit testing has never been this easy.

Typemock is launching a new product for ASP.NET developers – the ASP.NET Bundle - and for the launch will be giving out FREE licenses to bloggers and their readers.

The ASP.NET Bundle is the ultimate ASP.NET unit testing solution, and offers both Typemock Isolator, a unit test tool and Ivonna, the Isolator add-on for ASP.NET unit testing, for a bargain price.

Typemock Isolator is a leading .NET unit testing tool (C# and VB.NET) for many ‘hard to test’ technologies such as SharePoint, ASP.NET, MVC, WCF, WPF, Silverlight and more. Note that for unit testing Silverlight there is an open source Isolator add-on called SilverUnit.

The first 60 bloggers who will blog this text in their blog and tell us about it, will get a Free Isolator ASP.NET Bundle license (Typemock Isolator + Ivonna). If you post this in an ASP.NET dedicated blog, you'll get a license automatically (even if more than 60 submit) during the first week of this announcement.

Also 8 bloggers will get an additional 2 licenses (each) to give away to their readers / friends.

Go ahead, click the following link for more information on how to get your free license.

kick it on DotNetKicks.com Shout it

Tuesday, April 28, 2009

Comments are the true revealer of a bad coder

Comments that are in the code of the application that you have inherited maintenance of can sometimes very quickly reflect the quality of the codebase. In this case there is obviously a bit of confusion around the subtlety and use of the cast, as and is C# operators. Yes, there is also a grammatical error. And no, I have not removed any code from inside the if block - that is all there was.


if (payment is CreditCardPaymentDto)
{
// For some reason casting this outside the if statement throws and exception
CreditCardPaymentDto creditPayment = (CreditCardPaymentDto)payment;
}

kick it on DotNetKicks.com Shout it

Tuesday, February 17, 2009

Quotes that may indicate your workplace practices waterfall methods


The functional specs will be released to the development team once they are signed off later this week. You can start development next week.

To be fair it was Monday, so I would potentially have up to 4 days as development team lead to prepare the team. </sarcasm>

I guess I'll have to spend the first couple of weeks of development time working out what we are developing...

kick it on DotNetKicks.com Shout it

Monday, January 12, 2009

Don't take shortcuts on script tags!

This problem annoyed me for longer than I would have hoped recently - even though I have seen it before and spent a frustrating time 'fixing' it. I added a script reference (the first for the page) to the head of a html page and lazily wrote it as

<script type="text/javascript" src="scripts/jquery-1.3.1.js" />

Well this broke the ScriptManager, which couldn't hook up the postback methods for the UpdatePanels on the form. Hmm that's odd, even an empty .js file included this way cause a javascript error on page load such as "this._form is null" or "__doPostBack is not defined". And then something twigged from a not-so-distant project and there it was

<script type="text/javascript" src="scripts/jquery-1.3.1.js"></script>

Lesson: Don't take shortcuts when writing script tags!

More explanation of why http://ajaxian.com/archives/why-doesnt-script-work

kick it on DotNetKicks.com Shout it