Skip to content

Calculated properties

Jon Smith edited this page Jan 12, 2015 · 3 revisions

There are many instances where a database property isn't available with the exact data you need. Here are some examples:

  • The name is in parts, e.g. FirstName, LastName and you want to display it as one string.
  • You would like a count of the number of orders a customer has.
  • You would like someone's age today when the database contains their date of birth.

The most obvious way is to add a new properties to the data class which contain calculations. For instance:

public string FullName { get { return FirstName + " " + LastName ; } }

Direct access

Calculated properties work in most cases for direct access. Note: it does NOT work if you do further filtering, ordering etc. on the calculated properties in the Presentation Layer. You get the following error

System.NotSupportedException : 
The specified type member '????' is not supported in LINQ to Entities. 
Only initializers, entity members, and entity navigation properties are supported.

At the moment you need to swap to DTOs if that is the case. I could change this but I wanted to keep the direct route as fast as possible.

DTO access

Calcualted properties do not work straight out of the box on DTOs - it throws the error you see above. As about 90% of my code uses DTOs this needed fixing! Below I list how this is solved in GenericServices.

Recommended way of handling calculated properties

GenericServices uses the open-source library DelegateDecompiler to solve the project of projections of calculated properties. This provides a way to mark these properties and then have them converted into the correct form for LINQ projections. The process is as follows:

  1. In your Data Layer (see Architecture Overview about layers) add the NuGet package called DelegateDecompiler. This gives you access to the ComputedAttribute.
  2. In your data class, the one used by Entity Framework, add a calculated property, but make sure you also add the [Computed] attribute to that property, see example below:
[Computed]
public string FullName { get { return FirstName + " " + LastName ; } }
  1. By default GenericServices will use the DelegateDecompiler to convert this (see section on Configuration about how this is turned on/off).

Notes:

  1. GenericServices searches the data class attached to the DTO, and any nested DTOs (see Nested DTOs) to look for [Computed] properties. There are cases when flattening where GenericServices will miss [Computed] properties. If you get a NotSupportedException (see text near the top of this page) then it could be this. You get round this by overriding the ForceNeedDecompile and set it to true.
  2. Be warned that getting calculated properties right isn't always easy. If you have a problem then I suggest that you write a small Unit Test and try LINQ command directly on EF. Note that I wrote a test suite for DelegateDecompiler so failing all else you could clone that and add a test.
  3. While you can use a [Computed] property for simple .Count() or .Any() then we recommend you use AutoMapper's Aggregation. These is simple for those two cases and slightly quicker.

Other ways of producing calculated properties