-1

I have a data layer which abstracts away the underlying implementation (Entity Framework) by accepting & returning models that are defined elsewhere. I want to be able to pass in a Func<T, bool> predicate to one of the methods to allow additional clauses to be applied when querying the database.

Since the generic models know nothing of the underlying Entity Framework implementation, I need to convert my Func<T, bool> predicate to a predicate which can be applied against the Entity Framework context.

The generic models & Entity Framework models have exactly the same property names & I already have a class to perform the property value mappings between the two. Is there a way to convert the target type of the generic model delegate to that of the Entity Framework model?

An example of what I'm attempting to do: Given this delegate: func = Func<Schema.Data.Order, bool> I want to convert it to: dbFunc = Func<Models.Order, bool> And apply it to the context: ctx.Orders.Where(dbDel)

I found this post, but I can't piece together how to perform the conversion. Been bumping my head against this the whole evening, so any help would be greatly appreciated!

UPDATE

The original question & requirements seem to have been a bit vague, so I'll elaborate on my implementation & my requirements. The following code samples have been amended to use Expression<Func<TIn, TOut>> & not Func<TIn, TOut>, based on the suggestions from hvd & Alexei.

I have an interface & a set of classes that represent my data layer. These act as a façade over the data source, allowing for different implementations in order to access the database. I want to be able to pass through additional filter criteria as a predicate & apply this to the underlying data source. But since the façade is separate from the underlying implementation, the predicate is defined using the façade model classes. Furthermore, the façade model class properties have the same naming as that of my implementation, so direct property assignments using reflection, for example, is possible.

My façade implementation:

namespace Schema.Data
{
    public interface IDataStore
    {
        public IEnumerable<Order> GetOrders(string custCode, Expression<Func<Order, bool>> exp);
    }

    public class Order
    {
        public string CustomerCode { get; set; }
        public string OrderNumber { get; set; }
    }
}

I then implement the interface in a separate namespace, using Entity Framework to query my database:

namespace Data.EF
{
    // Entity Framework model is in this same namespace

    public class DataStore : Schema.Data.IDataStore
    {
        public IEnumerable<Schema.Data.Order> GetOrders(string custCode, Expression<Func<Schema.Data.Order, bool>> exp)
        {
            using (var ctx = new MyDatabaseEntities()) {
                // TODO: Convert "exp" to Expression<Func<Data.EF.Order, bool>> to pass it in below
                var orders = ctx.Orders.Where(e => e.CustomerCode == custCode).Where(dbExp ?? (n => true));

                // handling the retrieved data & returning result goes here
            }
        }
    }
}
Carl Heinrich Hancke
  • 2,660
  • 1
  • 30
  • 35
  • 1
    `Func dbFunc = order => func(MapSchemaToModel(order));`... or you are looking for some fancier conversion? – Alexei Levenkov Jul 21 '17 at 22:12
  • For EF, to get anything useful, you'd need an `Expression>`, not a `Func`. Which is what's used in the post you link to, but not what you're asking about in your question here. –  Jul 21 '17 at 22:15
  • You want to convert a type into another one that you can´t (or won´t) access? Seems strange to me. – MakePeaceGreatAgain Jul 21 '17 at 22:16
  • @AlexeiLevenkov Something like that will work perfectly well. I'm getting an error though, stating that it can't convert from Models.Order to Schema.Data.Order. This happens on the order parameter passed into MapSchemaToModel. I've also tried adding a cast operator onto Models.Order, but no dice. – Carl Heinrich Hancke Jul 21 '17 at 22:24
  • @hvd I'm happy to change the façade to work with Expressions, but I'll have the same problem performing the conversion. – Carl Heinrich Hancke Jul 21 '17 at 22:25
  • @HimBromBeere The generic models act as my façade to the database. The idea is that I can completely remove Entity Framework & replace it with a different implementation, but the data layer façade will stay the same. – Carl Heinrich Hancke Jul 21 '17 at 22:26
  • @AlexeiLevenkov My apologies, I had the MapSchemaToModel() method mapping the wrong way around. Read the error again & it was clear what was wrong. That did the trick! Thanks a bunch. – Carl Heinrich Hancke Jul 21 '17 at 22:41

1 Answers1

1

If you just need to convert function taking one type to function taking another type use regular composition approach -

  TDest MapSourceToDest(TSource v) {....}

  Func<TSource, bool> func = ...;
  Func<TDest, bool> dbFunc = x => func(MapSourceToDest(x));

Mapping can be done by hand, reflection, libraries or many other approaches - How to map properties of two different objects?.

Note that if you actually need to pass such method to EF you need Expression and not Func - in this case it actually possible to rewrite expression with destination type - How to change a type in an expression tree?

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Thanks for the explanation. As you & hvd suggested, I'll work to rather use Expressions, since this is meant to execute against Entity Framework. It's late here, but I will try this first thing in the morning. Appreciate it. – Carl Heinrich Hancke Jul 21 '17 at 23:00
  • @CarlHancke While EF wants expressions to analyze, this will still not work with EF since it won't know what to do with `MapSourceToDest`. You would need to analyze the expression yourself and construct a different one yourself. But doing so is not trivial. – Dave Cousineau Jul 21 '17 at 23:48
  • @Sahuagin Thank you for the advice, duly noted. I actually ran into this very issue while trying to implement my original solution. As Alexei mentioned though, for EF I actually require Expressions, so I've amended my façade accordingly. With the Expressions, the conversion is done before the EF query, so what gets translated to store language is the EF model expression, so MapSourceToDest is not a problem. – Carl Heinrich Hancke Jul 22 '17 at 11:20