I have an abstract, generic repository class that encapsulates a lot of logic, and it is able to do that because it knows how to isolate a unique data row, thanks to its KeyCompare property, which is injected into the repository's constructor:
Func<T, T, bool> KeyCompare { get; }
The role of this property is to tell the base class what fields to use to fetch a unique record - sometimes it's just "Id" and KeyCompare can be assigned like this (passed to constructor):
(item1, item2) => item1.Id == item2.Id
However it also gives me the flexibility of using Linq to SQL classes that have keys that span several columns, like this:
(item1, item2) => item1.Field1 == item2.Field1 && item1.Field2 == item2.Field2
And this works very well in the unit tests, but only because I'm testing with a mock repo that uses Linq to Object.
Production code uses Linq to SQL and since I've been told my KeyCompare
property won't behave as expected (because the Func
won't be translated to SQL), that I should be using an Expression
instead. So I started googling around (and apparently Func
won't be translated to SQL), and saw many examples, but nothing like what I'm doing (is that a sign of something?).
The way I'm using the Func
is as follows:
public T Item(T item)
{
return (_items.SingleOrDefault(e => KeyCompare(e, item));
}
I was told to implement the method like this (with KeyCompare
being an Expression
):
public T Item(T item)
{
return (_items.SingleOrDefault(KeyCompare);
}
But this won't compile (.SingleOrDefault wants a Func
, not an Expression
) and, if it did, it wouldn't be using the item value that I need to give it in order to do what I intend.
I'm injecting this Func
into the repo's constructor with Ninject; here's how I bind the KeyCompare
constructor argument:
.WithConstructorArgument("keyCompare", (Func<MockEntity, MockEntity, bool>)((item1, item2) => item1.Id == item2.Id));
So I'm stuck. How do I make this work with Linq to SQL?
The alternative, if I drop this KeyCompare
property in the base class, is to write redundant code every time I need to implement a concrete repository, which I'd like to avoid. With this KeyCompare
property, I only need to write the code that's specific to each repository type to implement a new one, and I like that.
This post has the complete [original - it has changed since] code for the abstract repository class I'm talking about: Can Ninject use an anonymous delegate (func) as a ConstructorArgument?
EDIT
LINQ-to-SQL : Convert Func<T, T, bool> to an Expression<Func<T, T, bool>> has an interesting answer and a pinpoint accurate title (and I really wonder how I could have missed that one in my research). It actually answers this question with an excellent, detailed answer that I'm going to try in a moment.
The usage is now a little weird, since this is a curried lambda. Instead of passing (x,y) => ... you will be passing x => y => ....
I'm sure the solution works (it's quite clever), but it does not explain why passing (x,y) needs a backflip and a triple axel to perform. What's the deal with two inputs? If Linq to Objects can pull this off, what is it exactly that's preventing Linq to SQL from doing the same?