This is an update to my post “C#, Clean up your Linq-queries and lambda expressions", where I got a comment that it will not work other than against Linq to objects. That is somewhat true. It will work but the query will fetch all posts from the database and then apply the where clause to objects in memory. In my case I only needed in memory but I thought, why not provide an example with Entity framework as well. This example will be targetting Entity framework Code first, CTP 5.
The test
[Test]
public void AgainstEfCodeFirstCtp5()
{
using (var dbContext = new StorageContext())
{
DbDatabase.SetInitializer(
new DropCreateDatabaseAlways<StorageContext>());
foreach (var customer in _customers)
dbContext.Customers.Add(customer);
dbContext.SaveChanges();
}
using (var dbContext = new StorageContext())
{
//Will not call the database
var bonusCustomers = dbContext.Customers.Where(new IsBonusCustomer());
//Will invoke call to database
CollectionAssert.AreEqual(
_expectedBonusCustomers.Select(c => c.Id),
bonusCustomers.Select(c => c.Id));
}
}
The sql-query will not be executed against the database until I access the deferred executable collection “bonusCustomers”. The query will look like this (interceptet with the Sql Profiler).
SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Customers] AS [Extent1]
WHERE
(
(0 = [Extent1].[NumOfYearsAsMember])
AND
([Extent1].[CashSpent] >= 3000)
)
OR
(
([Extent1].[NumOfYearsAsMember] > 0)
AND
(
(
[Extent1].[CashSpent] /
[Extent1].[NumOfYearsAsMember]
) >= 5000
)
)
It matches our rule of the c-sharp code, but the generated Sql will of course brake, since it will give a divide by zero for the customers with less than one year of membership, but that is not the point of this blog post.
The changes from the last blog post
Instead of letting the implicit operator return a Func<T>
it now returns Expression<Func<T>>
. To let the Evaluate
member work I also compiled the lambda to a Func.
public class ExpressionRule2<T>
: IRule<T> where T : class
{
public Expression<Func<T, bool>> Expression { get; private set; }
public Func<T, bool> Compiled { get; private set; }
public ExpressionRule2(
Expression<Func<T, bool>> expression)
{
Expression = expression;
Compiled = expression.Compile();
}
public static implicit operator Expression<Func<T, bool>>(
ExpressionRule2<T> item)
{
return item.Expression;
}
public bool Evaluate(T item)
{
return Compiled(item);
}
}
Yes, I know, not the best naming with ExpressionRule2
but it’s a quick hack to get the blog post compatible with non linq to object sources. There’s actually nothing more to it. Of course I have put up a custom DbContext
for Entity framework Code-first CTP 5, as well as added an Id to the customer.
public class StorageContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public StorageContext()
: base(@"data source=.;initial catalog=NiceLambdas;integrated security=SSPI;")
{
}
}
public class Customer
{
public int Id { get; set; }
public int NumOfYearsAsMember { get; set; }
public int CashSpent { get; set; }
}
Why this solution at all?
What this gives me:
- A name to my rule.
- I can have interfaces represent it and have a factory or IoC returning variations of the rule.
- I can isolate it for testing.
Happy coding!
//Daniel