views:

77

answers:

3

I have a bunch of Linq to Entity methods that had the same select statement, so I thought I would be clever and separate that out into it's own method to reduce redundancy... but when i attempted to run the code, i got the following error...

this method cannot be translated into a store expression

Here is the method i created...

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

And am calling in a method like this...

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

UPDATE: here is the code that works inline

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

You can't do this because the getUser method cannot be converted to any TSQL statement. if you return your DBUser first and then use it as the first parameter of the GetUser method then you are forcing it to execute and once you have you DBUser you can pass it to GetUser

Maybe you can try this:

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

EDIT

Since you are saying it still fails could it be beacuse of the enum??

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);
    }
}
Carlos Muñoz
I tried this technique and it still fails
wcpro
Why do you have DbUser and User anyway? Aren't EF's Entities supposed to be your domain entities??? It looks like User is a dumbed down version of DbUser why not just operate on DBUsers ?
Carlos Muñoz
Im guessing DbUser is the EF POCO, and User is his custom Business object. Still, i prefer mapping straight to my custom POCO. Leave out the middle man.
RPM1984
After your update - the enum could be A problem, but it's not THE problem. EF has no way of translating a User object into a queryable expression against DbUser object.
RPM1984
EF classes are really messy when you try to serialize them.
wcpro
i could do this statement inline, which is why im confused.
wcpro
A: 

The error is spot on, you can't translate that into a T-SQL (or P-SQL) query.

You need to make sure you've executed the query before you attempt to hydrate it into some other type.

Keep it simple, use an extension method. That's what they are there for.

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

Then in your 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();
}

See how i hydrate the POCO into an object after the context has been disposed? This way, you ensure EF has finished it's expression work before you attempt to hydrate into a custom object.

Also i dont know why you're passing uid to that method, it's not even being used.

On a further note, you shouldn't need to do this kind of thing (project EF POCO's into your own objects).

If you do, it's a good case for custom POCO's (map the tables straight into your custom POCO's, don't use the Code Generation).

RPM1984
well, i use the uid in a chunk of the query that im not showing here (that did not impact the error). When i had this statment inline, it worked just fine. But when i move the code into its own method if fails. Why cant the compiler just imply that this expression belongs in the EF statement. I know i could hydrate this object afterwards, but i have several fields that need to operate on the db context. They are just repeated over and over inline (and they worked fine). I jsut really don't understand why you cant extract this functionality into its OWN method.
wcpro
@wcpro - can you show the code where it works inline? I would be very interested to see how Entity Framework managed to "guess" the relationships between User (a random CLR object) and DbUser (an EF object) in order to create the query expression.
RPM1984
Anyway, did you try my answer - it should work (unless you've got an enum problem, as Carlos says below)
RPM1984
it does not give me the desired result. I need to add much more db logic in the select statement. please see the updated question, you can see where it works inline. I'm looking at writing a custom Expression<> and i think that will do what i need it to do
wcpro
A: 

This expression will work to give the desired result (somewhat) I still havent figured out how to pass in additional variables in teh select statements...

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