views:

149

answers:

3

I have a semi complicated question regarding Entity Framework4, Lambda expressions, and Data Transfer Objects (DTO).

So I have a small EF4 project, and following established OO principles, I have a DTO to provide a layer of abstraction between the data consumers (GUI) and the data model.

  • VideoDTO = DTO with getters/setters, used by the GUI
  • VideoEntity = Entity generated by EF4

My question revolves around the use of the DTO by the GUI (and not having the GUI use the Entity at all), combined with a need to pass a lambda to the data layer. My data layer is a basic repository pattern with Add. Change, Delete, Get, GetList, etc. Trying to implement a Find method with a signature like so:

public IEnumerable<VideoDTO> Find(Expression<Func<VideoEntity, bool>> exp)
...
_dataModel.Videos.Where(exp).ToList<Video>()
---

My problem/concern is the "exp" needing to be of type VideoEntity instead of VideoDTO. I want to preserve the separation of concerns so that the GUI does not know about the Entity objects. But if I try to pass in

Func<VideoDTO, bool> 

I cannot then do a LINQ Where on that expression using the actual data model.

Is there a way to convert a Func<VideoDTO,bool> to a Func<VideoEntity, bool>

Ideally my method signature would accept Func<VideoDTO, bool> and that way the GUI would have no reference to the underlying data entity.

Is this clear enough? Thanks for your help

A: 

Perhaps your design goal is to prevent propagation of the data model entities to the client tier rather than to prevent a dependency between the presentation layer and data model. If viewed that way then there would be nothing wrong with the query being formed the way you state.

To go further you could expose the searchable fields from VideoEntity via an interface (IVideoEntityQueryFields) and use that as the type in the expression.

If you don't want to add an interface to your entities then the more complicated option is to use a VideoEntityQuery object and something that translates an Expression<Func<VideoEntityQuery,bool>> to an Expression<Func<VideoEntity,bool>>.

mancaus
A: 

In architectures like CQRS there isn't need for such a conversion at all cause read & write sides of app are separated.

But in Your case, You can't runaway from translation.

First of all - You should be more specific when defining repositories. Repository signature is thing You want to keep explicit instead of generic.

Common example to show this idea - can You tell what indexes You need in Your database when You look at Your repository signature (maybe looking at repository implementation, but certainly w/o looking at client code)? You can't. Cause it's too generic and client side can search by anything.

In Your example it's a bit better cause expression genericness is tied with dto instead of entity.

This is what I do (using NHibernate.Linq, but the idea remains)

public class Application{
  public Project Project {get;set;}    
}

public class ApplicationRepository{
 public IEnumerable<Application> Search(SearchCriteria inp){
       var c=Session.Linq<Application>();
       var q=c.AsQueryable();
       if(!string.IsNullOrEmpty(inp.Acronym))
        q=q.Where(a=>a.Project.Acronym.Contains(inp.Acronym));
       /*~20 lines of similar code snipped*/
       return q.AsQueryable();
 }
}

//used by client
public class SearchCriteria{
 public string Acronym{get;set;}
 /*some more fields that defines how we can search Applications*/
}

If You do want to keep Your expressions, one way would be to define dictionary manually like this:

var d=new Dictionary<Expression<Func<VideoDTO,object>>,
                     Expression<Func<VideoEntity,object>>{
  {x=>x.DtoPropNumberOne,x=>x.EntityPropNumberOne} /*, {2}, {3}, etc.*/
};

And use it later:

//can You spot it?
//client does not know explicitly what expressions dictionary contains
_dataModel.Videos.Where(d[exp]).ToList<Video>();
//and I'm not 100% sure checking expression equality would actually work

If You don't want to write mapping dictionary manually, You will need some advanced techniques. One idea would be to translate dto expression to string and then back to entity expression. Here are some ideas (sorting related though) that might help. Expressions are quite complicated beasts.

Anyway - as I said, You should avoid this. Otherwise - You will produce really fragile code.

Arnis L.
A: 

Thanks for the repliesto both of you.

I'll try the idea of defining the search criteria in an object and using that in the LINQ expression. Just starting out with both EF4 and L2S, using this as a learning project.

Thanks again!

Jim