I'm using LINQ to Entities (EF5) to get and filter a list of products from an existing database. Products have a price which may or may
not have a discount (flat rate or percentage). I'd like a calculation to happen server-side so that I can easily filter, sort, and
retrieve the discounted price without writing a lot of duplicated Linq with some client code on top that re-calculates it yet again.
I know that Linq can't magically translate client-side functions into SQL, but it's my understanding that I could write an Expression
that can get translated to SQL. It seems like I ought to be able to write an Expression and store that value as a "column."
I've seen some examples that appear to do what I want:
http://blog.cincura.net/230786-using-custom-properties-as-parameters-in-queries-in-ef/
Include derived property into a linq to entity query
Trouble is, I can't get the expression/property to work on a basic level. Forget the discount calculation for a moment...
Below, I have a simple expression to evaluate as "true" if the product id is greater than 3. I have a property in my
Product model to return the result of the expression. When I try to get the property, I get a NotSupportedException:
"The specified type member 'test' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation
properties are supported."
public partial class ProductList
{
protected void Page_Load(object sender, EventArgs e)
{
using (var db = new eTailerContext())
{
var products = db.products
.Select(p => new
{
id = p.id,
name = p.name,
mytest = p.test
});
}
}
}
public class Product
{
public id { get; set; }
public name { get; set; }
public bool test
{
get { return testExpression.Compile()(this); }
}
public static Expression<Func<Product, bool>> testExpression
{
get { return t => t.id > 3; }
}
}
Alternatively, I've tried
mytest = Product.testExpression.Compile()(p)
but that throws a different NotSupported exception: "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."
Is there really no way to do this? It seems like it would be a pretty common need.
Here is the configuration code:
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
this.HasKey(t => t.id);
this.ToTable("Products");
this.Property(t => t.id).HasColumnName("ProductID");
this.Property(t => t.name).HasColumnName("ProductName").HasMaxLength(255);
this.Ignore(t => t.test);
}
}
public class MyDbContext : DbContext
{
public DbSet<Product> products { get; set; }
public MyDbContext() : base("Name=SiteSqlServer")
{ }
static MyDbContext()
{
Database.SetInitializer<MyDbContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Configurations.Add(new ProductMap()); }
}