使用Linq表达式计算属性

本文关键字:属性 计算 表达式 Linq 使用 | 更新日期: 2023-09-27 18:01:25

我正在使用LINQ to Entities (EF5)从现有数据库中获取和过滤产品列表。产品有一个可能的或可能的价格没有折扣(统一费率或百分比)。我希望计算发生在服务器端,这样我就可以轻松地过滤、排序和检索折扣价格,而无需编写大量重复的Linq和一些客户端代码,重新计算它。

我知道Linq不能神奇地将客户端函数转换成SQL,但我的理解是我可以写一个表达式可以转换成SQL。似乎我应该能够编写一个表达式并将该值存储为"列"。"

我已经看到了一些例子,似乎做了我想要的:

http://blog.cincura.net/230786-using-custom-properties-as-parameters-in-queries-in-ef/

包含派生属性到实体查询

问题是,我不能让表达式/属性在基本层面上工作。暂时忘记折扣计算…

下面,如果产品id大于3,我有一个简单的表达式计算为"true"。我有一个属性产品模型返回表达式的结果。当我试图获得属性,我得到一个NotSupportedException:"在LINQ到实体中不支持指定的类型成员'test'。只有初始化式、实体成员和实体导航支持属性。"

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; }
    }
}

或者,我试过

mytest = Product.testExpression.Compile()(p)

,但会抛出一个不同的NotSupported异常:" LINQ表达式节点类型'Invoke'在LINQ to Entities中不受支持。"真的没有办法吗?这似乎是一个相当普遍的需求。

下面是配置代码:
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()); }
}

使用Linq表达式计算属性

您需要将整个select封装为表达式。

public class Product
{
    public int id { get; set; }
    public string name { get; set; }
    public bool test
    {
        get { return id > 3;}
    }
    public static Expression<Func<Product, dynamic>> TestExpression = 
                    p => new
                       {
                           id = p.id,
                           name = p.name,
                           mytest = p.id > 3
                       };
}

那么你可以使用

var products = db.products
                   .Select(Product.TestExpression);

将生成一个像

这样的SQL语句
SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName], 
    CASE WHEN ([Extent1].[ProductID] > 3) THEN cast(1 as bit) WHEN ( NOT ([Extent1].[ProductID] > 3)) THEN cast(0 as bit) END AS [C1]
    FROM [dbo].[Products] AS [Extent1]
注意:你应该使用一个真正的类,而不是动态的