【问题标题】:The specified type member is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supportedLINQ to Entities 不支持指定的类型成员。仅支持初始化器、实体成员和实体导航属性
【发布时间】:2012-07-20 18:04:43
【问题描述】:
var result =
    (from bd in context.tblBasicDetails
     from pd in context.tblPersonalDetails.Where(x => x.UserId == bd.UserId).DefaultIfEmpty()
     from opd in context.tblOtherPersonalDetails.Where(x => x.UserId == bd.UserId).DefaultIfEmpty()
     select new clsProfileDate()
     {
         DOB = pd.DOB
     });

foreach (clsProfileDate prod in result)
{
    prod.dtDOB = !string.IsNullOrEmpty(prod.DOB) ? Convert.ToDateTime(prod.DOB) : DateTime.Today;
    int now = int.Parse(DateTime.Today.ToString("yyyyMMdd"));
    int dob = int.Parse(prod.dtDOB.ToString("yyyyMMdd"));
    string dif = (now - dob).ToString();
    string age = "0";
    if (dif.Length > 4)
    age = dif.Substring(0, dif.Length - 4);
    prod.Age = Convert.ToInt32(age);
}

GetFinalResult(result);

protected void GetFinalResult(IQueryable<clsProfileDate> result)
{
    int from;
    bool bfrom = Int32.TryParse(ddlAgeFrom.SelectedValue, out from);
    int to;
    bool bto = Int32.TryParse(ddlAgeTo.SelectedValue, out to);

    result = result.AsQueryable().Where(p => p.Age >= from);
}

这里我遇到了一个异常:

LINQ to Entities 不支持指定的类型成员“Age”。 只有初始化器、实体成员和实体导航属性 支持。

如果 Age 不在数据库中,它是我在 clsProfileDate 类中创建的用于从 DOB 计算 Age 的属性。有什么解决办法吗?

【问题讨论】:

    标签: entity-framework-4 linq-to-entities


    【解决方案1】:

    您不能在Where 表达式中使用未映射到数据库列的属性。您必须基于映射属性构建表达式,例如:

    var date = DateTime.Now.AddYears(-from);
    result = result.Where(p => date >= p.DOB);
    // you don't need `AsQueryable()` here because result is an `IQueryable` anyway
    

    作为您未映射的Age 属性的替代品,您可以将此表达式提取到静态方法中,如下所示:

    public class clsProfileDate
    {
        // ...
        public DateTime DOB { get; set; } // property mapped to DB table column
    
        public static Expression<Func<clsProfileDate, bool>> IsOlderThan(int age)
        {
            var date = DateTime.Now.AddYears(-age);
            return p => date >= p.DOB;
        }
    }
    

    然后这样使用:

    result = result.Where(clsProfileDate.IsOlderThan(from));
    

    【讨论】:

    • 另外检查下面的 hp93 答案,from a in db.aitems select new Model { A = a.A } 在此可查询中,您必须明确指定要在 Where 中使用的所有属性,因此您可以在此处过滤或分组,或仅订购由 A,因为您只有 A 的映射 - A = a.A
    【解决方案2】:

    很多人会说这是一个糟糕的答案,因为这不是最佳实践,但您也可以在 where 之前将其转换为 List。

    result = result.ToList().Where(p => date >= p.DOB);
    

    Slauma 的答案更好,但这也可以。这花费更多,因为 ToList() 将对数据库执行查询并将结果移动到内存中。

    【讨论】:

    • 感觉很脏,但小剂量有效。
    • ToList被调用时,查询会被执行,不必要的数据会被加载到内存中。所以最好在编码上有一些限制!
    • 这对我有好处,因为我有问题的属性在 Sum Linq 函数中,而不是在 Where 子句中。因此,我没有获得不必要的数据,并且在检索到的数据上,我正在执行正在处理列表的 Linq Sum 函数。谢谢!起初看起来很糟糕的东西在某些情况下可能非常有用!
    • 这对我很有用,因为我们正在修改其中一个字段以根据另一个字段中的布尔值在其上添加数据。 (标记项目已停用)并且它拒绝工作,直到我在中间卡了一个 tolist。
    • 正是我需要的!
    【解决方案3】:

    当您不小心忘记为属性定义 setter 时,您也会收到此错误消息。 例如:

    public class Building
    {
        public string Description { get; }
    }
    
    var query = 
        from building in context.Buildings
        select new
        {
            Desc = building.Description
        };
    int count = query.ToList();
    

    对 ToList 的调用将给出相同的错误消息。这是一个非常微妙的错误,很难检测到。

    【讨论】:

    • 经过一些愚蠢的重构,这就是我的目标。
    • 但如果它是一个不可更新的 SQL 视图,我为什么需要 setter?
    • 没关系。实体框架仍然需要一种设置属性的方法。在普通的 C# 代码中,您将无法执行以下操作: Building b = new Building(); b.Description = "测试";
    【解决方案4】:

    我忘记选择列(或将属性设置/映射到列值):

    IQueryable<SampleTable> queryable = from t in dbcontext.SampleTable
                                        where ...
                                        select new DataModel { Name = t.Name };
    

    调用queryable.OrderBy("Id") 将引发异常,即使DataModel 已定义属性Id

    正确的查询是:

    IQueryable<SampleTable> queryable = from t in dbcontext.SampleTable
                                        where ...
                                        select new DataModel { Name = t.Name, Id = t.Id };
    

    【讨论】:

      【解决方案5】:

      在我的情况下,我只在生产中收到此错误消息,但在本地运行时没有收到此错误消息,即使我的应用程序的二进制文件是相同的。

      在我的应用程序中,我是using a custom DbModelStore,因此运行时生成的 EDMX 会保存到磁盘并在启动时从磁盘加载(而不是从头开始重新生成)以减少应用程序启动时间 - 并且由于我的代码我没有使磁盘上的 EDMX 文件无效 - 因此生产使用磁盘中的旧版本 EDMX 文件,该文件引用了我在异常错误消息中重命名类型名称之前我的应用程序类型的旧版本。

      删除缓存文件并重新启动应用程序修复它。

      【讨论】:

        【解决方案6】:

        在这种情况下,最简单和最好的方法之一是首先将其转换为list,然后使用whereselect

        result = result.ToList().where(p => date >= p.DOB);
        

        【讨论】:

        • 将其转换为列表意味着将返回整个对象集并在本地运行查询。这可能非常昂贵且运行缓慢。这不是一个很好的项目解决方案。您应该在回答中解释可能的副作用。
        【解决方案7】:

        在 WHERE 子句解决我的问题之前检查 Count()。它比 ToList() 便宜

        if (authUserList != null && _list.Count() > 0)
            _list = _list.Where(l => authUserList.Contains(l.CreateUserId));
        

        【讨论】:

          猜你喜欢
          • 2020-08-15
          • 2011-10-28
          • 2023-03-13
          • 1970-01-01
          • 1970-01-01
          • 2015-01-27
          • 2012-07-17
          相关资源
          最近更新 更多