A class for using custom types in Entity Framework 6, similar to ValueConverter in EFCore.
- A typed Id, but no lock, you are stuck with
GUID
orint
ect. - NodaTime types, but no, only
DateTime
orDateTimeOffset
orTimeStamp
You must have a persistent model that works as is.
- Get a projection expression
ctx.Customers
.Where(x => x.Id == 5)
.Select(x => new Customer
{
Id = (Id<Customer>)x.Id,
Name = new CustomerName
{
GivenName = x.FirstName,
Surname = x.LastName,
},
ContactAddress = new Address
{
Street = x.HomeStreet
},
WorkAddress = new Address
{
Street = x.WorkStreet
City = x.WorkCity
},
BirthNumber = (BirthNumber)x.BirthNumber
});
- Get a list of all types that are instantiated in the select clause in the projection query In our case, the query materializes following types:
Customer
CustomerName
Address
and uses two custom types that will require custom value converters:Id<Customer>
BirthNumber
-
Create a new types for each materialized using reflection Each settable property will be in anon types, because creating a new type is not very cheap, so caching and reusing of anon types between many queries is a must.
-
Transform projection expression materialized types to anon types
- Each type will be
IQueryable<Customer> projectionQuery = ctx.Customers
.Where(x => x.Id == 5)
.Select(x => new /* AnonCustomer */
{
Id = x.Id, // WAS:int->Id<Customer>; NOW: int -> int
Name = new /* AnonCustomerName */
{
GivenName = x.FirstName,
Surname = x.LastName,
},
ContactAddress = new /* AnonAddress */ // Contact address and Work address use same anon type
{
Street = x.HomeStreet
},
WorkAddress = new /* AnonAddress */
{
Street = x.WorkStreet
City = x.WorkCity
},
BirthNumber = x.BirthNumber // WAS:string->BirthNumber; NOW: string -> string
});
- Create a select expression for mapping anon query into target query Note that names of properties are same, it should be 1:1 mapping with casing only
Expression<Func<AnonCustomer,Customer>> selectExpression = /*AnonCustomer*/ x => new Customer
{
Id = (Id<Customer>)x.Id,
Name = new CustomerName
{
GivenName = x.GivenName,
Surname = x.Surname,
},
ContactAddress = new Address
{
Street = x.Street
},
WorkAddress = new Address
{
Street = x.Street
City = x.City
},
BirthNumber = (BirthNumber)x.BirthNumber // PROJECTION:string->BirthNumber; MATERIALIZED: string -> string;
});
- Execute query
var projectToAnonQuery = visitor.Visit(projectionQuery);
var result = projectToAnonQuery.ToList().Select(selectExpression).ToList();
and you are done (^____^)!
- Properties,
- collections,
- custom types for a single property
- anonymous types (requires on the fly generation)
- inheritance
- polymorphism
- fields