I'm trying to use a '~classic' layered arch using .NET and Entity Framework. We are starting from a legacy database which is a little bit crappy:
- Inconsistent naming
- Unneeded views (view referencing other views,
select *
views etc...) - Aggregated columns
- Potatoes and Carrots in the same table
- etc...
So I ended with fully isolating my database structure from my domain model. To do so EF entities are hidden from presentation layer. The goal is to permit an easier database refactoring while lowering the impact of it on applications.
I'm now facing a lot of challenges and I'm starting to ask myself if I'm doing things right.
My Domain Model is highly volatile, it keeps evolving with apps as new fields needs are arising. Complexity of it keeps raising and class it contains start to get a lot of properties.
Creating include strategy and reprojecting to EF is very tricky (my domain objects don't have any kind of lazy/eager loading relationship properties):
DomainInclude<Domain.Model.Bar>.Include("Customers").Include("Customers.Friends") // To... IFooContext.Bars.Include(...).Include(...).Where(...)
Some framework are raping the isolation levels (Devexpress Grids which needs either XPO or
IQueryable
for filtering and paging large data sets)
I'm starting to ask myself if :
- the isolation of EF auto-generated entities is an unneeded cost.
- I should allow frameworks to hit
IQueryable
? Slow slope to hell? (it's really hard to isolate DevExpress framework, any successful experience?) - How can I lower the volatility of my domain model?
Precisions
EF Isolation and fields that should or should not be included on service calls
Here is how I isolate EF Entities. Presentation layer is an isolation layer as no presentation can access to repositories class.
// Inside a service class. For this sample Customer is defined in the service model
// And Person is autogenerated by Entity Framework
/// Get the customers who bought an item ///
IEnumerable<Customer> GetCustomers(Item item){
var query = from person in _personRepository.GetItemBuyers(item.Id)
select new Customer {
Id = person.CustomerId
, FirstName = person.FirstName
, LastName = person.LastName
, Sex = person.Sex == 'M' ? Sex.Male : Sex.Female};
return query;
}
It can get time consuming especially when you need to include (or not) some relationship properties. There is multiple solutions for including:
1. GetCustomersWithOrders(Item item);
2. GetCustomers(Item item, IncludeStrategy<Customer> include);
3. GetCustomers(Item item, params string[] fieldsIncluded);
- Will obvioulsy result in service class containing thousands of GetCustomersWith...With...
- Will create a lot of IncludeStrategy inherited class. I'm still working on a dynamic fluent approach
DomainInclude<Customer>.Include("Orders").Include("Friends")
- Will need the creation of some mapping dictionnary "Orders" => "CustomerAccount.PayedOrders"
Framework hitting IQueryable
Repositories don't expose IQueryable, the goal is to hide entity framework. Devexpress datagrids need an IQueryable to allow paging/filtering without loading the whole table in memory.
Finding a workaround is nearly impossible as it will be really time consuming and the portability of the workaround is not assured with new devexpress releases.
My question here is: Is it a big deal to allow devexpress going through the layers up to the EF context class. Should I drop Devexpress instead and use a more flexible datagrids (Sencha ExtJs, ...)