1
votes

I have extension method that has the following signature:

public static class GenericSeeder
{
    public static void Seed<TSeed, TEntity>(this DbContext context, IEnumerable<TSeed> seeds, Expression<Func<TEntity, TSeed, bool>> predicate)
    {
        // code that I'm looking for goes here
    }
}

To have better understanding of what the method is doing, here's how it should be used:

context.Seed<SeedClass, EntityClass>(seeds, (entity, seed) => entity.Name == seed.OtherProperty);

So basically, I am using the predicate to check whether the seed was already applied. However, in order to do a check, I must use Where or FirstOrDefault from Linq to Entities, which takes the following as parameter:

Expression<Func<TEntity, bool>> predicate

So my lambda expression is function of 2 IN parameters (TSeed, TEntity) and 1 OUT parameter (bool). I need to iterate through provided collection of TSeed objects, and for each of those, use that object as parameter for my lambda expression, to generate LINQ 2 Entities lambda expression which has 1 IN parameter (TEntity) and 1 OUT parameter (bool).

Is there a way to do a partial invoke of lambda expression / func to get another lambda expression / func?

3
Although I think I know what you are asking for I am not 100% sure. You can do multi-statement lambdas using something like (x,y)=>{x.Frob();return y==x;} or something like that (compiled fine in my head, may not with a real compiler). This would allow you to internally do stuff with only one property and then the other. I can't guarantee that any linq provider apart from linq to objects will be able to do things with that (and indeed would expect probably not). - Chris
The comparison in the predicate will always be == ? - Raphaël Althaus

3 Answers

1
votes

Through the use of LINQKit to allow expressions to be invoked in such a way that they will be transformed into other expressions the implementation of your method becomes fairly trivial:

public static IQueryable<TEntity> Seed<TSeed, TEntity>(
    this DbContext context,
    IEnumerable<TSeed> seeds,
    Expression<Func<TEntity, TSeed, bool>> predicate)
{
    return context.Set<TEntity>()
            .AsExpandable()
            .Where(entity => seeds.Any(seed => predicate.Invoke(entity, seed)));
}
0
votes

I have no idea what you are doing, but this is how you do partial application in c#:

Func<int,bool, string> twoInFunc= (int a, bool b) => a.ToString() + b.ToString();

int number = 7;

Func<bool, string> oneInFunc= (bool b) => twoInFunc(number,b);
0
votes

I've managed to create solution on my own. I did however use the infamous LinqKit extension library and it's AsExpandable() extension method.

LinqKit can be found here: NuGet link

So this is the implementation that works with Linq 2 Entities:

public static void Seed<TSeed, TEntity>(this DbContext context, IEnumerable<TSeed> seeds, Expression<Func<TEntity, TSeed, bool>> predicate)
            where TEntity : class
            where TSeed : class
        {
            foreach (TSeed seed in seeds)
            {
                Expression<Func<TEntity, bool>> matchExpression = (entity) => predicate.Invoke(entity, seed);
                TEntity existing = context.Set<TEntity>().AsExpandable().FirstOrDefault(matchExpression);

            // Rest of code is omitted as it is not related to the original question.
            // The query above is properly executed by Linq 2 Entities.
            }
        }