My domain model has a lot of complex financial data that is the result of fairly complex calculations on multiple properties of various entities. I generally include these
I did a lot of research on this the last several days because it's been a bit of a pain point in constructing efficient Entity Framework queries. I've found several different approaches that all essentially boil down to the same underlying concept. The key is to take the calculated property (or method), convert it into an Expression
that the query provider knows how to translate into SQL, and then feed that into the EF query provider.
I found the following libraries/code that attempted to solve this problem:
LINQ Expression Projection
http://www.codeproject.com/Articles/402594/Black-Art-LINQ-expressions-reuse and http://linqexprprojection.codeplex.com/
This library allows you to write your reusable logic directly as an Expression
and then provides the conversion to get that Expression
into your LINQ query (since the query can't directly use an Expression
). The funny thing is that it'll be translated back to an Expression
by the query provider. The declaration of your reusable logic looks like this:
private static Expression> projectAverageEffectiveAreaSelector =
proj => proj.Subprojects.Where(sp => sp.Area < 1000).Average(sp => sp.Area);
And you use it like this:
var proj1AndAea =
ctx.Projects
.AsExpressionProjectable()
.Where(p => p.ID == 1)
.Select(p => new
{
AEA = Utilities.projectAverageEffectiveAreaSelector.Project()
});
Notice the .AsExpressionProjectable()
extension to set up projection support. Then you use the .Project
extension on one of your Expression
definitions to get the Expression
into the query.
LINQ Translations
http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider and https://github.com/damieng/Linq.Translations
This approach is pretty similar to the LINQ Expression Projection concept except it's a little more flexible and has several points for extension. The trade off is that it's also a little more complex to use. Essentially you still define your reusable logic as an Expression
and then rely on the library to convert that into something the query can use. See the blog post for more details.
DelegateDecompiler
http://lostechies.com/jimmybogard/2014/05/07/projecting-computed-properties-with-linq-and-automapper/ and https://github.com/hazzik/DelegateDecompiler
I found DelegateDecompiler via the blog post on Jimmy Bogard's blog. It has been a lifesaver. It works well, is well architected, and requires a lot less ceremony. It does not require you to define your reusable calculations as an Expression
. Instead, it constructs the necessary Expression
by using Mono.Reflection
to decompile your code on the fly. It knows which properties, methods, etc. need to be decompiled by having you decorate them with ComputedAttribute
or by using the .Computed()
extension within the query:
class Employee
{
[Computed]
public string FullName
{
get { return FirstName + " " + LastName; }
}
public string LastName { get; set; }
public string FirstName { get; set; }
}
This can also be easily extended, which is a nice touch. For example, I set it up to look for the NotMapped
data annotation instead of having to explicitly use the ComputedAttribute
.
Once you've set up your entity, you just trigger decompilation by using the .Decompile()
extension:
var employees = ctx.Employees
.Select(x => new
{
FullName = x.FullName
})
.Decompile()
.ToList();