LINQ to Entities, Workarounds on what is not supported

by mosessaur| 31 August 2008| 15 Comments

In my previous post I talked about what is not supported in LINQ to Entities and made a simple comparison with LINQ to SQL show that these unsupported things works fine in LINQ to SQL.

Here I am going to provide workarounds these things. First one will be wrapping the entities with Business Classes. And the second way will be using client evaluation which is converting to work with LINQ to Object. Before you go further, I recommend that you read my previous post to save your time.

Using Business objects as Wrappers for Entity objects

I decided to make my model/business classes as wrappers to entity ones, and then check if this would work with LINQ to Entities or not. I found this would simplify my life a bit. And it allows me also to use my filter extension methods. But of course I'm sure there would be a better way. So feel free to share it and tell me your ideas.

To save some time, the code bellow shows my idea.

public class CategoryWrapper
{
 internal Data.Entities.Category categoryEntity;
 private IList<ProductWrapper> productsList;        
 public CategoryWrapper()
 {
     this.categoryEntity = new Core.Data.Entities.Category();
     this.productsList = null;
 }
 internal CategoryWrapper(Data.Entities.Category categoryEntity)
 {
     this.categoryEntity = categoryEntity;
     this.productsList = null;
 }
 public int ID 
 { 
     get { return this.categoryEntity.CategoryID; } 
     set { this.categoryEntity.CategoryID = value;} 
 }
 public string Name 
 {
     get { return this.categoryEntity.CategoryName; }
     set { this.categoryEntity.CategoryName = value; }
 }
 //Lazy Loading of products
 public IList<ProductWrapper> Products
 {
     get
     {
        if (this.productsList == null && this.categoryEntity != null)
        {
          this.productsList = new List<ProductWrapper>(this.categoryEntity.Products.Count);
                
          if (!this.categoryEntity.Products.IsLoaded)
              this.categoryEntity.Products.Load();

          foreach (Data.Entities.Product prod in this.categoryEntity.Products)
          {
              this.productsList.Add(new ProductWrapper(prod));
          }
      }
      else
      {
          this.productsList = new List<ProductWrapper>();
       }
       return this.productsList;
    }
    set
    {
       this.productsList = value;
    }
 }
}
Few things I want to make note about in the above code:

  • Access modifier of the member variable categoryEntity. It is internal because I don't want to expose it to public and at the same time it will be used by Repository classes resides in the same assembly.
  • The parametrized internal constructor. This is also used internally and you'll see it is usage when I want to access Category object from within one of its children products.
  • I do not add reference to Product Entity here! no need as I can access products from within the categoryEntity itself using Products property.
  • Products list property, this property returns list of related products to current category. Note that it performs lazy loading manually. I had to write this logic. However EF team made an open source project Transparent Lazy Loading for Entity Framework. You Can use it to save you some coding and logic time. I'm going to talk about in some post later.

Now how this is going to be used with LINQ to Entities? Very simple; really, here is a code snippet from the repository class that performs the query:

/// <summary>
/// Linq To Entities implementation for Categories
/// </summary>
public IQueryable<Model.CategoryWrapper> GetCategories6()
{
   var qry = from c in this.nwEntitiesContext.CategorySet
             select new Model.CategoryWrapper
             {
               categoryEntity = c
             };
   return qry;
}

I really wished if I could use the parametrized constructor instead of using object initializer. But the below is not supported as specified in my previous post.

/// <summary>
/// Linq To Entities implementation for Categories
/// </summary>
public IQueryable<Model.CategoryWrapper> GetCategories6()
{
   var qry = from c in this.nwEntitiesContext.CategorySet
             select new Model.CategoryWrapper(c);
   return qry;
}
In the service class I was able to perform filtering using my filter extension methods just as the following:
/// <summary>
/// Get the single category using filter extension method
/// </summary>
/// <returns>single category (CategoryWrapper)</returns>
public CategoryWrapper GetCategory2(int categoryId)
{
 return this.repository.GetCategories6().WithCategoryID(categoryId);
}
WithCategoryID is an extension method to IQuerable<CategoryWrapper>. Here is how it looks like:
/// <summary>
/// Filters the categories query by CategoryID
/// </summary>
/// <param name="categoryID">The Category Id to filter by</param>
/// <returns>Instance of Category</returns>
public static CategoryWrapper WithCategoryID(
      this IQueryable<CategoryWrapper> qry,
      int categoryId)
{
  return qry.First(p => p.categoryEntity.CategoryID == categoryId);
}
Note that I had to filter using categoryEntity.CategoryID  and not by p.ID. If you tried it you'll get another NotSupportedException telling you the following "The specified type member 'ID' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported." So you can add this to not supported list.

So after filtering the generated SQL would be something like this depending on your filter parameter:

exec sp_executesql N'SELECT 
[Limit1].[C1] AS [C1], 
[Limit1].[CategoryID] AS [CategoryID], 
[Limit1].[CategoryName] AS [CategoryName], 
[Limit1].[Description] AS [Description], 
[Limit1].[Picture] AS [Picture]
FROM ( SELECT TOP (1) 
	[Extent1].[CategoryID] AS [CategoryID], 
	[Extent1].[CategoryName] AS [CategoryName], 
	[Extent1].[Description] AS [Description], 
	[Extent1].[Picture] AS [Picture], 
	1 AS [C1]
	FROM [dbo].[Categories] AS [Extent1]
	WHERE [Extent1].[CategoryID] = @p__linq__1
)  AS [Limit1]',N'@p__linq__1 int',@p__linq__1=1
As you noticed earlier, CategoryWrapper business class has a property that returns list of products performing lazy loading. So if I tried to iterate through this list at any time, the generated SQL in addition to the above generated one will be:
exec sp_executesql N'SELECT 
1 AS [C1], 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName], 
[Extent1].[SupplierID] AS [SupplierID], 
[Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
[Extent1].[UnitPrice] AS [UnitPrice], 
[Extent1].[UnitsInStock] AS [UnitsInStock], 
[Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
[Extent1].[ReorderLevel] AS [ReorderLevel], 
[Extent1].[Discontinued] AS [Discontinued], 
[Extent1].[CategoryID] AS [CategoryID]
FROM [dbo].[Products] AS [Extent1]
WHERE ([Extent1].[CategoryID] IS NOT NULL) AND ([Extent1].[CategoryID] = @EntityKeyValue1)',N'@EntityKeyValue1 int',@EntityKeyValue1=1
You can come up with better ideas than this one. But it is so far stratifies my needs. Please if you have any optimization ideas or thoughts about this please share it.

Converting to client evaluation, LINQ to Objects way

As I mentioned in pervious post, Diego Vega, a Program Manager at Entity Framework Team suggested a solution to me 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.

He also provided me with some code snippets which I am going to present below to show his idea. Below is sample after I modified it to match my needs:

public IQueryable<Model.Category> GetCategories7()
{
    
    var qry = from c in nwEntitiesContext.CategorySet.AsEnumerable()
              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.AsQueryable();
}
As you can see, now I am using AsEnumerable() to convert to client evaluation. But this will cause one small issue which is All Categories will be retrieved from the Database. However products will be filtered properly.

Here the method that invokes the above method:

public Category GetCategory3(int categoryId)
{
   return this.repository.GetCategories7().WithCategoryID(categoryId);
}
Note that here I am able to apply filter again. However as I mentioned the filter will be based on client evaluation. Which means that all categories will be retrieved and LINQ will apply the filter in memory.

The generated SQL would be:

SELECT 
[Extent1].[CategoryID] AS [CategoryID], 
[Extent1].[CategoryName] AS [CategoryName], 
[Extent1].[Description] AS [Description], 
[Extent1].[Picture] AS [Picture]
FROM [dbo].[Categories] AS [Extent1]
And if you tried to load the products and additional statement will be executed on demand -I'm applying lazy loading- and here is how it will look like:
exec sp_executesql N'SELECT 
1 AS [C1], 
[Extent1].[CategoryID] AS [CategoryID], 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName]
FROM [dbo].[Products] AS [Extent1]
WHERE [Extent1].[ProductID] = @p__linq__1',N'@p__linq__1 int',@p__linq__1=1
Conclusion

So, there is a solution or workaround for any issue. Might not be the best, but soon you'll find or discover a better way. Myself I prefer for the moment to use my business classes as wrapper for Entity ones. But also I consider converting to client evaluation another easy and good working solution for many cases.

Feel free to provide me with your feedback and your thoughts. I need every idea that would help.

You can download the test project to check the full code and evaluate the generated SQL. This project is a modified one of the previous one I made for my first post in this subject.

kick it on DotNetKicks.com

Comments

Moses' Blog on 8/28/2008 2:17 AM Trackback from Moses' Blog

LINQ to Entities, what is not supported?
trackback
DotNetKicks.com on 8/29/2008 5:20 AM Trackback from DotNetKicks.com

LINQ to Entities, Workarounds on what is not supported
pingback
alvinashcraft.com on 8/29/2008 5:23 AM Pingback from alvinashcraft.com

Dew Drop - September 1, 2008 | Alvin Ashcraft's Morning Dew
Corey Gaudin
Corey Gaudin United States on 8/31/2008 12:03 PM The bad thing about the second way (i.e. the Client side evaluation), besides the mass amounts of data you need to pull into memory, is that now you are segregating your methods into some, like GetCategory7() with a AsEnumerable() and the GetProducts() with no AsEnumberable(). So you are segregating how you handle each pull from the Data Store based on what is the Main object, and its child references, which isnt all that bad if you have a repository per main object call.

However, supposebly you would have a Product Repository which has to duplicate what you are pulling from your Category Repository but with an AsEnumerable to correctly shape it.

This will make for duplicate logic in pulling data depending on if you need that entity as a child reference or base object.

Based on that alone, I would stick with the first option, which is basically a Virtual Proxy Pattern on top of each entity object. The problem here is the mass amount of mock up for each Property, especially when you already went through the trouble of creating your Model in the EDM, so now you have do it it through there, and now this wrapper proxy class. Again, duplicate work.

Unfortunately, I hate to say it, but the third way it seems is just using Entity Framework as the Repository object that calls into each entity is probably the less work way, but couples you completely to EF. You can then marry your Business rules and logic to the partial of each entity. This also allows you to utilize ADO.NET Data Services in a seamless way to expose your domain layer to multiple applications (which you can do with the first and second way but you will need to implement IUpdateable in your Repository -> which can be a pain).

There is no "utimate" way to do this at this time. But to me, saving work up front, especially when it could change for v2 is probably a simpler way. But layered wise, the proxy pattern approach is definately more architecturally sound, the problem is you are back to building out your DTO objects that match your entity model.

I dont know, but for situations where I want to utilize ADO.NET Dataservices, approach 3 seems the better route now, I just hope when POCO objects make there way, I dont get burnt in having to re-write much.

Essentially, the typical data layer model has you making a Repository Layer, a Service Layer (your business layer), and the application Layer (your MVP/MVC layer).

By utilizing EF at the Repository Layer, you completely couple yourself at that layer to EF, but gain the flexibility to easily change your model independently from your data schema (which is typically what the Repository does anyway).

I am testing out using the ADO.NET DataService as the service layer, since you can inject before query calls, and on updates/creates/etc. Unfortunately, to fully utilize my rules logic, it is simpler to push that into the partial for each entity, and throw an exception which I can trap in the DataService layer and respond out.

From there my Applications Model Layer in a MVC application will handle making a Application Business Layer that is specific for that application that simply consumes the ADO.NET Data Service to get the data and utilize the entity in the application. So rather than trying to make my Domain Layer with EF/ADO.NET Data Services be the business rules for everything, it just handles making sure my domain rules are correct for my model, and the application itself handles that its business rules are correct for that specific application (since this model is for multiple applications to consume this -- it cant be everything to everyone, but it can make sure it is valid at a domain layer).

Hope this helps, this is just some of the things I have played around with. Maybe we  can poke holes in both our ideas enough to come up with a cleaner solution.
mosessaur
mosessaur Egypt on 8/31/2008 12:19 PM @ Corey Gaudin Thank you so much for your detailed comment and comprehensive explanation.
In fact I used proxy because I didn't like how Entity Classes look like when I expose them to Presentation. Also I want it my Business Classes to be very simple and abstract. Entity Classes contains a lot of things that is totally not needed by developers who will work with presentation layer, and I want these stuff to be hidden from them.
I'll keep checking your blog frequently as you might reach to something interesting that would change my way or light some ideas for me to enhance my way.
Corey Gaudin
Corey Gaudin United States on 8/31/2008 12:21 PM FYI Muhammad,

I linked to this post and comment from my blog to see if we can get some community members to help shape some ideas on this.
http://polymorphicview.blogspot.com/
mosessaur
mosessaur Egypt on 8/31/2008 12:30 PM @Corey Gaudin That was a good idea. Currently I am testing my 1st approach on some practical example. Yes it contains some duplicate work, but it also performs some kind of isolation between Data Layer and other layers.
So I'll keep updating regarding this ideas as I go.
Corey Gaudin
Corey Gaudin United States on 9/1/2008 11:31 AM I guess my idea is more suited towards a SOA type architecture where you have multiple applications consuming this Domain Layer. So each application would need its own business layer that consumes the Domain Service Layer.

If you are building a Data Layer and Service Layer just for one app, I do think the proxy wrapper is a good mechanism to hide away the EF, especially if you dont like all the extra stuff that comes with the entities.

However, I have noticed that ADO.NET Data Services does hide away some of the crude from the Entities when it serializes it. So in a way, for a Service Oriented Architecture, it does get you that too.
Dave Craft
Dave Craft United Kingdom on 11/23/2008 10:42 AM Hey Muhammad,

I'm doing the exact same thing as you, basically trying to convert Rob Conery's MVC Storefront example and make it work with Linq to Entities. I ran in to all the problems you did. I got round it in a different way.

Instead of setting up the LazyList in the repository i decided to do it in the Service Layer instead so you end up with something like this:

/// <summary>  
/// Get the single category using filter extension method  
/// </summary>  
/// <returns>single category (CategoryWrapper)</returns>  
public Category GetCategory9999(int categoryId)  
{  
  Category result = this.repository.GetCategories().WithCategoryID(categoryId);  
result.Products = new LazyList(this.repository.GetProducts().WithCategoryID(categoryId);

}  

This works ok for me. This does mean though you have to setup alot more filters. I'm happy with it though as i get lazy loading and i can use my own entities.

Hope this helps any other Rob Conery fans out there.


trackback
DotNetShoutout on 4/19/2009 7:29 PM LINQ to Entities, learn workarounds on what is not supported!

Thank you for submitting this cool story - Trackback from DotNetShoutout
Gabriel Perez
Gabriel Perez Brazil on 4/21/2009 6:00 AM Very good post. I had the problem and a different approach to it. If you are interested here is the link:

www.gbogea.com/.../mvc-storefront-migrating-to-the-entity-framework

I'll be very happy to hear you thoughts on it.
Daniel
Daniel Switzerland on 5/4/2009 8:03 PM Hi,

I'm trying to use the evaluation on the client side, but I don't understand why I have to use a join between the two entities as the "service" entity has a link to the "serviceType" entity in the DB.

public IQueryable<Service> GetServices()
        {
            ObjectQuery<MaSalle.DataAccess.Service> services = context.Service;
            ObjectQuery<MaSalle.DataAccess.ServiceType> serviceTypes = context.ServiceType;//.Include("ServiceType");

            IEnumerable<Service> query = from service in services.AsEnumerable()
                                         join serviceType in serviceTypes
                                         on service.ServiceType.Id equals serviceType.Id
                                         select new Service
                                        {
                                            Id = service.Id,
                                            Name = service.Name,
                                            ServiceType = this.GetServiceTypes().Where(x => x.Id == service.ServiceType.Id).FirstOrDefault()
                                        };

            return query.AsQueryable<Service>();
        }

Why is it not the same as for category and products?
mosessaur
mosessaur Egypt on 5/4/2009 9:37 PM As I understood from your post, Service and ServiceType are related! so why you just do
var services = context.Service.Include("ServiceType");

This will load ServiceType along with each service instance
Daniel
Daniel Switzerland on 5/5/2009 5:35 AM yes, that's working with include to, but I thought that if there is a link in the DB, the Linq to Entities model knows the path and loads the two tables automatically.

Is Linq to Entities using lazy loading always? Can we change between eager/lazy in some cases?
trackback
VS2010学习 on 6/15/2009 9: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

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading