I can't really find a satisfying solution for that problem. I have a n-layers application :
My problem is that I need to work with full objects in my business layer. However I can't figure out what is the best way to load them. The question may look stupid - and perhaps it is - but I'm very puzzled by it. I have the following classes (just to illustrate):
public class Library
{
int ID {get;set;}
string Name {get;set;}
Book[] Novels {get;set;}
Book[] TextBooks {get;set;}
}
public class Book
{
int ID {get;set;}
Library[] SalePoints {get;set;}
string Name {get;set;}
string Type {get;set;}
}
And I have two Service Methods LoadLibrary and LoadBook whose aim is to populate these respective models. My problem is that it looks like I will get into an infinite loop. I'll explain myself right now.
I have pre-loaded domain models coming from the Repositories (The repositories convert the Data Models into domain models using valueinjecter (unflatenning), so basically the simple fields (int, string, etc..), are populated in the resulting domain model but the IEnumerable properties are not. That's why I'm finishing to load them in the Service Layer. (There are also two reaons why I can't fully load them in my repositories : 1/ I'm using a generic repository and 2/ The loading logic relay on business rules)
Let us say that I do the following :
public class LibrairiesProcessor
{
public Library LoadLibrairyObject(int ID)
{
Library l = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;
l.Novels = UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="Novel");
l.TextBooks= UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="TextBook");
for(int i=0;i<l.Novels.Count();i++)
{
l.Novels[i] = BooksProcessor.LoadBookObject(l.Novels[i].ID);
}
}
}
and
public class BooksProcessor
{
public Book LoadBookObject(int ID)
{
Book b = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;
b.SalesPoint = // Get From repo using the Unit of work
for(int i=0;i<b.SalesPoint.Count();i++)
{
b.SalesPoint[i] = LibrairyProcessor.LoadLibraryObject(b.SalesPoint[i].ID);
}
}
}
Isn't there an infinite Loop ? LoadLibraryObject() calling LoadBookObject() and vice versa... And especially when you come to the book whose SalePoint is the librairy who triggered LoadBookObject()... at best the same work is done twice, but I really suspect that it will never end.
So I'm wondering if I'm doing the things right. My goal is to have fully loaded objects to avoid wondering if the object is enough loaded before using it, but I'm not sure of the solution I've came up with. How do you usually achieve that ?
It looks like a trivial issue but seriously I can't figure out how to load my stuff. I feel like I have a dilemma :
1/ Manually Populating all the properties and sub properties by using UnitOfWork.Repository, but I will have a lot of code and a lot of redundant code.
2/ Live with partially loaded domain models with some properties set to null, but I really don't want that. And since I'm not working with EF objects directly since my domain model is complex, I can't use their lazy loading stuff.
3/Call the Service LoadObject Methods from one LoadObjectMethod to an other, to avoid code redundancy, but it looks like I'm introducing a lot of recursivity.
How should I handle that ?
Thanks !
This is probably best suited for CodeReview, but here are a few thoughts that may save you some trouble :
Having a whole separate DAL object model might be overkill. Most modern ORMs will take care of the domain objects/DB mapping for you without polluting your domain model with persistence stuff. See Should I create in BLL project class the same like poco class in DAL project and return it to UI project if I want to display data from database?
Having a generic repository seems like a poor excuse for not being able to load lists of sub-entities. As for "the loading logic relies on business rules", if you're referring to the Novels/TextBooks distinction, there are other, less class-intensive ways of doing this.
"since I'm not working with EF objects directly since my domain model is complex, I can't use their lazy loading stuff" : how exactly is your model complex ? Technically speaking, why couldn't you use lazy loading ?
As a good practice, Aggregates shouldn't refer to one another with navigatable properties but with (lists of) ids. The object that got an Aggregate root from a Repository will call another Repository if it wants to load associated aggregate roots.
UnitOfWork.LibraryRepo
looks awkward. The UoW should be injected or directly instantiated in the method, not made available as a singleton. Same is true for the Repository. Things will be more testable and loosely coupled if you pass an ILibraryRepo
around to the method instead. Unit of Work and Repository are two separate concepts, they somehow converge in Entity Framework but from the outside, they shouldn't look as tightly tied togteher as that.