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>();
}
}
}
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,
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
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).
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()
Related
Linq and Query's syntax is one of my weakest skills. I'm having issues with getting the desired result.
I have two tables/ collections. One is filled with DocumentTypes, the other with Notifications. These are the fields they hold that matter, I omitted the ones that don't.
DocumentTypes
ID
Name
SupplierID
Notifications
ID
DocumentTypeID
UserID
ProductID
Last_Sequence
I have three parameters; userID, supplierID and productID.
I need the supplierID to get a list of all the DocumentTypes tied to that supplier. Then I need the userID and ProductID to get a list of notifications tied to those.
Then I need to join those two lists, every notification will have a documentTypeID to which it is linked. When there is a notification for a certain document type, it needs to include the fields Last_Sequence and make a new bool field that is set to true.
When there is no notification, the Last_sequence can be left empty and the bool is created and set to false.
So the result would be a list with objects that have these types.
DocumentTypeID
DocumentTypeName
BoolField (true of there is a notification tied to this one)
NotificationID (only if there is one)
Last_sequence (only if there is one)
What I have so far.
The previous version only needed the bool field added, along with the documentType information. For that I had this statement, but I can't seem to add to that what I need:
List<DocumentTypeNotification> docTypes = repository.Get<Domain.DocumentType>().Where(d => d.SuppID == SuppId).Select(d => new DocumentTypeNotification
{
DocTypeID = d.Id,
DocTypeName = d.Name,
Subscribed = notifications.Any(n => n == d.Id)
}).ToList();
What I have tried for the new one is this, but it only gives data back, when there is a notification tied. When there isn't one, it doesn't give back that documentType data.
var temptest = from notif in repository.Get<Domain.Notification>()
join doctype in repository.Get<Domain.DocumentType>() on notif.DocTypeId equals doctype.Id
select new DocumentTypeNotification { DocTypeID = doctype.Id, DocTypeName = doctype.Name, Subscribed = true, NotifID = notif.Id, last_sequence = notif.Last_Sequence};
EDIT: here is an example of something I tried, but does not work. The issue here is that n does not exist when I try to do n.last_sequence.
List<DocumentTypeNotification> docTypes = repository.Get<Domain.DocumentType>().Where(d => d.SuppID == SuppId).Select(d => new DocumentTypeNotification
{
DocTypeID = d.Id,
DocTypeName = d.Name,
Subscribed = notifications.Any(n => n == d.Id),
last_sequence = test.Where(n => n.DocTypeId == d.Id).Select(n.Last_Sequence).FirstOrDefault()
//from n in test
//where n.DocTypeId == d.Id
//select n.Last_Sequence
}).ToList();
I was wondering how I should solve this. Do I need to make a collection of all the right DocumentTypes first, and then join that with the new collection that I made, or is there a better way to solve this?
What about left join
from d in repository.Get<Domain.DocumentType>()
join n in repository.Get<Domain.Notification>()
on d.Id equals n.DocTypeId
into temp
from notific in temp.DefaultIfEmpty()
where d.SuppID == SuppId
select new
{
d.Name,
last_sequence = notific != null ? notific.Last_Sequence : null,
Subscribed = notific != null
}
There is no concrete code samples, so at first I will define some variables.
Let's suppose that we have list of document types with name documentTypes and list of notifications with name notifications. If I am understanding your problem right, than the following linq query will do what you want
documentTypes.Where(d => d.SupplierID == SuppId)
.GroupJoin(notifications,
d => d.ID,
n => n.DocumentTypeID,
(document, notification) => new {document, notification}
)
.Select(join =>
{
var anyNotification = join.notification.FirstOrDefault();
return new
{
DocTypeID = join.document.ID,
DocTypeName = join.document.Name,
Subscribed = join.notification.Any(),
NotificationID = anyNotification != null ? anyNotification.ID : null,
Last_sequence = anyNotification != null ? anyNotification.Last_Sequence: null,
};
}).ToList();
I’m new to LINQ and need some help with a query.
I need all of the Resources from (tblResources) that belong to the Anonymous, and Public ResourceGroups (tblResourceGroups). In addition, I also need all of the Resources that belong to any of the ResourceGroups that the currentUser belongs to.
If the currentUser isn’t logged in (currentUser == null) then only Resources belonging to Anonymous, and Public ResourceGroups should be returned.
NOTE: My data model does not contain an entity for tblResourceAccess. I'm not sure why this entity wasn't added when the model was created.
string currentUser = IdentityHelper.GetUserIdFromRequest(Context.Request);
var result = from r in DbContext.Resources
where r.Active == true // && r.ResourceGroups?????
select new
{
ResourceTypeName = r.ResourceType.Name,
Name = r.Name,
Version = r.Version,
Description = r.Description,
Path = r.Path,
Active = r.Active
};
The tblResourceAccess was abstracted away by EF and the ResourceGroups property added to the Resource table to provide the functionality. Using this relationship we can piece together the following query:
from r in DBContext.Resources.ToList()
where (currentUser == null
&& ("anonymous,public").Contains(
r.ResourceGroups.Name.ToLower()))
|| (currentUser != null)
select new
{
ResourceTypeName = r.ResourceType.Name,
Name = r.Name,
Version = r.Version,
Description = r.Description,
Path = r.Path,
Active = r.Active
};
Finally, after a lot of trial and error! I'm not sure if this is best way performance wise to implement this query, but it works.
Thanks for your help #The Sharp Ninja!
string currentUser = IdentityExtensions.GetUserId(HttpContext.Current.User.Identity);
var resources = from r in DbContext.Resources
where r.ResourceGroups.Any(
rg => rg.Name == "Anonymous" ||
rg.Name == "Public" ||
rg.ResourceUserGroups.Any(ug => ug.UserID == currentUser))
select r;
I want to get the records with "Restricted" at top.
here Is my query:
var records = (from entry in db.Table1
orderby entry.Always_Prohibited
select new
{
RowID = entry.RowID,
VehicleMake = entry.Make,
VehicleModel = entry.Model,
Restricted = (entry.Always_Prohibited == null || entry.Always_Prohibited == "False") ? "Restricted" : "Not Restricted"
}).ToList();
I tried by Orderby but it is not working because entry.Always_Probibited is a string field.
Please suggest me.
If you have only two values, simply order descending:
from entry in db.Table1
orderby entry.Always_Prohibited descending
If you have more, assign integer values to your strings:
from entry in db.Table1
orderby entry.Always_Prohibited=="A" ? 0 : entry.Always_Prohibited=="B" ? 1 : 2 // and so on
As a side note, strings are a pretty terrible way of storing state in databases. You should redesign it to store it as well defined integers (preferably as foreign keys in master lookup tables, ie strongly typed enums).
User then below give code. It will help to you.
var records = (from entry in db.Table1.AsQueryable();
orderby entry.Always_Prohibited
select new
{
RowID = entry.RowID,
VehicleMake = entry.Make,
VehicleModel = entry.Model,
Restricted = (entry.Always_Prohibited == null || entry.Always_Prohibited == "False") ? "Restricted" : "Not Restricted"
}).ToList();
i have the following code:
int selectedcourseId = Convert.ToInt32(c1.Text);
var cid = (from g in re.Sections where g.CourseID == selectedcourseId select g.CourseID);
int selectedinstructorid = Convert.ToInt32(c2.Text);
var iid = (from u in re.Sections where u.InstructorID == selectedinstructorid select u.InstructorID);
i want to compare the two (selectedcourseId) with (cid) and (selectedinstructorid) with (iid) in if-statement such as:
if (selectedcourseId = cid && selectedinstructorid = iid)
{
MessageBox.Show("it already exists");
}
i have tried many things that didnt work our because i have limited knowledge.
thank you very much in advance for any comment or answer
You can change your code as: (but it is meaningless for your situation to check this)
if (selectedcourseId == cid.First() && selectedinstructorid == iid.First())
First of all for checking equality in if statement you must use ==, not =. And the second is the IQueryable<T> allows you to execute a query against a specific data source, but it uses deferred execution. For executing it in your case, you can use First().
But, I suggest that you are just learning how to use LINQ and therefore you have written this code.
I don't know what you are trying to achive. But, if you want to search if there is any result with that ID's, the you must use Any():
var result1 = from g in re.Sections where g.CourseID == selectedcourseId select g.CourseID;
var result2 = from u in re.Sections where u.InstructorID == selectedinstructorid select u.InstructorID;
if(result1.Any() && result2.Any()) { ... }
Or, if you want to find if there is any row which has specified CourseID and InstructorID, then you can call one Any():
if(re.Sections.Any(x => x.CourseID == selectedcourseId && x.InstructorID == selectedinstructorid))
{ ... }
Let me try to find the X from this XY-problem. I guess you want to check if there is already a combination of courseid + instructorid. Then use a single query:
var data = from section in re.Sections
where section.InstructorID == selectedinstructorid
&& section.CourseID == selectedcourseId
select section;
if(data.Any())
{
MessageBox.Show("it already exists");
}
You should not do it in two queries, because the two results might be related to two different rows. This would lead to "false positives" when an instructor handles some section, and a course has some instructors, but the two matches do not belong to the same row:
course instructor
------ ----------
100 10
101 15
102 20
If you are looking for a combination (101, 10) it is not enough to see that 100 is present and 10 is present; you need to check that the two belong to the same row in order to consider it a duplicate.
You can fix this by making a "check presence" query, like this:
var existing = re.Sections
.Any(s => s.InstructorID == selectedinstructorid && s.CourseID == selectedcourseId);
if (existing) {
MessageBox.Show("it already exists");
}
if (selectedcourseId = cid && selectedinstructorid = iid)
this will not work, since single '=' is an assignment, not a comparation (which is '==')
also, you can try to do something like this
var cid = (int)((from g in re.Sections where g.CourseID == selectedcourseId select g.CourseID).FirstOrDefault());
so you select the first or default record from your list and cast it to int
I have a fairly complex Linq query:
var q = from eiods in LinqUtils.GetTable<EIOfficialDesignee>()
let eiodent = eiods.Entity
join tel in LinqUtils.GetTable<EntityTelephone>()
on new { EntityID = eiodent.ID, TelephoneType = EntityTelephone.TTelephone.Office } equals new { tel.EntityID, tel.TelephoneType }
into Tel
let Tel1 = Tel.FirstOrDefault()
join fax in LinqUtils.GetTable<EntityTelephone>()
on new { EntityID = eiodent.ID, TelephoneType = EntityTelephone.TTelephone.Fax } equals new { fax.EntityID, fax.TelephoneType }
into Fax
let Fax1 = Fax.FirstOrDefault()
join cell in LinqUtils.GetTable<EntityTelephone>().DefaultIfEmpty()
on new { EntityID = eiodent.ID, TelephoneType = EntityTelephone.TTelephone.Mobile } equals new { cell.EntityID, cell.TelephoneType }
into Mobile
let Mobile1 = Mobile.FirstOrDefault()
where eiods.ID == CurrentEIPatient.EIOfficialDesigneeID
select new {
ID = eiods.ID,
EIODName = eiodent.FormattedName,
Phone = Tel1 != null ? Tel1.FormattedNumber : "",
Fax = Fax1 != null ? Fax1.FormattedNumber : "",
Cellphone = Mobile1 != null ? Mobile1.FormattedNumber : "",
};
This query is returning me an SQL error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Yes, in triplicate. That's an obvious indicator that the problem with the query is being repeated 3 times i.e. in the 3 different types of phone number.
According to the SQL server documentation, this comes from a malformed query. But this is Linq, for heaven's sake! How can it be malforming the query?
Aside from the main answer, I'd also appreciate any comments you may have about optimizing my query...
Thanks!
Solved it myself, and here it is for anyone else's benefit.
The devil is in the select clause at the end, specifically:
Phone = Tel1 != null ? Tel1.FormattedNumber : "",
Fax = Fax1 != null ? Fax1.FormattedNumber : "",
Cellphone = Mobile1 != null ? Mobile1.FormattedNumber : "",
That FormattedNumber property is a calculated field based on the Number and Extension properties of the EntityTelephone object. When I replace FormattedNumber with Number, everything works fine.
The best solution to this problem is found here.