什么导致Linq错误:此方法无法转换为存储表达式?

6

我有一堆Linq to Entity方法,它们都有相同的select语句,所以我觉得我可以聪明地将它们分离成它自己的方法来减少冗余......但是当我尝试运行代码时,我收到了以下错误信息...

这个方法无法转换为存储表达式

这是我创建的方法...

public User GetUser(DbUser user, long uid)
{
    return new User
    {
        Uid = user.uid,
        FirstName = user.first_name,
        LastName = user.last_name
    };
}

我正在以以下方式调用一个方法...
public User GetUser(long uid)
{
    using (var entities = new myEntities()) {
        return
            entities.DbUsers.Where( x => x.uid == uid && x.account_status == ( short )AccountStatus.Active ).
                Select( x => GetUser( x, uid ) ).FirstOrDefault( );
    }
}

更新:以下是可在行内工作的代码

public User GetUser(long uid, long uid_user)
        {
            using (var entities = new myEntities())
            {

                var q = from u in entities.DbUsers
                        where u.uid == uid_user
                        select new User
                        {
                            Uid = u.uid,
                            FirstName = u.first_name,
                            LastName = u.last_name,
                            BigPicUrl = u.pic_big,
                            Birthday = u.birthday,
                            SmallPicUrl = u.pic_small,
                            SquarePicUrl = u.pic_square,
                            Locale = u.locale.Trim(),
                            IsFavorite = u.FavoriteFriends1.Any(x => x.uid == uid),
                            FavoriteFriendCount = u.FavoriteFriends.Count,
                            LastWishlistUpdate = u.WishListItems.OrderByDescending(x => x.added).FirstOrDefault().added,
                            Sex = (UserSex)u.sex
                        };

                var user = q.FirstOrDefault();
                user.DaysUntilBirthday = user.Birthday.DaysUntilBirthday();
                return user;
            }
        }
3个回答

9

这个错误提示很准确,你无法将其翻译成T-SQL(或P-SQL)查询。

在尝试将查询转换为其他类型之前,您需要确保已执行该查询。

保持简单,使用扩展方法。那就是它们存在的原因。

public static User ToUserEntity(this DbUser user)
{
    return new User
    {
        Uid = user.uid,
        FirstName = user.first_name,
        LastName = user.last_name
    };
}

然后在你的数据访问层(DAL)中:
public User GetUser(long uid)
{
    User dbUser;

    using (var entities = new myEntities())
    {
        dbUser = entities.DbUsers
                  .Where( x => x.uid == uid && x.account_status == (short)AccountStatus.Active )
                 .FirstOrDefault(); // query executed against DB
    }

    return dbUser.ToUserEntity();
}

在上下文被处理后,看看我如何将POCO转换为对象?这样,您可以确保EF在尝试将其转换为自定义对象之前已经完成了表达式工作。
另外,我不知道为什么您要将uid传递给该方法,因为它甚至没有被使用。
进一步说,您不应该需要做这种事情(将EF POCO投影到自己的对象中)。
如果您这样做,那么使用自定义POCO是一个很好的选择(直接将表映射到自定义POCO中,而不使用代码生成)。

好的,我在一个查询块中使用了uid(这里没有显示,但并不影响错误)。当我将此语句内联时,它可以正常工作。但是当我将代码移动到自己的方法中时,它就失败了。为什么编译器不能暗示这个表达式属于EF语句呢?我知道我之后可以填充这个对象,但我有几个需要在db上下文中操作的字段。它们只是一遍又一遍地内联重复出现(而且它们也能正常工作)。我真的不明白为什么你不能将这个功能提取到它自己的方法中。 - Rod Johnson
@wcpro - 你能展示一下代码在哪里内联工作吗?我非常想看看Entity Framework是如何“猜测”User(一个随机的CLR对象)和DbUser(一个EF对象)之间的关系,以创建查询表达式的。 - RPM1984
无论如何,你尝试过我的答案了吗?它应该可以正常工作(除非你像 Carlos 说的那样有一个枚举问题)。 - RPM1984
它没有给我想要的结果。我需要在select语句中添加更多的数据库逻辑。请查看更新后的问题,您可以看到它在内联中起作用。我正在考虑编写自定义Expression<>,我认为那将做我需要它做的事情。 - Rod Johnson

3

这个表达式可以得到所需的结果(有点),但我仍然没有弄清楚如何在选择语句中传递其他变量...

  ..... .Select(GetUser).FirstOrDefault()        

static readonly Expression<Func<DbUser, User>> GetUser = (g) => new User {
            Uid = g.uid,
            FirstName = g.first_name,
            LastName = g.last_name,
            BigPicUrl = g.pic_big,
            Birthday = g.birthday,
            SmallPicUrl = g.pic_small,
            SquarePicUrl = g.pic_square,
            Locale = g.locale.Trim(),
            //IsFavorite = g.FavoriteFriends1.Any(x=>x.uid==uid),
            FavoriteFriendCount = g.FavoriteFriends.Count,
            LastWishlistUpdate = g.WishListItems.OrderByDescending( x=>x.added ).FirstOrDefault().added
        };

我真的很喜欢这个,因为你可以在枚举之前在linq查询中使用它,因此它适用于列表。 - tkerwood

0

你不能这样做,因为getUser方法无法转换为任何TSQL语句。 如果你先返回DBUser然后将其作为GetUser方法的第一个参数使用,则会强制执行它,一旦你有了DBUser,就可以将其传递给GetUser

也许你可以尝试这个:

public User GetUser(long uid)
{
    using (var entities = new myEntities())
    {
        return GetUser(
            entities.DbUsers
                .Where( x => x.uid == uid && x.account_status == (short)AccountStatus.Active )
                .FirstOrDefault(),
            uid);
    }
}

编辑

既然你说它仍然失败,那可能是因为枚举类型的原因吗?

public User GetUser(long uid)
{
    using (var entities = new myEntities())
    {
        short status = (short)AccountStatus.Active;
        return GetUser(
            entities.DbUsers
                .Where( x => x.uid == uid && x.account_status == status )
                .FirstOrDefault(),
            uid);
    }
}

为什么你会有DbUser和User?难道 EF 的实体不应该是你的领域实体吗?看起来 User 是 DbUser 简化版,为什么不直接操作 DBUsers 呢? - Carlos Muñoz
我猜DbUser是EF POCO,而User是他的自定义业务对象。不过,我更喜欢直接映射到我的自定义POCO。省略中间人。 - RPM1984
在你的更新之后,枚举可能是一个问题,但它不是主要问题。EF无法将User对象转换为针对DbUser对象的可查询表达式。 - RPM1984
当您尝试对EF类进行序列化时,它们会变得非常混乱。 - Rod Johnson
我可以在一行代码中完成这个语句,这就是为什么我感到困惑。 - Rod Johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接