views:

602

answers:

4

It is true what they say about design patterns that they are simply the embodiment of techniques already in general use. I have been using the Active Record Pattern since 1985.

One of the attributes of this pattern is the use of static members in the implementation to perform searches that return collections of the underlying data.

class Customer { 
   static Customer FindCustomerById( int Id ) { ... } 
   static Customer[] FindActiveCustomers() { ... }
}

In many cases where I need much more flexibility I break encapsulation and include a method such as

static Customer[] FindCustomers( string criteria ) { ... }

where one would call it as

Customer[] customers = Customer.FindCustomers( "LastName = 'Smith'" );

Of course this is a hold over from when I used this pattern in C, is clearly not a best practice, and in the wrong hands can lead to SQL injection and other problems.

Is there a suitable pattern or practice that could be applied that would allow the Customer class to become a "criteria" for such a search?

For example suppose I want to find customers whose last name was Smith, I might consider writing an implementation such as:

static Customer[] FindCustomers( Customer customer ) { ... }

to be called as (with the appropriate constructor of course):

Customer[] customersnamedsmith = 
   Customer.FindCustomer( new Customer( "Smith" ) );

Or is it better to create a co-class that would define the criteria?

+1  A: 

Just from working with LINQ, I like the idea of passing in an expression instead of a string. But, perhaps that is just me? I am also not fond of ActiveRecord, as it mixes state and behavior in the same object. Nice packaging, but not a clean separation of model and data access.

I have seen cases where a customer class is passed to active record, but if you are going that route, a repository pattern is much cleaner and does separate the behavior from state. I see nothing wrong, however, with using the investment you already have in active record and passing an object.

If you would like create a criteria class, you will end up with a bit of a strategy pattern, which can make your active record a bit more active than it is today. It is an applicable pattern, however, and will also solve any injection issues.

Gregory A Beamer
A: 

Although it pushes you out of the database level, you could use something similar to a comparator. I don't know C#, so I just fudged it, but you get the gist:

class CustomerLastNameEvaluator : IEvaluate
{
    private Customer _customer;

    public CustomerLastNameEvaluator (String lastName)
    {
        _customer = new Customer (lastName);
    }

    bool IEvaluate.Evaluate(Customer c)
    {
        return (_customer.LastName == c.LastName);
    }
}

Customer[] customers = Customer.FindCustomers( new CustomerLastNameEvaluator("Smith") );
Pesto
A: 

Without a reason not to, I'd provide a method like this:

public static IEnumerable<Customer> AllCustomers()
{
   return Customers.AsEnumerable();
}

If you have reasons not to do this, you need to articulate those reasons and examine them in detail in order to design the right solution.

Robert Rossney
A: 

Take a look at the WWPlatform DataAccess sample on codeplex. It shows an excellent example of providing search specification instances as params albeit through the repository pattern.

Daz Lewis