自定义属性和lambda表达式

时间:2013-02-06 10:06:26

标签: c# .net linq linq-to-sql lambda

我已经为LINQ-to-SQL实体添加了一个自定义属性:

public partial class User
{
    public bool IsActive
    {
        get
        {
            // note that startDate and endDate are columns of User table
            return startDate <= DateTime.Now && endDate >= DateTime.Now;
        }
    }
}

现在我需要将此属性与lambda表达式一起使用:

activeUsers = users.Where(u => u.IsActive);

但是当执行此代码时,我得到System.NotSupportedException。异常消息表明“不支持成员'User.IsActive'的SQL转换”

有没有办法解决这个问题?

3 个答案:

答案 0 :(得分:6)

您显示的IsActive是常规C# - 它将被编译为IL,并且不可用于LINQ-to-SQL(等)以检查并转换为TSQL以在数据库中执行。这里的一个选择可能是:

public static Expression<Func<User,bool>> GetIsActiveFilter() {
    return user => user.StartDate <= DateTime.Now &&
                   user.EndDate >= DateTime.Now;
}

然后你应该可以使用:

activeUsers = users.Where(User.GetIsActiveFilter());

或类似地 - 也许是一种扩展方法:

public static IQueryable<User> ActiveOnly(this IQueryable<User> users) {
    return users.Where(user => user.StartDate <= DateTime.Now &&
                               user.EndDate >= DateTime.Now);
}

然后:

activeUsers = users.ActiveOnly();

这里的不同之处在于我们始终使用IQueryable<T>接口和表达式树,这允许LINQ理解我们的 intent ,并创建合适的TSQL实现同样的目标。

答案 1 :(得分:2)

您可以尝试以下操作:

activeUsers = users.AsEnumerable().Where(u => u.IsActive);

请注意,标准IsActive将不会转换为SQL,因为从数据库角度来看,不了解IsActive背后的逻辑。因此,数据库将对整个表用户执行读取,并且标准将应用于内存中。

答案 2 :(得分:1)

我用这种方式解决了类似的问题:

我的ORM是NHibernate,底层数据库是Oracle,当我需要添加一些像IsActive这样的字段时,确实是这样的事情

public partial class User
{
   public bool IsActive{get;set;}
}

在相关的映射文件中添加类似这样的内容

<property name="IsActive" access="property" insert="false" update="false"
  formula="case when startDate  <= sysdate And endDate  >= sysdate then 1 else 0 end" />

现在可以使用

activeUsers = users.Where(u => u.IsActive);