I've been working on the PostSharp4EF project of EFContrib. I'm pleased to announce relationships are now supported.
This actually proved to be somewhat more work, because of all the things that are happening behind the scenes.
Entity Framework expects EdmRelationshipNavigationProperty attributes on top of the property that points to a relationship. But there is also the giant EdmRelationshipAttribute with about 8 constructor parameters.
So, I now generate these for you.
The next step was to actually get the relationship for you. I do that by removing the backing field, and replacing it with methods that access the relationshipmanager to get the relationship.
I do not want you to work directly with EF types, but you will need a way to Load relationships. So, I've introduced an interface with the appropriate Load and IsLoaded methods. Just in case you do want to hook into the EntityCollection and EntityReference, I've also supplied methods on that interface that will get you these types.
Let's look at a domain model:
0 [Poco
("SimpleRelationshipTestEntities")] 1 public class Customer
2 { 3 public int CustomerID { get; set; }
4 public string Name
{ get; set; } 5
6 public ICollection<Car> Cars
{ get; set; } 7
8 public ICollection<Order> Orders
{ get; set; } 9 }
10 11 [Poco("SimpleRelationshipTestEntities")]
12 public class Car 13 {
14 public int CarID
{ get; set; } 15 public string Make { get; set; }
16 17 public Customer Customer { get; set; }
18 19 }
20 21 [Poco("SimpleRelationshipTestEntities")]
22 public class Order 23 {
24 public int OrderID
{ get; set; } 25 public int Amount { get; set; }
26 27 public Customer Customer { get; set; }
28 29
30 } 31
You might notice that I do not expose the concrete EF types, but instead expose ICollections.
After compilation, you can use this domainmodel like this:
0 using (SimpleRelationshipTestEntities context
= new SimpleRelationshipTestEntities()) 1 {
2 3 foreach (Car oldCar in context.Car)
4 { 5 IRelationshipLoader noLazyLoading = PostSharp.Post.Cast<Car, IRelationshipLoader>(oldCar);
6 7 bool wasLoaded = noLazyLoading.IsLoaded("Customer");
8 9 EntityReference<Customer> cu = noLazyLoading.GetRelatedReference<Customer>("Customer");
10 cu
.Load
(); 11
12 wasLoaded
= noLazyLoading
.IsLoaded
("Customer"); 13 }
14 15
16 17 // clear out database
18 foreach (Customer old
in context
.Customer
) 19 {
20 IRelationshipLoader
noLazyLoading
= PostSharp
.Post.Cast
<Customer, IRelationshipLoader
>(old
); 21
22 bool wasLoaded
= noLazyLoading
.IsLoaded
("Orders"); 23
24 noLazyLoading
.Load
("Orders"); 25 wasLoaded = noLazyLoading.IsLoaded("Orders");
26 27 EntityCollection<Order> orders = noLazyLoading.GetRelatedCollection<Order>("Orders");
28 29 context.DeleteObject(old);
30 } 31 context.SaveChanges();
32 33
34 Customer c
= new Customer { Name
= "Ruurd Boeke" }; 35 Customer c2 = new Customer { Name = "Test Customer" };
36 Car car
= new Car { Make
= "Ferrari" }; 37 Order o1 = new Order { Amount = 10 };
38 Order o2
= new Order { Amount
= 20 }; 39 Order o3 = new Order { Amount = 30 };
40 41 car.Customer = c;
42 car
.Customer
= c2
; // can re assign test 43
44 // add customer on order 45 o1.Customer = c;
46 o2
.Customer
= c
; 47 // add order on customer, new way
48 c
.Orders
.Add
(o3
); 49
50 51
52 context
.AddToCustomer
(c
); 53 context.AddToCustomer(c2);
54 context
.SaveChanges
(); 55 }
56 } Points of interest:
Line 5 casts your object to the implemented interface. This is checked at compile-time, so no worries about breaking at runtime. Using this cast, I can get to the EF types like I do at line 9 and line 27. Obviously, if you were inclined, you could place these methods on your domain classes. It does couple you to EF more though and clutters the code.
Next up are complex types. When that's done, I'm finished.
Quite pleased with this actually! 