2

I writing a email system where we have a table of users "tblUsers" and a table of messages. A user can have many messages (from other users in tblusers) in his or her inbox (one:many).

In tblUsers table, I have a column called ImageURL (string) that contains the URL to the user's avatar. In this case, I'm looping through the messages in an inbox belonging to a user and what I'm trying to do is, once I get the message, walk up the tree to the tblUser and get the value in the ImageURL column for the owner of that message as marked "SenderAvatar" below.

Here's what I tried. The problem is that the sub linq for SenderAvatar below is throwing a nullpointer exception even though I have confirmed that there is a value for ImageURL (this is dev so there's only three users). Somehow my logic and linq's logic is at odds here. Can someone please help? Thanks!

Edit I found two bugs. The first bug is Dzienny pointed me to the right direction where I was comparing apples and oranges. The second bug is FromUserId = ux.tblUserId, where I'm setting the current user id to FromUserId Guys, thank you for all your help on this.

 public List<UserInboxMsg> GetUserInboxMsg(IKASLWSEntities conx, int userid)
    {
        var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();
        if (u != null)
        {

            return (from ux in u.tblInboxes
                    orderby ux.CreationTS descending
                    select new UserInboxMsg
                    {
                        CreationTS = ux.CreationTS,
                        ExpirationDate = ux.ExpirationDate,
                        FromUserId = ux.tblUserId,
                        HasImage = ux.HasImage,
                        ImageId = ux.ImageId ?? 0,
                        IsDeleted = ux.IsDeleted,
                        IsRead = ux.IsRead,
                        MsgId = ux.Id,
                        MsgSize = ux.MessageSize,
                        ParentId = ux.ParentId,
                        Title = ux.Title,
                        ToUserId = userid,
                        FromUserName = ux.Title,
                        SenderAvatar = conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault().tblUser.ImageURL,
                        Message = ux.Message
                    }).ToList<UserInboxMsg>();
        }
        else
        {
            return new List<UserInboxMsg>();
        }
    }

}
Zuzlx
  • 1,246
  • 14
  • 33

4 Answers4

1

Try this.

   public List<UserInboxMsg> GetUserInboxMsg(IKASLWSEntities conx, int userid)
        {
            var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();
            if (u != null && conx != null)

        {

            return (from ux in u.tblInboxes
                    orderby ux.CreationTS descending
                    select new UserInboxMsg
                    {
                        ...
                        ...
                        SenderAvatar = conx.tblMessages.Any(mu=>mu.Id == ux.Id) ? (conx.tblMessages.First(mu=>mu.Id == ux.Id).tblUser != null? conx.tblMessages.First(mu=>mu.Id == ux.Id).tblUser.ImageURL : null) : null,
                        Message = ux.Message
                    }).ToList<UserInboxMsg>();
        }
        else
        {
            return new List<UserInboxMsg>();
        }
    }

}

if you are getting null for the Avatar, it is either because there are no entries in tblMessages where mu.Id equals ux.Id or the tblMessage entry is there but the tblUser property is null

yohannist
  • 4,166
  • 3
  • 35
  • 58
1

If in the entity-framework, there is a foreign key reference between the two tables you could probably do this:

SenderAvatar = conx.tblMessages.FirstOrDefault( mu=>mu.Id == ux.Id).ImageURL,
Sajal
  • 4,359
  • 1
  • 19
  • 39
  • Thank you. I copied and pasted your solution and VS is saying that ImageURL does not exists which makes sense since in this case we are grabbing tblMessage object which doesn't have a property named ImageURL. The ImageURL is one level up in tblUser. – Zuzlx Jul 04 '15 at 18:23
1

There are several problems here.

The first is that the second statement is executed in memory, while it's possible to make the whole query run as SQL:

from u in conx.tblUsers where m.Id == userid
from ux in u.tblInboxes
orderby ux.CreationTS descending
select new UserInboxMsg
{
    CreationTS = ux.CreationTS,
    ExpirationDate = ux.ExpirationDate,
    FromUserId = ux.tblUserId,
    HasImage = ux.HasImage,
    ImageId = ux.ImageId ?? 0,
    IsDeleted = ux.IsDeleted,
    IsRead = ux.IsRead,
    MsgId = ux.Id,
    MsgSize = ux.MessageSize,
    ParentId = ux.ParentId,
    Title = ux.Title,
    ToUserId = userid,
    FromUserName = ux.Title,
    SenderAvatar = conx.tblMessages.Where(mu => mu.Id == ux.Id)
                       .FirstOrDefault().tblUser.ImageURL,
    Message = ux.Message
}

This has three benefits:

  • you fetch less data from the database
  • you get rid of the null reference exception, because SQL doesn't have null references. It just returns null if a record isn't found.
  • you can return the result of this statement without the if-else.

Second, less important, is that you should use a navigation property like Inbox.Messages in stead of joining (sort of) the inbox and its messages. This makes it less likely that you use the wrong join variables and it condenses your code:

SenderAvatar = ux.Messages.
                 .FirstOrDefault().User.ImageURL,

Now if there is no avatar, there is no avatar. And there's no null reference exception.

(By the way, you can see that I hate these prefixes in class and property names).

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
1

I can only guess this part of your code is wrong : SenderAvatar = conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault().tblUser.ImageURL I think for example you should use (mu=>mu.UserId == ux.Id) instead of (mu=>mu.Id == ux.Id). In your code, you are comparing "Id" of a table to "Id" of another table which normally in one to many relations is wrong. (only works in one to one relations)

I said I can guess because you didn't mention any information about tblInboxes and tblMessages fields. If you could provide me more information about their structure, I could answer in more detail.

By the way to make your code more clear you can use:

var u = conx.tblUsers.FirstOrDefault(m=>m.Id == userid);

instead of

var u = (from m in conx.tblUsers where m.Id == userid select m).FirstOrDefault();

OR

conx.tblMessages.FirstOrDefault(mu=>mu.Id == ux.Id)

instead of

conx.tblMessages.Where(mu=>mu.Id == ux.Id).FirstOrDefault()
Afshin Aghazadeh
  • 547
  • 4
  • 17