MVC Storefront: Migrating to the Entity Framework

The title of the post is actually misleading. A better title (although too long) would be: Migrating the Repository concept used in the MVC Storefront to the Entity Framework.

Lately I’ve been playing more with the Entity Framework. The other day I was trying to replicate the way Rob Conery used the Repository Pattern in the MVC Storefront sample application and I was surprised to learn that the Entity Framework has some differences from LINQ to SQL that I did not expect.

My first test was very simple. I wanted to get an IQueryable of Posts with it’s respective Comments attached. The StoreFront did this in a really clever and simple manner. The following code was supposed to work:

class BlogRepository
{
    SOEntities ctx = new SOEntities();

    public IQueryable<Comment> GetComments()
    {
        return from comment in ctx.Comments.Include("Post")
               select new Comment()
               {
                   CommentId = comment.CommentId,
                   Author = comment.Author,
                   Body = comment.Body,
                   PostId = comment.Post.PostId
               };
    }

    public IQueryable<Post> GetPosts()
    {
        var posts = from post in ctx.Posts
                    let comments = GetComments()
                    select new Post()
                           {
                               PostId = post.PostId,
                               Title = post.Title,
                               Body = post.Body,
                               Comments = comments.Where(x => x.PostId == post.PostId)
                           };
        return posts;
    }
}

class Post
{
    public Guid PostId { get; set; }

    public string Title { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }
}

class Comment
{
    public Guid CommentId { get; set; }

    public Guid PostId { get; set; }

    public string Author { get; set; }

    public string Body { get; set; }

}

class Program 
{
static void Main(string[] args)
{
    BlogRepository rep = new BlogRepository();
    var posts = rep.GetPosts();
    posts = posts.Where(x => x.Title.Contains("MVC"));
    List<Post> postList = posts.ToList();
}
}

I get an error saying that Linq to Entities does not recognize the method GetComments().

It’s not a bug, it’s is just the way the EF works. The method GetComments can’t be translated to anything in the provider. But why does it work with Linq to Sql you ask? Because it converts the Linq expression in a different way. Sorry but the post would be too long if I got into this subject.

The way recommended by the EF team is you use the AsEnumerable() extension method, forcing the initial query to be executed and then you would be working with objects. Here is how I needed to change my code for this to work:

public IQueryable<Post> GetPosts()
{
    var posts = from post in ctx.Posts.AsEnumerable()
                let comments = GetComments()
                select new Post()
                       {
                           PostId = post.PostId,
                           Title = post.Title,
                           Body = post.Body,
                           Comments = comments.Where(x => x.PostId == post.PostId)
                       };
    return posts.AsQueryable();
}

This does work, but when ToList() method is called in the posts object, instead of filtering the posts in the sql code, all the posts are brought from the database and then they are filtered as objects. This is a work around but it’s not ideal.

I have another work around, it also isn’t the ideal but it does work in the sense that it will apply the filter in the database.

public IQueryable<Post> GetPosts()
{
    var comments = GetComments();
    var posts = from post in ctx.Posts
                select new Post()
                       {
                           PostId = post.PostId,
                           Title = post.Title,
                           Body = post.Body,
                           CommentsQry = comments.Where(x => x.PostId == post.PostId)
                       };
    return posts;
}

class Post
{
    public Guid PostId { get; set; }

    public string Title { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }

    public IQueryable<Comment> CommentsQry
    {
        set { Comments = new LazyList<Comment>(value); }
    }
}

The trick is that I introduced a IQueryable property in the Post class. This property receives an IQueryable and under the hood converts it to a LazyList and sets the Comments property. Doing this overcomes the limitation that the EF has in not letting you use constructors that take parameters in the EF code. It only allows parameterless constructors on the select projection.

If you run this code you will see that the records are filtered in the database. The reason I still don’t consider this solution ideal is that I had to make a change to my model class and created a property that is not related to the business but rather to a limitation imposed by the framework. I still don’t see a way around this. If anyone has any suggestions please do leave them as comments.

A very interesting post was written by Muhammad Mosa a while ago. It’s worth visiting his blog to checkout his idea.

When to use GUID over Autoincrement

The autoincrement feature in SQLServer tables is one that is really very handy. I’ve always like Oracle databases but it’s sequences are really much more complicated to manage than the autoincrement columns in SQLServer. If you are doing a project that is only going to run on Oralce or SQLServer either approach is fine and you won’t have any trouble.

The project I’m working on however has a requirement to work just the same on Oralce or SQLServer and the possibility that other databases may be added in the future. From day one my first worry was about if we were going to use Autoincrement and Sequences or we had to find another way out. The way out in this case would be to use GUID (Globally Unique Identifiers). In out case we chose to use Autoincrement for SQLServer and Sequences in Oracle. Mostly this approach was chosen because the guys that hired me thought that it would be much easier to search database for a record with a primary key like 1 or 15604 then something like 3F2504E0-4F89-11D3-9A0C-0305E82C3301. And I agreed with them on this, it is really simpler to say to the guy next to you to check if the person with id 2350 exists in the database the to tell him a GUID.

Of course we had a price to pay for this decision. Our DAL (Data Access Layer) was much more complicated because we had to deal with the Autroincrement vs Sequences problem. Imagine that you have a simple table called person with two columns only: ID and Name. Look at the difference in the insert clauses for each database:

Oracle
INSERT INTO PERSON (ID, NAME) VALUES (SEQ_PERSON.NEXTVAL, 'GABRIEL');
SQLServer
INSERT INTO PERSON (NAME) VALUES ('GABRIEL');

Observe that with Oracle I have to specify the PK column and in SQLServer I don’t and even worst I have to call the sequence SQL_PERSON to get the next value for the insert. If I want to return the value of the ID of the record we just inserted I’d have to do this:

Oracle
INSERT INTO PERSON (ID, NAME) VALUES (SEQ_PERSON.NEXTVAL, 'GABRIEL') RETURNING ID INTO :ID;
SQLServer
INSERT INTO PERSON (NAME) VALUES ('GABRIEL');
SELECT CAST(SCOPE_IDENTITY() AS INT);

I think you get the point right? There are a lot of differences in both databases! If we had choosen to use GUID id’s our life in the DAL would be much simpler. The GUID would be generated in the DAL which would make the insert statements very similar if not equal most of the time. Returning the generated GUID would also be a piece of cake. It’s true that using numeric PK’s creates better indexes and would consume less space but I find that these improvements would’ve been superseded by the ease in development.

So to sum up, when developing a system that should be database independent I would recommend using the table’s primary key’s as GUID in order to avoid using Autoincrement and Sequences. Your DAL layer would be more uniform and you could use ANSI SQL more often.

Just in case you are wondering we did when we couldn’t use ANSI SQL we created a SQL translator that would convert all the commands create for SQLServer to Oracle and this translator is part of our DAL generator so when I create a DAO for SQLServer the generator will create its’ structure and also Oracle’s all at once.