Carpool Project: Part #5
October 13, 2010 Leave a comment
I am now building factories for my POCOs. I have a design question. Should POCOs hold the key to another object or an actual reference? DDD says that I should a reference and EF does that, but POCOs do not seem to pattern that way.
The choice is this:
1 public class Swimmer : ValidationBase 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public SwimGroup SwimGroup { get; set; } 6 } 7
or this (flattened class):
1 public class Swimmer : ValidationBase 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public int SwimGroupId { get; set; } 6 public string SwimGroupName { get; set; } 7 } 8
I see the benefits to both sides. On one hand, the flattened class (option #2) makes it very easy to code up the UI. On the other hand, option #2 is much more rigid and brittle – you can have lots of properties and a change to 1 entity class means that every value object that depends on the entity object has to change. You can add interfaces for each of the permutations of the flattened classes to account for change, but that becomes unwieldy.
After going back and forth a couple of times, I settled on doing both. I left my base class (Swimmer) as option #1 and then build a SwimmerSummary class that is option #2. With both, I will see what is easier to work with on the UI.
Starting on Option #1, I created a Swimmer Factory with a mapper function like this:
The problem is that the SwimGroup reference is expected my Domain-Defined class, not the EF class. I need to wire up a factory for the SwimGroup and pass a Domain-Driven class like this:
1 Swimmer swimmer = new Swimmer { 2 Id = carpool_swimmer.SwimmerId, 3 Name = carpool_swimmer.SwimmerName}; 4 5 SwimGroupFactory swimGroupFactory = new SwimGroupFactory(); 6 swimmer.SwimGroup = swimGroupFactory.GetSwimGroup(carpool_swimmer.Carpool_SwimGroup.SwimGroupId); 7 8 return swimmer; 9
Which leads me to my next problem – how to get both the Swimmer and the SwimGroup in the same database call? As this is written, each factory has a unique reference to the EF class, so getting the swimmer and his/her SwimGroup means 2 round trips. I can put them all into a single call (Unit of work) by using the include statement, but then I will need to change the factory to do the mapping WITHOUT the database call.
I started implanting this, but then I ran into references two deep – Swimmer needs SwimGroup which needs SwimTeam – so the SwimmerFactory has to know to call for SwimGroup and SwimTeam in it’s EF call. I wonder if there are guidelines about how deep to make the references?
So I have this:
1 public static SwimGroup MapSwimGroup(Carpool_SwimGroup carpool_swimgroup) 2 { 3 SwimGroup swimGroup = new SwimGroup { 4 Id = carpool_swimgroup.SwimGroupId, 5 Name = carpool_swimgroup.SwimGroupName, 6 SwimTeam = SwimTeamFactory.MapSwimTeam(carpool_swimgroup.Carpool_SwimTeam) }; 7 8 return swimGroup; 9 } 10
and this:
1 public static Swimmer MapSwimmer(Carpool_Swimmer carpool_swimmer) 2 { 3 Swimmer swimmer = new Swimmer { 4 Id = carpool_swimmer.SwimmerId, 5 Name = carpool_swimmer.SwimmerName, 6 SwimGroup = SwimGroupFactory.MapSwimGroup(carpool_swimmer.Carpool_SwimGroup) }; 7 8 return swimmer; 9
and then a EF call that does this:
1 public Swimmer GetSwimmer(int swimmerId) 2 { 3 var selectedSwimmer = (from swimmer in carpoolEntity.Carpool_Swimmer 4 .Include("Carpool_SwimGroup") 5 .Include("Carpool_SwimGroup.Carpool_SwimTeam") 6 where swimmer.SwimmerId == swimmerId 7 select swimmer).First(); 8 9 return MapSwimmer(selectedSwimmer); 10 } 11
The 2 things I need to be mindful of are:
· No intellisense on the include statement – so the name has to match
· I need to check for null in the mapping in case the calling function forgets the include statement. My mapping cannot handle lazy loading.
This got me thinking about the Factory pattern in general. My Factory class is actually a plant with 2 factories. Factory 1 maps EF classes to POCO classes. Factory 2 class calls EF and returns POCOs. I wonder if I need to divide these 2 functions into separate classes? The examples of the repository pattern that I have seen keep them together – but I have only seen them in a “hello nerd dinner” context.
I then thought harder about the subject and realized that I missing the point of the factory. Conceptually, I picture a window with Alot behind it. I can either hand him an int or a EF class. No matter what I pass in, I get a POCO class out. In this sense, the factory spits out POCOs and I can use overloaded parameters to handle the different permutations that come in. The factory class then looks like this:
1 public class SwimGroupFactory 2 { 3 CarpoolEntities carpoolEntity = null; 4 5 public SwimGroupFactory() 6 { 7 carpoolEntity = new CarpoolEntities(); 8 } 9 10 public SwimGroup GetSwimGroup(int swimGroupId) 11 { 12 var selectedSwimGroup = (from swimGroup in carpoolEntity.Carpool_SwimGroup 13 where swimGroup.SwimGroupId == swimGroupId 14 select swimGroup).First(); 15 return MapSwimGroup(selectedSwimGroup); 16 } 17 18 public SwimGroup GetSwimGroup(Carpool_SwimGroup carpool_swimgroup) 19 { 20 return MapSwimGroup(carpool_swimgroup); 21 } 22 23 24 private static SwimGroup MapSwimGroup(Carpool_SwimGroup carpool_swimgroup) 25 { 26 SwimGroup swimGroup = new SwimGroup { 27 Id = carpool_swimgroup.SwimGroupId, 28 Name = carpool_swimgroup.SwimGroupName, 29 SwimTeam = SwimTeamFactory.MapSwimTeam(carpool_swimgroup.Carpool_SwimTeam) }; 30 31 return swimGroup; 32 } 33 } 34
Which is great – but then the new class uses a new EF. I have 2 EF classes, though only 1 is used for an active connection. Perhaps the EF class should be moved from the constructor and placed in the methods that actually use it…
So I refactored and got all green – making my factories static and overloaded methods passing in ints or EF classes. For ints, the methods created a new EF context and queries the db. For EF classes, it just maps and send back…
For example:
1 public static SwimGroup GetSwimGroup(int swimGroupId) 2 { 3 using (CarpoolEntities carpoolEntity = new CarpoolEntities()) 4 { 5 var selectedSwimGroup = (from swimGroup in carpoolEntity.Carpool_SwimGroup 6 where swimGroup.SwimGroupId == swimGroupId 7 select swimGroup).First(); 8 return MapSwimGroup(selectedSwimGroup); 9 } 10 } 11 12 public static SwimGroup GetSwimGroup(Carpool_SwimGroup carpool_swimgroup) 13 { 14 return MapSwimGroup(carpool_swimgroup); 15 } 16 17 private static SwimGroup MapSwimGroup(Carpool_SwimGroup carpool_swimgroup) 18 { 19 SwimGroup swimGroup = new SwimGroup { 20 Id = carpool_swimgroup.SwimGroupId, 21 Name = carpool_swimgroup.SwimGroupName, 22 SwimTeam = SwimTeamFactory.GetSwimTeam(carpool_swimgroup.Carpool_SwimTeam) }; 23 24 return swimGroup; 25 } 26 } 27
I then used this pattern for the rest of my factories…