LINQ - null exception on getting certain cell value - c#

I'm a greenhorn when it comes to LINQ. I've read some null exceptions and most of them pointed out
... in sth.AsEnumerable() ...
as root of the problem.
I've run into:
The function evaluation requires all threads to run.
I tried to retrieve
fieldname
from TrashPlaces (it has 2 fields only - id and name) by string Id I get from UserPlace record (UserPlace has 2 keys - 1 is userId that can be retrieved from AspNetUsers and the other is TrashPlace id). I've run into null problem on this LINQ code:
public ActionResult JsonResult()
{
var users = db.AspNetUsers;
//this was added, so I could root out mismatch in the queryId
var nameformyuser = User.Identity.Name;
//the null starts here and no-shock it goes further (meaning query is null and
//can't do nothing later)
var queryId = from user in users.AsEnumerable()
where user.Email == User.Identity.Name
select user.Id;
var placerecord = db.UserPlace;
var userplace = from uplace in placerecord.AsEnumerable()
where uplace.usersId == queryId.ToString()
select uplace.placesId;
var places = db.TrashPlaces;
var field = from blah in places.AsEnumerable()
where blah.Id == userplace.ToString()
select blah.nameOfThePlace;
TempData["username"] = User.Identity.Name;
TempData["fieldname"] = field.ToString();
TempData["datename"] = DateTime.Now;
List<TrashViewModel> json = (List<TrashViewModel>)TempData["jsonList"];
return View(json);
}
Would be grateful for help and or/advice what's the best approach towards cascading LINQ.
Thanks!

You could do all in one (don't enumerate to early by the way, this is not good for performance).
Not sure why you use ToString() you shouldn't have to if your ids are of the same type.
var query = from u in db.AspNetUsers
join up in db.db.UserPlace on u.Id equals up.usersId
join tp in db.TrashPlaces on up.placesId equals tp.Id
where u.Email == User.Identity.Name
select tp.nameOfThePlace;//or more data if you need to.
var result = query.FirstOrDefault(); //or query.AsEnumerable()
Then you can do a null check
if (result == null)
In your case, for TempData, you may just do
TempData["fieldname"] = result == null ? string.Empty : result;
or
TempData["fieldname"] = result;
But then you'll have to check if TempData["fieldname"] is not null before using it.
...

Related

LINQ query for an in-memory List takes seconds on ToList() call

The following LINQ query is slow even for an in-memory List. It takes seconds to execute.
I tried both List and IQueryable and it is slow for both.
Can you provide any advice why it is slow?
edit:
The list has 350,000 rows. The logic is converted from a stored procedure to cache table in a list and retrieve data from memory instead of hitting database multiple times.
var list = GetData().AsQueryable(); //GetData returns List<MDEntity>
var query = (from g in list join ux in list on new {
Page = sPage, g.Property, Product = sProdAll, Section = (Guid?) null
}
equals new {
ux.Page, ux.Property, ux.Product, Section = (Guid?) null
}
into ux_join from ux in ux_join.DefaultIfEmpty() join up in list on new {
Page = sPage,
g.Property,
Section = (Guid?) null
}
equals new {
up.Page,
up.Property,
Section = (Guid?) null
}
into up_join from up in up_join.DefaultIfEmpty() where(up.Product ?? string.Empty) == sProd || up.Product == sProdAlt where g.Section == (Guid?) null &&
g.Page == sPage &&
((g.Product ?? string.Empty) == sProd || g.Product == sProdAlt || g.Product == sProdAll) orderby g.Property,
g.Product,
g.Language select new MDEntity {
Property = g.Property,
Product = (up.Page ?? string.Empty) == string.Empty ? Coalesce(up.Product, ux.Product, null, null) : Coalesce(up.Product, "SA", null, null),
Language = Coalesce(up.Language, ux.Language, null, null),
Value = Coalesce(up.Value, ux.Value, null, null)
});
var result = query.ToList();
The ToList() call is the point at which the actual query executes. So that big block of code you have above, is being lazily executed at the moment that you call ToList().
Given that I see you're able to use AsQueryable, I'm almost certain this is ultimately a database query under the covers. Although, I could be wrong.
Prior to the ToList() call, C# does NO SQL calls. So actually, it is NOT an in-memory operation.
That is a fairly sophisticated query, so I'm not surprised that it could take a few seconds for the database to complete the query and for C# to then return the resulting list.

Write attributes from list to var

I have the following piece of code:
var Attributes = db.Users.Where(u => u.UserId == PwRetreival.uId).Select(u => new { u.Name, u.UserId }).ToList();
user.Name = Attributes(SOMETHING?)
user.UserId = Attributes(SOMETHING?)
I have no idea how i would write the selected attributes to my model variables. I guess it doesn't know the type of the attribute when i write it like this?
This line returns a list of anonymous objects:
var Attributes = db.Users
.Where(u => u.UserId == PwRetreival.uId).Select(u => new { u.Name, u.UserId }).ToList();
Therefore, you can either iterate the list or index it:
user.Name = Atrributes[0].Name;
NOTE: Since you are getting the item by its Id, I would use Single or SingleOrDefault and not Where. See below for more.
Use Single
If you expect a single item to be returned, then do not use Where but use Single instead. Then you can do this:
user.Name = Attributes.Name;
Use SingleOrDefaut
If you expect a single item or no item, then use SingleOrDefault but check the value before using it like this:
if (Attributes != null)
{
user.Name = Attributes.Name;
}
There are also First, FirstOrDefault, Last and LastOrDefault.
As it is of type List, you need to use FirstOrDefault() to get the first record from the list (assuming that your Where clause have enough conditions to get the exact record you want).
var Attributes = db.Users.Where(u => u.UserId == PwRetreival.uId).Select(u => new { u.Name, u.UserId }).ToList().FirstOrDefault();
if (Attributes != null)
{
user.Name = Attributes.Name;
user.UserId = Attributes.UserId;
}
Attributes now is a list of an anonymous type containing Name and UserId.
user.Name = Attributes[0].Name
user.UserId = Attributes[0].UserId
... Would get the name and id of the first user, if the list contains at least 1 element.
You can also do:
foreach(var user in Attributes)
{
// var userName = user.Name;
// var userId = user.UserId;
}
... to iterate through all users. In this case, you don't even need the ToList() method in your query;
However, it seems like this query should return just one user. In this case, you can change your query to
var Attributes = db.Users.Where(u => u.UserId == PwRetreival.uId).Select(u => new { u.Name, u.UserId }).FirstOrDefault();
... and now Attributes has only 1 object with a Name and a UserId and you can access it like:
user.Name = Attributes.Name
user.UserId = Attributes.UserId
As pointed out by #Chris, if you can assume that your query is going to return 0 or 1 element, you should use SingleOrDefault(). If it should return just 1 element, you should use Single(). If the result contains more elements than it will throw an exception. And when you use XOrDefault you should always check for null afterwards.

Passing parameter to LINQ query

I have a method like below:
public void GetUserIdByCode(string userCode)
{
var query = from u in db.Users
where u.Code == userCode // userCode = "LRAZAK"
select u.Id;
var userId = query.FirstOrDefault(); // userId = 0 :(
}
When I ran the code, I got the default value of 0 assigned to userId meaning the Id was not found.
However, if I changed the userCode with a string like below, I will get the value I want.
public void GetUserIdByCode(string userCode)
{
var query = from u in db.Users
where u.Code == "LRAZAK" // Hard-coded string into the query
select u.Id;
var userId = query.FirstOrDefault(); // userId = 123 Happy days!!
}
My question is why passing the parameter into the LINQ query does not work?
When I stepped into the code, I got the SQL statement like so:
// Does not work...
{SELECT "Extent1"."LOGONNO" AS "LOGONNO"FROM "DEBTORSLIVE"."DEBTORS_LOGONS" "Extent1"WHERE ("Extent1"."LOGONCODE" = :p__linq__0)}
The hard-coded LINQ query (the working one) gives an SQL statement as below:
// Working just fine
{SELECT "Extent1"."LOGONNO" AS "LOGONNO"FROM "DEBTORSLIVE"."DEBTORS_LOGONS" "Extent1"WHERE ('LRAZAK' = "Extent1"."LOGONCODE")}
What would be the solution?
As a work-around, I use Dynamic Linq.
The code below is working for me.
public void GetUserIdByCode(string userCode)
{
string clause = String.Format("Code=\"{0}\"", userCode);
var userId = db.Users
.Where(clause)
.Select(u => u.Id)
.FirstOrDefault();
}
The database query returns an object of User with Code and Id as properties. This is defined in one of my classes.
Here is syntax that will work to pass an argument to a LINQ query.
Not sure how many people will be searching this topic so many years later, but here's a code example that gets the job done:
string cuties = "777";
// string collection
IList<string> stringList = new List<string>() {
"eg. 1",
"ie LAMBDA",
"777"
};
var result = from s in stringList
where (s == cuties)
select s;
foreach (var str in result)
{
Console.WriteLine(str); // Output: "777"
}

Proper use of StartWith and Contains in Entity Framework

I want to remove prefixes from StoreName field in Linq Linq to Entity query.
I have following query which gets list of all prefixes in table
Query1
var _prefix = context.Prefixes.Select(pre => pre.Prefix1);
I want to use this result in Query2
Query2
var objRetailer = from stores in context.RetailerStoredtls
join ret in context.RetailerContactdtls
on stores.RetailerID equals ret.RetailerID
join retreg in context.RetailerRegDates
on stores.RetailerID equals retreg.RetailerRegDateId
where (stores.IsDeleted == null || stores.IsDeleted == false)
&& (stores.CreatedDate.Value.Year == iYear || stores.ModifiedDate.Value.Year == iYear)
&& retreg.IsApproved== true
orderby stores.StoreName
select new
{
stores.StoreID,
Store = stores.StoreName,
Area = stores.StoreCity,
Zip = stores.StoreZip,
SellingCard = (storessellingcard.Contains(stores.RetailerID.Value) ? true : false)
StoreWithoutPrefix = stores.StoreName.StartsWith(<one of prefix retrieved from Query1> ? stores.StoreName : <stores.StoreName without prefix>
};
Unfortunately we have .StartWith() that take only string parameter not result of Query1, if I go with .Contains it will not check for whether it starts with or not, it just check whether string is present there. What should I do to accomplish this task?
Thanks.
You can use IndexOf, but probably you have problems with case sensitivity. So add a parameter CompareOptions.IgnoreCase

using linq to sql to specify where clause

I am using this method to get results to fill my grid but this method is also used to fill another grid which requires a where clause with two params and this one
only needs one. Even though i passed in null of the param that isn't used but still it is returning no results because of the where clause. Any advice of how i could
change this maybe use linq to sql where i call the method to specify the where clause instead of in the method getting data?
DocsForReview.DataSource = docLib.GetGrid(Guid.Empty, lib);
using (var dc = new DocMgmtDataContext())
{
var subs = (from doc in dc.Documents
join u in dc.Users on doc.OwnedByUserID equals u.ID
where doc.OwnedByUserID == usr && doc.LibraryID == lib
select new StudentDocuments
{
DocID = doc.ID,
Assignment = doc.Library.Name,
Submitted = doc.UploadDT,
Student = u.FullName
}).OrderByDescending(c => c.Submitted).AsEnumerable().ToList();
return subs;
}
For nullable types try this:
doc.LibraryID == (lib ?? doc.LibraryID)
In your case (a System.Guid) you can try this:
doc.LibraryID == (lib == Guid.Empty ? doc.LibraryID : lib)

Categories