Linq Query Fails With Null Value - linq

I am using JQuery Datatables with my MVC 5 application. I am implementing sorting on one of my datatables within my MVC Controller, however, I am having some difficulties because it appears some of the columns which a user can choose to sort by, contains null values.
//Get column index to sort
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
if (sortColumnIndex == 0 || sortColumnIndex == 1)
{
if(sortColumnIndex == 0)
{
Func<survey_status, string> orderingFunction = (c => c.doctor.Doctor_FName);
}
else if(sortColumnIndex == 1)
{
Func<survey_status, string> orderingFunction = (c => c.Facilitator.Doctor_FName);
}
}
If the sortColumnIndex is 0, then there is never a problem because c.doctor is never Null. However, when the sortColumnIndex is 1, then sometimes c.Facilitator is Null, which makes the code fail.
Is there anything I can do check if c.Facilitator is Null, if so, return an empty string like "" or "No data".
Any help would be greatly appreciated.
Thanks.

(c => c.Facilitator == null ? "No Data" : c.Facilitator.Doctor_FName)

Related

automatically expand the result of an odata function

I defined an odata function to mimic $search which is not supported yet in the recent core release. I want to return the core entity plus an expanded entity which would translate into a js object on each Person in the returned json values array.
I tried odata/People/MyNS.Find(text='john', orderby='CreatedOn')?$expand=CurrentWork where CurrentWork is on People, but that did not work.
Thoughts on how to do this?
// my controller code for the function
[HttpGet]
public ActionResult<ICollection<People>> Find([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (text == null || text.Length == 0)
return Get().ToList();
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn";
return _db.People
.Where(p => p.FirstName.Contains(text)
|| p.LastName.Contains(text)
|| p.Nickname.Contains(text))
.OrderBy(orderBy)
.Take(5000)
.ToList();
}
Regular expansion of CurrentWork in a non-function works fine e.g. odata/People?$expand=CurrentWork.
By looking at the Linq query, it's fetching only People data and not any of it's child collections. You should use Include to fetch data for child collections along with parent entity like below. Read more on loading related entities here.
// my controller code for the function
[HttpGet]
public ActionResult<ICollection<People>> Find([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (text == null || text.Length == 0)
return Get().ToList();
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn";
return _db.People
.Where(p => p.FirstName.Contains(text)
|| p.LastName.Contains(text)
|| p.Nickname.Contains(text))
.Include(p => p.CurrentWork) // I have added this line
.OrderBy(orderBy)
.Take(5000)
.ToList();
}
Note: You still need to use $expand=CurrentWork as query string. Without this query string, server will remove child collections before sending response to client.
Here's what I came up with in the end. I noticed that the included entities were pulling in alot of data from the database so I reduced down the pull quite a bit by being specific. Include just pulled everything and I could not reduce the Include down directly so I had to use a Select.
[HttpGet]
public IOrderedQueryable Find2([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn DESC";
if (text == null || text.Length == 0)
return Get().OrderBy(orderBy);
var r = LikeToRegular(text);
return _db.People
.AsNoTracking() // can't use if using lazy loading
.Select(p => new
{
p.FirstName,
p.LastName,
p.Nickname,
p.CreatedOn,
p.CurrentWork.Title,
p.CurrentWork.Company.CompanyName
})
// Forces local computation, so pulls entire people dataset :-(
.Where(x => Regex.IsMatch(x.LastName ?? "", r)
|| Regex.IsMatch(x.FirstName ?? "", r, RegexOptions.IgnoreCase)
|| Regex.IsMatch(x.Nickname ?? "", r, RegexOptions.IgnoreCase)
|| Regex.IsMatch($"{x.FirstName} {x.LastName}", r,
RegexOptions.IgnoreCase))
.OrderBy(orderBy);
}
// Allow some wildcards in the search...
public static String LikeToRegular(String value)
{
return "^" + Regex.Escape(value)
.Replace("_", ".")
.Replace("%", ".*") + "$";
}

Count nulls in properties with one LINQ query

Paste into LINQPad:
void Main()
{
List<Data> list = new List<Data>();
list.Add(new Data());
list.Add(new Data{a="a", b="b"});
list.Add(new Data{a=null, b="b"});
var queryA = from data in list where data.a == null select data;
var queryB = from data in list where data.b == null select data;
var countNulls = new {a = queryA.Count(),b = queryB.Count()};
countNulls.Dump();
}
class Data
{
public string a {get;set;}
public string b {get;set;}
}
Instead of using queryA and queryB is it possible to do this in one query?
Answer:
All queries below generate exactly same SQL, so it's just a preference of coder what to choose.
var countNulls = new
{
a = queryA.Count(),
b = queryB.Count()
};
var countNulls2 = new
{
a = list.Count(d => d.a == null),
b = list.Count(d => d.b == null)
};
var countNulls3 = list.Aggregate(
new { a = 0, b = 0 },
(acc, data) => new
{
a = acc.a + (data.a == null ? 1 : 0),
b = acc.b + (data.b == null ? 1 : 0),
});
Update: apparently (thanks to Evan Stoev) this task can be done 20x faster on EF's DbSet and it creates one SQL query.
var countNulls4 =
(from data in db.Data
group data by 1 into g
select new
{
a = g.Sum(data => data.a == null ? 1 : 0),
b = g.Sum(data => data.b == null ? 1 : 0)
}).First();
For completeness, you can use Aggregate to get the result in a single pass over the input sequence:
var countNulls = list.Aggregate(
new { a = 0, b = 0 },
(acc, data) => new
{
a = acc.a + (data.a == null ? 1 : 0),
b = acc.b + (data.b == null ? 1 : 0),
});
But I'm not sure it would be more efficient compared to 2 separate Count calls due to the need of anonymous object allocation on each step.
UPDATE: It turns out that you are asking for a single SQL query (so list is not actually a List<Data> but DbSet<Data> I guess). In LINQ to Entities, you can use group by constant technique, which combined with replacing Count(condition) with Sum(condition ? 1 : 0) will produce a nice single SQL query pretty similar to what you would write manually:
var countNulls =
(from data in db.Data
group data by 1 into g
select new
{
a = g.Sum(data => data.a == null ? 1 : 0),
b = g.Sum(data => data.b == null ? 1 : 0)
}).First();
You can simply use the Count() overload that takes a predicate:
var countNulls = new
{
a = list.Count(d => d.a == null),
b = list.Count(d => d.b == null)
};
Output is:
As far as I understand, you want to count nulls for two different object properties regardless if the other one is null. In other words:
a=null, b="b"
a="a", b=null
a=null, b=null
would return count for 2 and 2.
In that case you can do it this way in one query:
var queryAB = from data in list where data.a == null || data.b == null select data;

LINQ select null values from List

I have a List<string> and it may contain null values in random indexes. I want to check which elements null and select that elements for throwing message.
What i am using;
List<string> getNames = EbaUsers.GetNamesFromIds(activeDirectoryInfo[7],
activeDirectoryInfo[8], activeDirectoryInfo[9], activeDirectoryInfo[10]);
if(getNames[7].Equals(null))
{
MessageBox.Show("getNames[7] is null");
}
if(getNames [8].Equals(null))
{
MessageBox.Show("getNames[8] is null");
}
if(getNames[9].Equals(null))
{
MessageBox.Show("getNames[9] is null");
}
if(getNames[10].Equals(null))
{
MessageBox.Show("getNames[10] is null");
}
I know this is very easy to do with LINQ but i haven't found anywhere.
Thanks for your help.
Sounds like you want to first project the strings to index/value pairs, then pick the elements with null values, and project to just the indexes:
var nullIndexes = names.Select((value, index) => new { value, index })
.Where(pair => pair.value == null)
.Select(pair => pair.index)
.ToList();
You need to get the index by Enumerable.Select() with index and check if the info is null:
var nullInfoIndexes = activeDirectoryInfo.Select((info, index) => new {info, index})
.Where(x => x.info == null)
.Select(x => x.index);
foreach (var index in nullInfoIndexes)
MessageBox.Show("activeDirectoryInfo[" + index + "] is null");

Entity Framework Passing Table Value in Parameter Of a Function

I have a requirement, where I check a particular string value within a particular column of 2 and more than 2 table (the column name of different table is same)
and if the string is present in any one of table column it will return false.
public Boolean CheckAirCraftType(String AirCraftTypeCode, ref ExceptionEntity ExceptionEntityObject)
{
Boolean InUse = false;
try
{
var QueryTFLTORD = (from tflord in RoyalFleetEntities.TFLTORDHs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select tflord).ToList();
var QueryTAIRCRFT = (from taircraft in RoyalFleetEntities.TAIRCRFTs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select taircraft).ToList();
var queryTFLTORDH = (from tfltordh in RoyalFleetEntities.TFLTORDHs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select tfltordh).ToList();
if (QueryTFLTORD.Count > 0 || QueryTAIRCRFT.Count > 0 || queryTFLTORDH.Count > 0)
{
InUse = true;
}
}
catch (DbEntityValidationException ExceptionObject)
{
..
}
return InUse;
}
It is working well, but I want to customized my code so that it can be reusable.
So I want to pass table name [in my case "TFLTORDHs",TAIRCRFTs] in the parameter of a function, and it will check whether the string is present in that table or not?
How can I achieve it? Please assist me.
Well, first of all, this is not going to be the answer you wanted.
You may create an interface, and then ask for the implementations of it.
public interface IAirCraftUseCheck
{
bool IsAirCraftUsed(string airCraftCode);
}
public class RoyalFleetEntities : IAirCraftUseCheck
{
bool IsAirCraftUsed(string airCraftCode)
{
bool InUse = false;
try
{
var QueryTFLTORD = (from tflord in RoyalFleetEntities.TFLTORDHs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select tflord).ToList();
var QueryTAIRCRFT = (from taircraft in RoyalFleetEntities.TAIRCRFTs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select taircraft).ToList();
var queryTFLTORDH = (from tfltordh in RoyalFleetEntities.TFLTORDHs.Where(a => a.ACFT_TYPE_CODE == AirCraftTypeCode)
select tfltordh).ToList();
if (QueryTFLTORD.Count > 0 || QueryTAIRCRFT.Count > 0 || queryTFLTORDH.Count > 0)
{
InUse = true;
}
}
catch (DbEntityValidationException ExceptionObject)
{
..
}
return InUse;
}
}
You can use Reflection or some libraries like StructureMap to go through all the classes that implement that interface, and then invoke the method and check the result if it is in use.
Why go this way, because in the future some other module that you are unaware of may use the Aircraft and it can even use a completely different database but still depend on the same aircraft object that you are controlling now. And if they implement that interface then you still know that it is in use or not.

Unable to show View if Database table don't have records

This method throws exception when db.Profits doesn't have any records. How to prevent explode page
public double getProfitSum()
{
return db.Profits.Where(p => p.IdUser.UserId == WebSecurity.CurrentUserId).Sum(p => p.Value);
}
Error :
The cast to value type 'Double' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
try that:
var result = db.Profits.Where(p => p.IdUser.UserId == WebSecurity.CurrentUserId).Sum(p => p.Value);
if(result != null)
{
return result;
}
return 0;
The reason must be Sum() expects not nullable value. But your result might give null.
try
return db.Profits.Where(p => p.IdUser.UserId == WebSecurity.CurrentUserId
&& p.Value != null).Select(x=>(double?)x.Value).Sum() ?? 0.0M;
or even better
public double getProfitSum()
            {
var result = db.Profits.Where(p => p.IdUser.UserId == WebSecurity.CurrentUserId
&& p.Value != null).Sum(p => p.Value);
return result == null ? 0 : result;
}

Resources