Entity Framework mapping private members with Search

In one of my project’s, I am in the process of implementing a complex domain by embracing DDD. I am using Entity Framework and working on a legacy database.

EF is good enough to provide us with sufficient mapping powers to use the data model as our business model. Also I would want to keep things simple and direct by using the same model for both domain as well as EF. However, this brings up a few challenges.

If you need a fully encapsulated domain, you would probably want to expose your collections as IEnumerable<T>. But, you cannot have IEnumerable<T> for your collection and map to EF. It requires the collection to be of type ICollection<T>. But this would allow direct access to manipulate the collection through its Add, Remove methods. Your domain is not completely encapsulated. You can get around this limitation and use IEnumerable<T> in your model so that it is protected by your domain rules, but you would have to load the collection manually through your repository or factory in to the domain model. You would need to track the changes yourself and let EF know at the time of persisting by attaching each object from the collection back to EF and set with the relevant EntityState. This is needed since you could not have mapped an association in EF as your ICollection<T> backing member was private.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Student
{
public string Name { get; private set; }

private List<Course> _courses;
public IEnumerable<Course> Courses { get { return _courses; } }

public void AssignCourses(IEnumerable<Course> courses)
{
_courses = new List<Course>(courses);
}

public Student(string name)
{
Name = name;
}
private Student() { }
}

public class StudentRepository
{
public Student Get(string name)
{
var student = context.Students.FirstOrDefault(c => c.Name == name);
var courses = context.Courses.Where(c => c.Name == name).AsEnumerable();
student.AssignCourses(courses);
return student;
}
}

Another approach would be for you to map your private properties or members. This is a bit more involved than the previous method, but you can take advantage of EF while loading objects. In this article, mapping to non-public member shows one way how this can be achieved. As the article explains, this would not be a pure POCO since it contains knowledge of configuration for persistence. But I like this approach as it is simple and non-intrusive to the actual behaviour of the POCO itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public partial class Account
{
public int Id { get; private set; }

public string Name { get; private set; }

private string Code { get; set; }
}

public partial class Account
{
public class EFStorageExpression
{
public static readonly Expression<Func<Account, string>>
CodeAccessor = a => a.Code;
}
}

You can then map it as follows under EntityTypeConfiguration<Account>

1
Property(Account.EFStorageExpression.CodeAccessor)

The reason I stated this as complicated was that, sometimes it does not end with us just being able to map the entity and load it. We would also need to query using the private properties. If a private field is holding a collection, then there might be more reasons to filter on them. You would still be able to Include them to eager load them. You would then need to go through the Expression which you have exposed.

Let us see how we can query the for Account.

1
var account = context.Accounts.Where(a => a.Id == id).FirstOrDefault();

This would infact load the value for the property Code. But what if you need to filter on Code? All we have is the Expression<Func<Account, string>> pointing to Code property, but we require an Expression<Func<Account, bool>> which should be passed as the Where clause. For this we’ll need to build an Expression which represent our filter.

1
2
3
4
5
6
7
8
9
10
11
static Expression<Func<Account, bool>> SearchCode(string code)
{
ParameterExpression param = Expression.Parameter(typeof(Account));
var codeExp = Expression.Constant(code);

var invokedExpr = Expression.Invoke(Account.EFStorageExpression.CodeAccessor,
param);
var searchCodeExp = Expression.Equal(invokedExpr, codeExp);
// a => a.Code == code
return Expression.Lambda<Func<Account, bool>>(searchCodeExp, param);
}

Using the above, our search on Code can be done like this.

1
var account = context.Accounts.Where(SearchCode("A001")).FirstOrDefault();

But during runtime, this fails miserably throwing an exception

Message=The LINQ expression node type ‘Invoke’ is not supported in LINQ to Entities.

The problem is LINQ to Entities does not support Invocation of expressions. This works fine with LINQ to Objects and LINQ to SQL, but not with EF. You would need to use an ExpressionVisitor to rewrite the tree.
Or use LinqKit as I do here. This can be downloaded from Nuget. With this you can use your Expressions with EF.

The same query above after LinqKit looks as below.

1
2
var account = context.Accounts.AsExpandable().
Where(SearchCode("A000001")).FirstOrDefault();

The only addition was AsExpandable() extension before calling your custom expression. Everything works like a charm.

You can now hide your data and also make use of EF without adding much gunk to your model.