LINQ to Entities, what is not supported?

by mosessaur| 24 August 2008| 18 Comments

Recently and after .Net 3.5 SP1 & Visual Studio.Net 2008 SP1 released, I started to play with LINQ to Entities. Really Entity Framework is interesting. Maybe not the best ORM tool, but still it is interesting and easy to use plus it is all one IDE at the end.

At the same time I was watching screen-casts made by Rob Conery about Building MVC Storefront along with TDD. In fact he was using LINQ to SQL. And I liked his coding techniques presented in his sample application and screen-casts.

I decided to play around with LINQ to Entities with MVC. Here I am going to talk about my adventures with LINQ to Entities, and really it is completely different from working with LINQ to SQL.

My recommendation for every developer is to start by reading Supported and Unsupported Methods (LINQ to Entities). Yes it will save you sometime. Do You know LINQ to SQL? Then you are familiar with LINQ. But you need to know what there and what is not there in LINQ to Entities. This even Might help you to build a wish list for LINQ to Entities also ;O).

Here I'm going to use some code samples presented in MVC Storefront and show some of the techniques that will not work with LINQ to Entities. And I will compare code with LINQ to SQL. In some other post, I'm going to present some other workarounds provided by ADO.NET Team when I asked them about the issues I have with LINQ to Entities. Also I'll present my own workarounds. And I'll compare the Generate SQL statements. I am not query optimizer, so I will not say what is the best choice. But you can help me in that.

Project Structure

When I started I decided not to use the generated entities as Business Objects. I wanted to keep them as Data Objects and not expose them to the Presentation. So I had to make another set of classes that should work as my Business objects. For demonstration I built a simple project structure as shown below:

LINQ2SQL_LINQ2Entities_ProjectStructure 

You can download this project. Here is an explanation of this project structure to be more clarified:

  • Core.Data.Entities & Core.Data.LinqToSql (Data Classes) namespaces
    Namespaces that contain Entity Classes generated by Entity Framework and LINQ to SQL Classes generated by LINQ to SQL generated.
  • Core.Data.Repository namespace
    Contain Data Services classes that is responsible for querying the Data Classes using LINQ.
  • Core.Model namespace
    Business Classes, very simple.
  • Core.Business.Services namespace
    Business services classes that is supposed to be directly used by presentation. I'm using them in Test Project as well. They interact with classes in Core.Data.Repository namespace, but really should know anything about Core.Data.* namespaces.
  • Core.Model.Filters namespace
    Extension Methods classes to perform LINQ filters over returned results. This is to simplify queries but applying filter over generic queries.

LINQ to SQL, where everything is possible:

Simply I'm using 2 tables from Northwind database, Categories and Products. Lets see how to query those 2 tables using LINQ to SQL with the structure show above. I have a Class under Core.Data.Repository called CatalogRepositoryLinqToSql. It worth to mention that I made all classes in my generated LINQ to SQL as internal. Here is the code:

internal partial class CatalogRepositoryLinqToSql : IDisposable
{
    NorthwindDataContext nwDataContext;
    
    private CatalogRepositoryLinqToSql(NorthwindDataContext dataContext) 
    {
        nwDataContext = dataContext;    
    }
    public static CatalogRepositoryLinqToSql CreateRepository()
    {
        return new CatalogRepositoryLinqToSql(new NorthwindDataContext());
    }
    /// <summary>
    /// Linq To Sql implementation for Categories
    /// </summary>
    /// <returns>IQueryable of Categories</returns>
    public IQueryable<Model.Category> GetCategories()
    {
        var qry = from c in nwDataContext.Categories
                  let products = this.GetProducts().WithCategoryID(c.CategoryID)
                  select new Model.Category
                  {
                      ID = c.CategoryID,
                      Name = c.CategoryName,
                      Products = new Model.LazyList<Core.Model.Product>(products)
                  };
        return qry;            
    }        
    /// <summary>
    /// Linq To Sql Implementation for Products
    /// </summary>
    /// <returns></returns>
    public IQueryable<Model.Product> GetProducts()
    {
        var qry = from p in nwDataContext.Products
                  select new Model.Product
                  {
                      CategoryID = p.CategoryID,
                      ID = p.ProductID,
                      Name = p.ProductName
                  };
        return qry;
    }
    public void Dispose()
    {
        if (nwDataContext != null)
        {
            nwDataContext.Dispose();
        }
    }
}

I don't want to go through explanation of the code. You can watch early screen casts of MVC Storefront to have better understanding of the above code. But here are few hints:

  • GetProducts method it returns a LINQ query (IQueryable<Mode.Product>). It builds the query then I can use my Filter Extension Method WithCategoryID for (IQueryable<Mode.Product>), to get Products of certain category. The reason why I do not hit Category.Products in LINQ to SQL generated code is because it will hit the Database, and I wanted to apply lazy loading.
  • Model.LazyList is custom class to apply lazy loading for Business Class.

When the above methods (in code) are invoked, everything move is just fine. And the database is hit exactly when needed. The Repository Classes are consumed by the Business Services Classes. So for the above class I have a service in Core.Business.Services called CatalogServiceForLinqToSql which invokes the the LINQ to SQL repository class as the following:

public class CatalogServiceForLinqToSql : IDisposable
{
    private CatalogRepositoryLinqToSql repository;

    public CatalogServiceForLinqToSql()
    {
        repository = CatalogRepositoryLinqToSql.CreateRepository();
    }
    /// <summary>
    /// Get the categories from the repository
    /// </summary>
    /// <returns>List of categories</returns>
    public IList<Category> GetCategories()
    {
        return this.repository.GetCategories().ToList();
    }
    /// <summary>
    /// Returns a single category by ID
    /// </summary>
    /// <param name="id">The Category ID</param>
    /// <returns>Category</returns>
    /// <remarks>
    /// Possible generated SQL:
    /// "exec sp_executesql N'SELECT [t0].[CategoryID] AS [ID], ....
    /// FROM [dbo].[Categories] AS [t0]
    /// WHERE [t0].[CategoryID] = @p0',N'@p0 int',@p0=1"
    /// 
    /// If Products property of Category class is called Lazy loading
    /// will take place
    /// Additional generate SQL:
    /// "exec sp_executesql N'SELECT [t0].[CategoryID],....
    /// FROM [dbo].[Products] AS [t0]
    /// WHERE [t0].[CategoryID] = @p0',N'@p0 int',@p0=1"
    /// </remarks>
    public Category GetCategory(int categoryId)
    {
        return this.repository.GetCategories().WithCategoryID(categoryId);
    }
    #region IDisposable Members
    public void Dispose()
    {
        if (this.repository != null)
        {
            this.repository.Dispose();
        }
    }
    #endregion
}
You'll notice that I am using the Extension Method WithCategoryID when trying to return a Category filtered by its ID. I commented the code and provided the dynamically generated SQL for reference.

Now if you tried to return products of a certain category, you just need to access Products property of Core.Model.Category Class. Note that this is lazy loading, which means products are not initially loaded when you retrieved the category. It is just loaded when you want to access the products.

LINQ to Entities, not everything considered common sense!

Well, at first time I thought that if I just reused the code above but instead of using LINQ to SQL I would use LINQ to Entities I would get the same results and I am Ok! well done! But the answer is NO! this will not work. Now I am going to explore different things that are not Supported by LINQ to Entities, but in fact they are supported by LINQ to SQL.

To redo the work with EF, I created Entities classes for the same tables from northwind (Categories and Products).
Then created another Repository Class Core.Data.Repository.CatalogRepositoryLinqToEntities that works exactly the way the LINQ to SQL repository class work. Also a Business Service Class is created CatalogServiceForLinqToEntities.

Custom Methods & Extension Methods cannot be translated into a store expression

What is that?! well ok have a look at the following code snippet from CatalogRepositoryLinqToEntities class:

/// <summary>
/// Linq To Entities implementation for Categories
/// </summary>
/// <returns>IQueryable of Categories</returns>
/// <remarks>
/// Will throw System.NotSupportedException: 
/// LINQ to Entities does not recognize the method 
/// 'System.Linq.IQueryable`1[Core.Model.Product] WithCategoryID(System.Linq.IQueryable`1[Core.Model.Product], Int32)' method, 
/// and this method cannot be translated into a store expression
/// 
/// Explanation:
/// LINQ to Entities didn't recognize the my custom filter 
/// Extension Method IQueryable(of T).WithCategoryID(int)
/// </remarks>
public IQueryable<Model.Category> GetCategories1()
{
    var qry = from c in nwEntitiesContext.CategorySet
              let products = this.GetProducts().WithCategoryID(c.CategoryID)
              select new Model.Category
              {
                  ID = c.CategoryID,
                  Name = c.CategoryName,
                  Products = new Model.LazyList<Core.Model.Product>(products)
              };
    return qry;
}

I commented the code for better explanation. On line 18, I'm making a calls to GetProducts method then trying to apply filter using WithCategoryID Extension Method. Exactly the same way as I did in LINQ to SQL. And I got an exception NotSupportedException. And the explanation is mentioned in the code comment.

So I made a work around, thinking myself smart enough to get away from this exception. I changed line 18 with the following:

let products = this.GetProducts().Where(p => p.CategoryID == c.CategoryID)
And I got the same exception but this time it LINQ to Entities didn't recognize GetProducts Method.

When returned to ADO.NET team regarding this issue and in fact they responsed in no time. Diego Vega, a Program Manager at Entity Framework Team sent me the following explanation of the above issue:

"By design, LINQ to Entities requires the whole LINQ query expression to be translated to a server query. Only a few uncorrelated subexpressions (expressions in the query that do not depend on the results from the server) are evaluated on the client before the query is translated. Arbitrary method invocations that do not have a known translation, like GetProducts() in this case, are not supported."

LINQ to Entities only support Parameterless constructors and Initializers

So lets continue with the rest of issues I faced. Diego also suggested to make a sub query for products instead of directly invoking a method from within a a LINQ query. So I can do something like this:

var productsQuery = this.GetProducts();
var qry = from c in nwEntitiesContext.CategorySet
	let products = productsQuery.Where(p => p.CategoryID == c.CategoryID)
	.....
In face this should work, but now there is another issue; have a look at the following:
/// <summary>
/// Linq To Entities implementation for Categories
/// </summary>
/// <returns>IQueryable of Categories</returns>
/// <remarks>
/// Will throw System.NotSupportedException: 
/// Only parameterless constructors and initializers are supported in 
/// LINQ to Entities.
/// 
/// Explanation:
/// Because I am trying to initialize LazyList with new LazyList(IQuerable)
/// </remarks>
public IQueryable<Model.Category> GetCategories3()
{
    var productsQuery = this.GetProducts();
    var qry = from c in nwEntitiesContext.CategorySet
            let products = productsQuery.Where(p=>p.CategoryID==c.CategoryID)
            select new Model.Category
            {
                ID = c.CategoryID,
                Name = c.CategoryName,
                Products = new Model.LazyList<Core.Model.Product>(products)
            };
    return qry;
}
The issue is with line 22. I am trying to instantiate a new instance of LazyList using its parameterless constructor. Just as I did in LINQ to SQL. But LINQ to Entities only support parameterless constructors and initializers.

IEnumrable types cannot be initialized in LINQ to Entities query

Another attempt to be smart, I decided to work around the issue above by using LazyList object initializer but that just didn't work out. Check it out below:

/// <summary>
/// Linq To Entities implementation for Categories
/// Workaround for GetCategories3
/// </summary>
/// <returns>IQueryable of Categories</returns>
/// <remarks>
/// Will throw System.NotSupportedException: 
/// A type that implements IEnumerable 'Core.Model.LazyList`1' 
/// cannot be initialized in a LINQ to Entities query.
/// 
/// Explanation:
/// Cannot make new instance of anything that implements IEnumerable
/// </remarks>
public IQueryable<Model.Category> GetCategories4()
{
    var productsQuery = this.GetProducts();
    var qry = from c in nwEntitiesContext.CategorySet
           let products = productsQuery.Where(p=>p.CategoryID==c.CategoryID)
           select new Model.Category
           {
               ID = c.CategoryID,
               Name = c.CategoryName,
               Products = new Model.LazyList<Core.Model.Product>
               {
                   query = products
               }
           };
    return qry;
}
The Solution Suggested

Well Diego also suggested me a solution which is to switch to client evaluation of LINQ queries (LINQ to Objects). This will require to invoke AsEnumrable method for my LINQ to Entities queries. He also mentioned that in LINQ to SQL, the switch to client side evaluation is implicit and always happens in the outermost projection.

Check out my next post where I explored this solution as well as providing my own one. For now you can download the attached project which include a test project to be able to observe what I presented here.

Conclusion

Entity Framework is really interesting. But I can say it is the first version, still a baby, but I believe that this baby will grow up one day and become a good man ;o) that can take over great responsibilities.

kick it on DotNetKicks.com

Comments

trackback
DotNetKicks.com on 8/19/2008 12:12 AM Trackback from DotNetKicks.com

LINQ to Entities, what is not supported?
Corey Gaudin
Corey Gaudin United States on 8/19/2008 11:07 PM Muhammad,

I have ran down the EXACT same paths as you trying to switch out a LINQ to SQL project to EF. I had utilized alot of things from the MVC Storefront, and ran into the same types of problems, and tried to fix them the same type of ways. I also read the responses that the EF team presented. To get around it, I ended up having to push more business domain logic into the partial classes of EF to get it to fully work with ADO.NET Data Services (since you run into another set of problems there using IUpdateable and IExpandProvider with the Entities being used as DTOs). All in all, I have learned that for release 1 of these things, you have to couple everything to it to make it work correctly, unless you want TONS of headache and more work - or keep with LINQ to SQL and WCF using the Storefront DTO Business Object projection from your Repository.

I am curious to see if you found some neat ways to tackle this without having to client side project tons of data and filter after the fact in you projections. It just seemed to me that you cant easily Project into custom objects well from EF. Hence, why I am looking forward to V2 of EF where it supports POCO Objects where the business object itself becomes the entity (and the EDM becomes the repository, and ADO.NET Data Service becomes the Business Service layer).
mosessaur
mosessaur Egypt on 8/19/2008 11:15 PM @ Corey Thank you for sharing your experience with EF. Actually my first work around that I'll propose in my next post is to wrap the Entity Class instances in my Business Classes. I tried it and it works fine so far. The other option is to switch to Client Evaluation. But this will result in poor data performance I guess. As it will result in retrieving the whole data form DB and perform filtering in memory.
I think V2 will be much mature, but this might make us wait for some long time Smile
Corey Gaudin
Corey Gaudin United States on 8/19/2008 11:17 PM Also, what are your feelings on EF in terms of growth. It seems that most things that EF will tie into  such as ADO.NET Data Services, Reporting Portals, SQL Server 2008, will want to tie into EF directly and not want to be abstracted out into your own objects. It seems microsoft is moving in a direction that utilizes the CDL generated to make assumptions about your model. So it seems microsoft wants the CDL or EDM to be the center of your business / Domain layer. Which means that they want you to consider EF as the Repository and not abstract that data layer out.

Just wondering your thoughts. It just seems to the fully utilize what EF will become with other products, you must drop your own Repository Pattern and put EF in front of it utilizing its Entities (which can be POCO later). Of course you will still have your own Service Layer that the controller/presentation layer can call (be it WCF, ADO.NET Data Services, or just a class wrapper around EF doings). It just seems that to tie it into these futurables, you need EF more viewable and coupled in your projects (which is bad in one way, and not so bad in another since it can utilize any datasource - XML, MySQL, Oracle, etc.).
mosessaur
mosessaur Egypt on 8/20/2008 6:05 PM @ Corey I really don't know where EF would go?! or What is the Plan of EF team in general?! Sometimes I ask, why LINQ to SQL and EF. Why not only one solution?! There is a difference between both but what if LINQ to SQL support several Engines and Data Stores? Because LINQ to SQL shows a great example of Data Layer. But when you worked with EF, you might get confused. while it is just another generated Data Layer.
Maybe one day LINQ to SQL and Entity Framework will be merged in one solution. Who knows.
Martin Golding
Martin Golding United Kingdom on 8/21/2008 12:38 AM Hi.

I am using EF and LINQ and have an issue trying to format columns to a UI with objects gained from EF/LINQ. I want to take 2 column (one string and one int) and output a single column as a string ( eg s1 + i1.ToString() )

Reading your comment below, when you say switch to client evaluation, how can that be achived? I need to use EF, can I evaluate the EF result and then apply Linq to objects to form my view to the UI?

>Well Diego also suggested me a solution which is to switch to client
>evaluation of LINQ queries (LINQ to Objects).

thanks
Martin
mosessaur
mosessaur Egypt on 8/21/2008 9:23 AM @ Martin
For your first question, you just need to to have a continue your entity partial class and add a read only property to it. Then in the get accessor you can do the concatenation e.g:
public string CompositColumn
{
   get{ return this.Column1 + this.Column2.ToString();}
}
Regarding client evaluation, I'm going to write about that during this week, I started already building the sample. But here is the code submitted to me regarding LINQ to Objects:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication32
{
    class Program
    {

        static readonly NorthwindEntities context = new NorthwindEntities();
        static void Main(string[] args)
        {
            var result = from c in context.Categories.AsEnumerable()
                         let products = GetProducts(c.CategoryID)
                         select new Category
                         {
                             ID = c.CategoryID,
                             Name = c.CategoryName,
                             Products = products
                         };

            foreach (var c in result)
            {
                Console.Write(c.Name);
                Console.Write(" ");
                Console.WriteLine(c.Products.Count());
            }

        }

        public static IEnumerable<Products> GetProducts(int CategoryID)
        {
            return from p in context.Products
                   where p.Categories.CategoryID == CategoryID
                   select p;
        }

        public class Category
        {
            public int ID;
            public string Name;
            public IEnumerable<Products> Products;
        }
    }
}
Hope that helps
trackback
Moses' Blog on 8/25/2008 11:19 PM Trackback from Moses' Blog

LINQ to Entities, Workarounds on what is not supported
Fred Morrison
Fred Morrison United States on 10/31/2008 9:00 PM Your sample project refers to a database called NorthwindMod.  Please provide a link to either a downloadable MDF+LDF file pair or a sql script that will create this (apparently) "modified" Northwind database.
mosessaur
mosessaur Egypt on 11/1/2008 3:17 PM @Fred Morrison It is the same Original Northwind Database. I just have 2 northwind DBs on my local machine one to modify its data and the other to remain fixed. So just use the original one and change the connection string to map to your northwind database.
pingback
goneale.wordpress.com on 12/10/2008 10:15 PM Pingback from goneale.wordpress.com

Just another.. ‘Linq to SQL vs. Entity Framework’ Post on the Internet « {Programming} & Life
Keith J. Farmer
Keith J. Farmer United States on 12/30/2008 1:56 PM Regarding the first block, where you are attempting a nested query, storing the results using the let keyword, have you viewed the log output in the LINQ to SQL case?  I'm going on a hunch that you'll see many repeated queries, and that would be LINQ to SQL lifting the client-side operation, which Diego mentions will be done implicitly.

The problem is that while query composition works just fine at the outermost layer:

var q0 = db.FooQuery();
var q1 = q0.Where(...);

... it doesn't necessarily compose if you embed:

var q2 = db.BarQuery().Where(x => x == q0.First());

The reason for this is that what you *want* to compose are Expression objects, but what you're actually outputting are IQueryables which wrap expressions.  

Judging from Diego's response, LINQ to Entities chooses not to implicitly lift the operation.  It's a trade-off regarding behavior expectations and performance.

I will say that the code in LINQ to SQL that goes through and does that work is not necessarily trivial.  I will also say that I wish we'd been able to figure out a better means for composing query expressions post-hoc.
trackback
DotNetShoutout on 4/18/2009 4:27 PM LINQ to Entities, Learn about what is not supported!

Thank you for submitting this cool story - Trackback from DotNetShoutout
trackback
VS2010学习 on 6/13/2009 3:19 AM First Steps toward Test Driven Design\Development

Introduction: I'm totally new to TDD world. I saw many sample open source projects that based on
grow taller
grow taller United States on 6/22/2009 8:37 AM This technology is going to change the way we write SQL and anlyze objects.. can't wait until it becomes mainstream.
koozie
koozie United States on 7/2/2009 8:09 AM IMHO my purpose is understood(you can use List<int> instead of int[] and then you'll have contains)I agree this is not the correct answer, but I did my best and even checked that it compiles (in LINQ not LINQ to Entities). My answer could help anyway
pingback
topsy.com on 8/24/2009 9:17 PM Pingback from topsy.com

Twitter Trackbacks for
        
        Moses' Blog | LINQ to Entities, what is not supported?
        [mosesofegypt.net]
        on Topsy.com
pingback
kintespace.com on 11/23/2009 6:32 PM Pingback from kintespace.com

the rasx() context  » Blog Archive   » Flippant Remarks about Wrapping Entity Framework with a WCF Service
Comments are closed