EF4 – Customizing the OjectContext for LazyLoading!

by Mikael Henriksson 29. May 2009 02:56

In previous post I wrote a minor struggle about POCO. I was reading like a few 100 blog posts about EF4 and came across an interesting post about lazy loading by Julie Lerman. I agree with Rob that lazy loading should be possible to set for a specific collection and I am sure this will be possible in the future but for now we will have to do without I think. My take on it is to allow this to be set in the constructor of the CustomContext. I’ll use an enum for this for now:

public enum Options
{
	None = 0,
	LazyLoading = 1,
	ProxyCreation = 2,
	All = LazyLoading | ProxyCreation
}
Now I can set this straight away. Since I hate code duplication I created a couple of private methods so I don’t have to make mistakes while ctrl+c + ctrl+v.

public class EntityContext : System.Data.Objects.ObjectContext
{
	public ObjectSet<User> Users { get; set; }
	public ObjectSet<Blog> Blogs { get; set; }
	public ObjectSet<Post> Posts { get; set; }

	public EntityContext()
		: base("name=Entities", "Entities")
	{
		CreateObjectSets();	
	}

	public EntityContext(Options options)
		: base("name=Entities", "Entities")
	{
		CreateObjectSets();
		SetContextOptions(options);	
	}

	private void SetContextOptions(Options options)
	{
		switch (options)
		{
			case Options.All:
				this.ContextOptions.DeferredLoadingEnabled = true;
				this.ContextOptions.ProxyCreationEnabled = true;
				break;
			case Options.LazyLoading:
				this.ContextOptions.DeferredLoadingEnabled = true;
				break;
			case Options.ProxyCreation:
				this.ContextOptions.ProxyCreationEnabled = true;
				break;
			case Options.None:
				this.ContextOptions.DeferredLoadingEnabled = false;
				this.ContextOptions.ProxyCreationEnabled = false;
				break;
			default:
				break;
		}			
	}

	private void CreateObjectSets()
	{
		this.Users = CreateObjectSet<User>();
		this.Blogs = CreateObjectSet<Blog>();
		this.Posts = CreateObjectSet<Post>();
	}
}

Now I can go ahead and create my EntityContext with providing the desired options in the constructor:

var context = new EntityContext(Options.LazyLoading);

If it is performance we are after then another option would be what Alex suggests in this specific post of his series of tips regarding EF4.

Tags:

Entity Framework

Entity Framework 4 POCO tutorial - Part 1

by Mikael Henriksson 29. May 2009 00:34

First of all thanks again to Microsoft for both taking the EF further towards something really powerful and usable and then special thanks to Alex James for helping the community out over on stack overflow. I started of small scale. Let’s say I want to create a blog. I have users, blogs, entries and comments. Think that sums up the basic functionality of a blog. That leaves me with the following conceptual model:

issue

From this I want to remove the auto generated classes. I recommend reading this blog post for more information on how to remove this and for a really good tutorial in general. Now all that is left is to right click the model (not a class) and click on generate database script. This does a good job I end up with a database script that I can just run from within visual studio and the database is created for me. Just bear in mind that generating this script and running it will totally delete everything within your database. Only do this on your development machine, not on a production db. To synchronize the changes from your development machine go ahead and use Red Gate’s SQL Toolbelt.

I then move ahead to create my POCO classes and end up with the following:

public partial class User
{
	public User()
	{
		Blog = new Blog();
		Active = true;
	}

	public virtual int Id { get; set; }
	public virtual bool Active { get; set; }
	public virtual string Alias { get; set; }
	public virtual string Email { get; set; }
	public virtual string Password { get; set; }
	public virtual string Hash { get; set; }
	
	public virtual Blog Blog { get; set; }
}

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool AllowComments { get; set; }
    public User User { get; set; }
    public IList<Entry> Entries { get; set; }
}

public abstract class Post
{
    public virtual int Id { get; set; }
    public virtual string Header { get; set; }
    public virtual string Text { get; set; }
    public virtual DateTime CreatedAt { get; set; }
    public virtual int UserId { get; set; }
}

public class Entry : Post
{
    public Blog Blog { get; set; }
    public IList<Comment> Comments { get; set; }                
}

public class Comment : Post
{
    public Entry Entry { get; set; }
}

I instantly run in to an extremely silly problem where I get the following exception:

System.Data.MetadataException: Schema specified is not valid. Errors: The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'Entry'. Previously found CLR type 'Entry', newly found CLR type 'System.Collections.Generic.Dictionary2+Entry'. The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'Entry'. Previously found CLR type 'Entry', newly found CLR type 'System.Runtime.CompilerServices.ConditionalWeakTable2+Entry'.

This is according to Alex (Program Manager on the Entity Framework team) over on Stack Overflow due to me using Entry as a class name. There was a few other “Entry” within the framework found and I get an exception. It should be fixed in Beta2 of EF4 but until then I go ahead and change my class name to BlogEntry instead and voila no more exception. Cheers Alex.

I realize this isn’t an ultimate model of a blog. First of all only a certain user (in this simple blog) should be allowed to post to his/her own blog so there is no need for every blog entry to have the user id and for the comments there is no need for a header. Simple text would do. I refactor the conceptual model and the classes to the following:

public abstract class Post
{
	public virtual int Id { get; set; }
	public virtual string Text { get; set; }
	public virtual DateTime CreatedAt { get; set; }
}

public class BlogEntry : Post
{
	public virtual string Header { get; set; }
	public virtual Blog Blog { get; set; }
	public virtual IList<Comment> Comments { get; set; }
}

public class Comment : Post
{
	public virtual string Header { get; set; }
	public virtual int UserId { get; set; }
	public virtual BlogEntry BlogEntry { get; set; }
}

I add virtual because I want to support automatic lazy (deferred) loading. This should improve performance and usability of the tiny blog domain quite a bit. Now to make this work I need to create my own ObjectContext. It’s not such a big thing to do and I simplified it a lot with just a couple of automatic properties.

public class EntityContext : System.Data.Objects.ObjectContext
{
	public ObjectSet<Blog> Blogs { get; set; }
	public ObjectSet<Post> Posts { get; set; }
	public ObjectSet<BlogEntry> Entries { get; set; }
	public ObjectSet<Comment> Comments { get; set; }
	
	public EntityContext() : base("name=Entities", "Entities")
	{
		this.Blogs = CreateObjectSet<Blog>();
		this.Posts = CreateObjectSet<Post>();
		this.Entries = CreateObjectSet<BlogEntry>();
		this.Comments = CreateObjectSet<Comment>();	
	}
}

The constraint : base(“name=Entities”, “Entities”) is just telling what connection string we should use. And the rest is pretty straight forward. However I’d like to note that the ObjectSet is an enhanced/empowered version of the ObjectQuery if I am not mistaken.  Now it’s time to start playing around with this. I need to create a unit test and in my first try I got a couple of things wrong. I try to use it in mixed mode and it doesn’t work out as I expected.

/// <summary>
/// A test for adding users
/// </summary>
[TestMethod()]
public void CanAddUser()
{
	EntityContext context = new EntityContext();

	var user = new User();
	user.Alias = "CatZ";
	user.Active = true;
	user.Email = "first.last@domain.com";
	user.Hash = "1234";
	user.Password = "1234";

	user.Blog.Title = "Mad Cat";
	user.Blog.EnableComments = true;

	context.Users.AddObject(user);
	context.SaveChanges();

	Assert.IsTrue(user.Id > 0);
}

It gives me the following exception:

Test method threw exception: System.ArgumentException: There are no EntitySets defined for the specified entity type 'BlogEntry'. If 'BlogEntry' is a derived type, use the base type instead. For example, you would see this error if you called CreateObjectSet() and DiscontinuedProduct is a known entity type but is not directly mapped to an EntitySet. The DiscontinuedProduct type may be a derived type where the parent type is mapped to the EntitySet or the DiscontinuedProduct type might not be mapped to an EntitySet at all. Parameter name: TEntity

Huh? LOL! WTF? Ok back to basics or back to base class actually (at least that’s what the message says). I tweak the EntityContext a bit and end up with something like:

public class EntityContext : System.Data.Objects.ObjectContext
{
	public ObjectSet<User> Users { get; set; }
	public ObjectSet<Blog> Blogs { get; set; }
	public ObjectSet<Post> Posts { get; set; }
	
	public EntityContext() : base("name=Entities", "Entities")
	{
		this.Users = CreateObjectSet<User>();
		this.Blogs = CreateObjectSet<Blog>();
		this.Posts = CreateObjectSet<Post>();
	}
}

Woohoo end of problems or so I thought at least. The test runs and fails with the following message:

Failed    CanAddUser    UnitTests    Test method threw exception: System.InvalidOperationException: Invalid relationship fixup detected in the navigation property 'User' of the entity of the type 'Blog'.   

This is insane, I was ready to give up. I mean what could be wrong now? The only thing I could think of was that the navigation property of the blog was not set to the current user. Quick modification of the test:

/// <summary>
/// A test for adding users
/// </summary>
[TestMethod()]
public void CanAddUser()
{
	EntityContext context = new EntityContext();

	var user = new User();
	user.Alias = "CatZ";
	user.Active = true;
	user.Email = "first.last@domain.com";
	user.Hash = "1234";
	user.Password = "1234";

	user.Blog.Title = "Mad Cat";
	user.Blog.EnableComments = true;
	user.Blog.User = user;

	context.Users.AddObject(user);
	context.SaveChanges();

	Assert.IsTrue(user.Id > 0);
}

The test runs and IT IS A SUCCESS!!! WOOHOO!! Now I am soooo happy I run it again and bummer I end up with two identical users and blogs in the database with two different id’s. Ok so I need some validation but I don’t care right now. I am just so happy with how easy it finally is to use my own classes with the entity framework. Ultimately I would not add a user to the database like this of course though I would hardly let the user add itself but it’s a start of course. I’ll write more in the future about this since I’ve waited 1,5 years for this I am quite eager to learn how it’s working!

Tags:

Entity Framework

Improvements in Entity Framework 2.0

by Mikael Henriksson 21. May 2009 23:09

Well first of all model first of course. Second and even more importantly is that I don’t need to think too much about related entities I can just go ahead, add stuff to my entities and then just save it all. Take the following for example.User 
It’s probably a silly example but I’ll go ahead anyway. Each user can have a profile with some personal details that I don’t want to load unless explicitly told to (you know performance etc). Earlier I had to first create a user, then create a profile have do: user.AddToProfile(profile)

Nothing wrong with that really but it wasn’t optimal. In the new release I can go ahead and do what I really wanted to do:

// Create a user
User user = new User();
user.Alias = "MyCoolAlias";
user.Email = "bogusemail@domain.se";
user.Password = "1234";
user.Salt = "1234";
user.Active = true;

// Create a profile for the user
user.Profile = new Profile();
user.Profile.FirstName = "I am";
user.Profile.LastName = "Impressed";
user.Profile.Address.StreetOne = "By the new changes street in 2.0";
user.Profile.Address.City = "Oslo";
user.Profile.Address.Zip = "0473";
user.Profile.Address.Country = "Norway";

// Add the user to the context and save it to db
Entities context = new Entities();
context.AddToUsers(user);
context.SaveChanges();

How much easier is that I ask you?

Tags:

Entity Framework

Visual Studio 2010 love at first beta sight!

by Mikael Henriksson 21. May 2009 22:43

I completely love Visual Studio 2010. So many incredible enhancements. I totally love to be able to model first in Entity Framework. I can sit there with my class diagram and consider what should go where and then just generate a database script to execute! Amazing, finally and WOW! The UI has also been improved so much you need to try it yourself to believe it. Further I am really enjoying the UML capabilities. Now I can use the same tool for generating sequence diagrams etc. It is similar but so different from Visual Studio 2008.

Thanks

Tags:

Visual Studio

How to group by date time in MS SQL Server

by mikael 15. May 2009 12:24

select   count(*),
   cast(convert(varchar,a.CreatedDate,101) as smalldatetime) as VisitDate
from     ActivityLogs.dbo.DetectionLogs a
where   a.Column = 'value'
group by cast(convert(varchar,a.CreatedDate,101) as smalldatetime)
order by cast(convert(varchar,a.CreatedDate,101) as smalldatetime) asc

This is extremely useful to gather some quick statistics! :)

Tags:

SQL Server

NHibernate – A waste of precious time!!

by Mikael Henriksson 8. May 2009 00:13

By coincidence I found something called Fluent NHibernatewow automatic mappings etc. After having a read up on NHibernateI also found something called NHibernate Validationthat seemed to serve my purposes well. The only reason for having a look around was to easily add validation to my classes. I found this a bit hard with Entity Framework.

All was well and good I started of with learning how to map entities with fluent. After a couple of hours I had produced a model that I could use. Then I hooked in the validation dll. Tried to validate one of my entities with an attribute. I am not very fond of the whole xml configuration thing to tell you the truth. It’s more work in the long run. I find that if I need to change something I am changing at code level so I might as well change the attribute! Anyway, when I try to instantiate the ValidatorEngie with:

var validator = new ValidatorEngine();

It throws an exception! It’s due to using an older version of NHibernate so I try to upgrade NHibernate and then everything else starts throwing exceptions or complaining about versions. I added some version remaps in the app.config but not luck.

That’s the time I stop trying and go back to Entity Framework. Even if it is still a bit immature it is a piece of cake setting up even for more advanced scenarios and with version 2 on the way I rather have to quickly create a validation service than have to get in to every single detail about NHibernate!

Thanks but no thanks!

Tags: ,

C# | NHibernate