Entity framework 4 – Part 2 – Relationships between non-public members
2009/11/10 5 Comments
Part 1 – Getting started
Part 3 – Adding pluralization support
Ok, the mission of this post is to extend the code from the previous post (about dynamic and non strongly typed object-contexts) and add some relations between the entities. I will show you one way how you could setup relationships between entities where members are not public.
Download a complete running example.
The example now is as follows:
A Person is represented by an user-account which can have many whishlists and each list can have many wishes.
The client code – Using Entity store
Lets look at the code that consumes the Entity store and creates some wishes for “Little Ben”.
//Create the User: Little Ben
var userAccount =
new UserAccount
{
Username = "littleben",
Password = "ilovesnow",
Email = "ben@thelittles.com"
};
//Create Little Ben's Wishlist for Christmas
var wishlist = new WishList { WishedBy = userAccount };
wishlist.WishSomething(new Wish { Description = "A new computer." });
wishlist.WishSomething(new Wish { Description = "A new lens." });
using (IEntityStore es = new EfChristmasEntityStore("Test"))
{
es.EnsureCleanDatabaseExists();
//Store the useraccount, the wishlist and it's wishes.
//No need for: es.AddEntity(userAccount); or es.AddEntity(wish1)...(wish2);
es.AddEntity(wishlist);
es.SaveChanges();
}
To make the above work, you need to tell the ContextBuilder how to map entities. This is done using a fluent API which is refactoring friendly, no bloated Xml is required.
UserAccount – mappings
Lets look at the mappings for the UserAccount.
[Serializable]
public class UserAccountMapping
: EntityConfiguration<UserAccount>
{
public UserAccountMapping()
{
HasKey(u => u.Id);
Property(u => u.Id).IsIdentity();
Property(u => u.Email)
.IsRequired()
.IsNotUnicode()
.MaxLength = 150;
Property(u => u.Username)
.IsRequired()
.IsUnicode()
.MaxLength = 20;
Property(u => u.Password)
.IsRequired()
.IsUnicode()
.MaxLength = 20;
}
}
No rocket science there. The mappings for the WishList is the first entity that has a required relationship (doesn’t allow null values) to another entity, the UserAccount.
WishList – mappings
[Serializable]
public class WishListMapping : EntityConfiguration<WishList>
{
public WishListMapping()
{
HasKey(wl => wl.Id);
Property(wl => wl.Id).IsIdentity();
Relationship<UserAccount>(wl => wl.WishedBy).IsRequired();
}
}
Wish – mappings
The final mapping is the actual Wish-entity which is owned by a certain WishList. The Wishes are stored in an internal wish-collection in the WishList-entity and it does not have a public scope hence you can’t use Expressions to configure it. I have put together some code that allows you to use expressions for this, but you need to pass a string that defines the name of the member.
[Serializable]
public class WishMapping : EntityConfiguration<Wish>
{
public WishMapping()
{
HasKey(wl => wl.Id);
Property(wl => wl.Id).IsIdentity();
Relationship<WishList>(w => w.BelongsToList).IsRequired();
//Can't use "RelationshipFrom<WishList>(w => w.Wishes);" since Wishes in WishList has scope "protected".
var wishesExpression = ObjectAccessor<WishList>.CreateExpression<ICollection<Wish>>("Wishes");
RelationshipFrom<WishList>(wishesExpression);
}
}
Lets register the mappings in the builder so that they are known when the context is being constructed.
The custom Entity store
public class EfChristmasEntityStore : EfEntityStore
{
public EfChristmasEntityStore(string connectionStringName) : base(connectionStringName)
{
}
protected override void ConfigureEntities()
{
ConfigureEntity(new UserAccountMapping());
ConfigureEntity(new WishListMapping());
ConfigureEntity(new WishMapping());
}
}
There is also an alternative way to use the infrastructure in the client code, if you don’t want to setup an custom Entity store.
//You can also consume it like this (but then you need references to System.Data.Entity and Microsoft.Data.Entity.CTP):
var builder = new ObjectContextBuilder<ObjectContext>("Test");
builder.RegisterEntity(new UserAccountMapping());
builder.RegisterEntity(new WishListMapping());
builder.RegisterEntity(new WishMapping());
using (var ctx = builder.CreateContext())
{
if (!ctx.DatabaseExists())
ctx.CreateDatabase();
ctx.EntitySet<WishList>().AddObject(wishList);
ctx.SaveChanges();
}
Thats it. Happy coding. Download the code and try for your self.
//Daniel

Pingback: Entity framework 4 – CTP 2 – Clean code with POCO entities « Daniel Wertheim
Hi,
Thank you for this info. But I’ve ran into a problem where you apparently haven’t.
I also have an abstract class with a property ID.
However, my classes that inherit this abstract class can’t use HasKey(u => u.ID);
I get the following error “A key registered for the derived type ‘…User’. Keys must be registered for the root type ‘…Entity’”
Have you encountered this and found a way around it?
Thanks in advance,
Arne
Hi!
I bumbed into this error but that was when I only mapped the property as an identity and didn’t specify HasKey for the property.
I repeat this mapping in every EntityConfiguration (one for each entity being stored).
//Daniel
Hi,
Very nice article.
I’m fiddling around too and one thing i can’t get to work is when i define a navigation property in the abstract class.
I want all my derived classes to have a certain assocation with another class.
So in fact i want to inherit an assocation/relationship.
I get the same error as Arne (previous reply).
here’s my problem explained: http://stackoverflow.com/questions/2270789/ef-4-0-code-only-assocation-from-abstract-to-derived
Hi!
Haven’t been able to look at this. Will see if I get some time over this weekend.
//Daniel