views:

63

answers:

3

I'm trying to dynamically build a LINQ query for LINQ to Entities so that I can avoid repeating the same function twice.

Here is what I'm trying to do:

private IUser GetOrUpdateUser(Predicate<IUser> filter, Func<IIdentityProvider, UserRecord> fetch)
{
    var user = (from u in this.adapter.Users
                where filter(u)
                select u).SingleOrDefault();

    if (user == null)
    {
        // User not found.  Add him.
        user = this.adapter.AddUser(fetch(this.idenityProvider));
    }
    else if (user.IsExpired)
    {
        // User found, but expired.  Update him.
        this.adapter.UpdateUser(user, fetch(this.idenityProvider));
    }

    return user;
}

protected IUser GetUserByNetworkId(string username)
{
    return GetOrUpdateUser(
        u => u.NetworkId == username,
        i => i.GetUserByNetworkId(username));
}

protected IUser GetUserByEmail(string email)
{
    return GetOrUpdateUser(
        u => u.Email == email,
        i => i.GetUserByEmail(email));
}

The filter parameter is throwing this exception:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities

I could potentially do this:

protected IUser GetUserByNetworkId(string username)
{
    return GetOrUpdateUser(
        from u in this.adapter.Users
        where u.NetworkId == username
        select u,
        i => i.GetUserByNetworkId(username));
}

protected IUser GetUserByEmail(string email)
{
    return GetOrUpdateUser(
        from u in this.adapter.Users
        where u.Email == email
        select u,
        i => i.GetUserByEmail(email));
}

But, that is not as clean.

Any suggestion?

A: 

You can do this, but you need to use, e.g., Expression<Func<..., not just Func<....

Expression<Func<User, Bool>> MakePredicate(int id)
{
    return u => u.Id == id;
}

void DoStuff()
{
    Expression<Func<User, Bool>> pred = MakePredicate(123);
    User u = Context.Users.Where(pred).Single()
}

Note that interfaces won't translate to L2E. So you must use User, not IUser (or the like).

Craig Stuntz
What syntax would I then use in the `where` clause?
John Gietzen
I added a code example.
Craig Stuntz
+1  A: 

You can combine the two LINQ syntaxes:

private IQueryable<IUser> BuildQuery(IQueryable<IUser> users, string userName)
{
    users = users.Where(u => u.UserName == userName);
    return users;
}

Then when you call it:

var query = from u in this.BuildQuery(this.adapter.Users, userName)
            select u;

Hopefully this will point you in the right direction!

GenericTypeTea
A: 

There are two notable projects on this.

  1. LINQ Expression Builder, and
  2. LinqPad's LinQkit which is open source. The PredicateBuilder class demonstrates how to build Linq Query expressions on the Fly.

these references might guide you.

For your immediate use, I suggest Predicatebuilder.

Bhuvan