Is is feasible to try to convert expression trees between business and data domains?

286 views Asked by At

I have a repository layer that deals with LINQ to SQL autogenerated entities. These eventually get mapped into domain-friendly types on the surface. I'd now like to provide some more sophisticated querying capabilities for the client code, and that client code knows only about the domain object types.

I'd like to implement this with the Query Object pattern (as named in Martin Fowler's Patterns of Enterprise Application Architecture), but allowing the client code to use lambda expressions with domain types. Under the covers, I'd like to convert the domain-aware lambda expression into a database-aware lambda and send this converted expression to the repository for execution against the database with LINQ to SQL.

I currently have a poor-man's implementation that restricts the client's mapping ability to simple properties, but I'd like to open it up a little to more sophisticated querying. I'm not sure how I would approach this with AutoMapper or any other existing mapping tool, nor am I sure OTTOMH how I could do it with homegrown code.

Here's the kind of functionality I'd like:

// Example types to be interconverted...
//    DomainId should map to DataEntityId and vice versa
//    DomainName should map to DataEntityName and vice versa
public class DomainType
{
    public int DomainId { get; set; }
    public string DomainName { get; set; }
}
public class DataEntityType
{
    public int DataEntityId { get; set; }
    public string DataEntityName { get; set; }
}

// And this basic framework for a query object.
public class Query<T>
{
    public Query(Func<T, bool> expression) { ... }
    public Func<T, bool> Query { get; }
}

// And a mapper with knowledge about the interconverted query types
public class QueryMapper<TSource, TDestination> 
{
    public void SupplySomeMappingInstructions(
             Func<TSource, object> source, Func<TDestination, object> dest);
    public Query<TDestination> Map(Query<TSource> query);
}

// And a repository that receives query objects
public class Repository<T> 
{
    public IQueryable<T> GetForQuery(Query<T> query) { ... }
}   

With the ultimate goal of getting something like this to work:

// a repository that is tied to the LINQ-to-SQL types.
var repository = new Repository<DataEntityType>(...);

// a query object that describes which domain objects it wants to retrieve
var domain_query = new Query<DomainType>(item => item.DomainId == 1);

// some mapping component that knows how to interconvert query types
var query_mapper = new QueryMapper<DomainType, DataEntityType>();
query_mapper.SupplySomeMappingInstructions(
                   domain => domain.DomainId, data => data.DataEntityId);
query_mapper.SupplySomeMappingInstructions(
                   domain => domain.DomainName, data => data.DataEntityName);


IQueryable<DataEntityType> results = 
    repository.GetForQuery(query_mapper.Map(domain_query));

My questions are really this, I think:

  1. Is it feasible to create such a mapper, and if so...
  2. Is it feasible to do so with a tool like AutoMapper, and if so...
  3. Is it possible to take advantage of the AutoMapper mapping that I already have that interconverts DomainType and DataEntityType or would I need to explicitly map Query<DomainType> to Query<DataEntityType>?

I ultimately want to do this to have the flexibility of using arbitrary mapping functions that are not necessarily simple object properties.

2

There are 2 answers

0
jeroenh On

IMHO, this design looks pretty complex. Have you investigated the possibility of defining your domain types directly through the ORM? It's what an ORM is designed for, after all... It would certainly open lot's of possibilities without this huge extra effort...

Many people don't know that Linq 2 SQL actually supports POCO style, at least to some extent? If you can consider other ORM alternatives, both NHibernate and Entity Framework have better POCO support though.

POCO allows you to define your domain model directly on top of the ORM, but in a way that your domain classes can be (more or less, depending on the actual ORM) 'persistence ignorant'. As such, you can leverage the ORM's querying capabilities to expose a rich domain querying API to your users.

If you would consider Entity Framework (v4), you might also want to have a look at WCF Data Services

Just my 2 cents...

0
kerem On

If you are going to allow LINQ queries written against ViewModels and execute them in the repository which uses entity models, you also need to checkout LINQ expression trees and expression tree translation. AutoMapper would be of help when translating the objects, but your expression trees would need translation too.

http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx