Introduction
ProLinq library provides you with IQueryable extension to project a query of one type to another.

How I can use it?
Just like that:
IQueryable<Product> products = ProductRepository.Get();
IQueryable<BusinessProduct> businessProducts1 = products.Project().To<BusinessProduct>();
IQueryable<BusinessProduct> businessProducts2 = products.Project().To<BusinessProduct>(p => new BusinessProduct());
IQueryable<BusinessProduct> businessProducts3 = products.Project().To<BusinessProduct>(p => new BusinessProduct
				{
					Name = p.Name + "_Business",
					CategoryId = p.CategoryId
				});


Can't I do the same using Select extension?
Not the same.
When you use Select to create projection, all it does is modifing expression tree. Actual projection is responsibility of underlying provider. But even most powerful providers like EntityFramework's one put some constraints on projections. You can't use non default constructors or assing local references to properties for example.

Ok, give me an example of what I can do with it.
Let's say you have a ProductManager class in your BLL. Now you don't want it to return simple Product entities from your DAL. You want to create new fancy BusinessProduct in OOP style. But you also don't want to lose IQueryable part - no problem:
public class ProductManager
{
	...

	public IQueryable<BusinessProduct> GetProducts()
	{
		var products = ProductRepository.Get();

		...

		return products.Project().To<BusinessProduct>(p => new BusinessProduct(this, p));
	}
}

public class BusinessProduct
{
	public BusinessProduct(ProductManager manager, Product product)
	{
		...
	}

	...

	public Int32 CategoryId { get; set; }
	public Decimal Price { get; set; }
	public Decimal DiscountPrice
	{
		get { return Price * 0.9m; }
	}
}

Then when you call this method with some filtering:
var products = productManager.GetProducts().Where(p => p.CategoryId == 1 && p.DiscountPrice < 1.1m ).ToList(); 

in database you can see an actual query having WHERE 1 = [Extent1].[CategoryId] filter. What happend is extension found a match between CategoryId properties on Both types and passed that query part to underlying provider. Though DiscountPrice doesn't exist on the Product and can't be passed to EF provider, it's filter part is applied on a later stage in memory so you get expected results.

How does it work?
In short it intercepts and rewites an original query removing parts that operate on unmatched properties of the projected type. Once query executed in underlying provider it executes the original query one more time to apply all filter logic for unmatched properties and etc.

If something's gone wrong or just Tracing
Add the following trace source to your .config file:
  <system.diagnostics>
    <sources>
        <source name="ProLinq.Projection" switchValue="Information" />
    </sources>
  </system.diagnostics>
ProLinq log information about Expression transformation so you can see what's executed on source query provider and in memory.


Last edited Jan 17, 2013 at 10:12 AM by DGolubets, version 3

Comments

No comments yet.