John Pool
John Pool

Reputation: 31

How to convert from Expression<Func<DomainType>>predicate to Expression<Func<DTOtype>predicate

Some background: I have in my application multiple layers, two of which are a domain layer and an infrastructure layer which serves as my DAL. In the domain layer, I have implemented a generic repository pattern as such:

    public interface IRepository<T, in TId> where T : IEntity<TId>
    {
        void Insert(T entity);
        void Delete(T entity);
        IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate);
        IQueryable<T> GetAll();
        T GetById(TId id);
    }

In my DAL I have a generic DAO pattern implemented as such:

    public interface IDao<TEntity> where TEntity : class
    {
        IQueryable<TEntity> Select();
        IQueryable<TEntity> GetAll();
        IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> predicate);
        TEntity GetSingle(Expression<Func<TEntity, bool>> predicate);
        TEntity GetFirst(Expression<Func<TEntity, bool>> predicate);
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Attach(TEntity entity);
    }

I have a domain class that represents, in business terms, an individual. I have a similar individual object in my DAL layer that is used to represent the object in my database. I have a class called EntityFrameworkDao that implements my IDAO interface and is responsible for all of the actions found in that DAO interface (CRUD and some other actions as described above).

I am trying (days of trying) to find a way to map the expression that is used in the repository to the expression used in the DAL. The specific example is this: I have a generic DomainRepository that implements my IRepository interface (see above). The SearchFor method looks like this:

        public IQueryable<TDomainEntity> SearchFor(Expression<Func<TDomainEntity, bool>> predicate)
    {
        var convertedExpression = **SomeMagicFunctionToConvertExpressions**();
        var dataEntities = _dao.Where(convertedExpression);
        return AutoMapper.Mapper.Map<IEnumerable<TEfEntity>, IEnumerable<TDomainEntity>>(dataEntities).AsQueryable();
    }

I need to figure out what SomeMagicFunctionToConvertExpressions is so that I can convert the predicate in my domain layer to something that the Where method can understand in my DAL class that implements IDao:

        public IQueryable<TEfEntity> Where(Expression<Func<TEfEntity, bool>> predicate)
        {
            return _context.Set<TEfEntity>().Where(predicate).AsQueryable();
        }

I have tried using Automapper's CreateMapExpression as found in this article: AutoMapper for Func's between selector types

But that only tells me how to convert between Func<DomainType,bool> predicate to Func<DTOType,bool> (predicate), not Expression to Expression. I am looking for a way to convert something like this:

Expression<Func<TDomainEntity, bool>> predicate

To this:

Expression<Func<TDAOEntity, bool>> predicate

I thought I was on to something after I found this article about mutiating expression trees, but it won't allow me to pass in a compex linq expression that contains && or || or anyting more complex than a simple i => i.id.Equals(12345) type query.

I am using Automapper, so any solutoins using that would be great, but I am open to any ideas at this point. I really am stuck and have been researching this for days now. It seems like a fairly common task: convert a query based on a type from one layer in the architecture to a type used in the DAL.

Thanks in advance for your help.

Upvotes: 3

Views: 1326

Answers (1)

Tom
Tom

Reputation: 511

You should probably not be doing this, it looks like you are over-complicating things.

First: if you use a generic repository pattern you are basically violating the TDA principle; you are creating a direct dependency to your storage implementation. It is fine to have an "all","by Id" and "full text search" on your generic repository, but any others should be hidden by your interface. A simple example:

repo.search(x=>x.IsActive==true)

Let's assume this is a simple flag, and somehow later your domain changes somewhere, and you decided that an item can be soft-deleted as well. This would imply you need to change your code everywhere in the domain from the former to this:

repo.search(x=>x.IsActive==true && x.IsDeleted == false)

The proper way to do this, would be having a proper method on your repository like this

repo.ActiveItems()

This makes sure you do not spread your behavior everywhere.

Next, if you are exposing a generic search, why not use Linq directly in your domain model; after all, you are still implementing a layer over another layer of your sql objects. Could you explain me the added value of adding these extra layers? After all, they are still tied to same implementation (even though their might be some extra name/value conversion in there).

Upvotes: 2

Related Questions