Check property for null LINQ - c#

I have the following code that give me data from database:
var t = (from ula in proxy.eUserLoginAttempts
where ula.Date >= DateTime.Now && ula.Email.ToLower().Contains("")
&& ula.User != null
&& ula.User.Client != null
&& ula.User.Client.prStatus == 1
select ula).ToList();
In this case, I would get prStatus from Client entity and I check User and Client object if they are not null. Should I do it or ula.User.Client.prStatus will translate in Inner Join and this check is needless?

Answering your direct question: NO, you should test the nullable first...
About your code, I really do suggest a readable way:
var t = proxy.eUserLoginAttempts
.Where(ula => ula.Date >= DateTime.Now)
.Where(ula => !string.IsNullOrEmpty(ula.Email))
.Where(ula => ula.User != null)
.Where(ula => ula.User.Client != null)
.Where(ula => ula.User.Client.prStatus == 1)
.ToList();
Or even better with C# 6
var t = proxy.eUserLoginAttempts
.Where(ula => ula.Date >= DateTime.Now)
.Where(ula => !string.IsNullOrEmpty(ula.Email))
.Where(ula => ula.User?.Client?.prStatus == 1)
.ToList();

Related

Improve performance in linq query - trying to change the query from subquery to join

I am doing the below linq query which is costing me a lot and this query is in a loop which I can not avoid and I have to do it in C# which also I can not avoid. I have lot of logic above the linq query and after the query. I wanted to check if I can change anything on the query to improve the performance at least a little bit.
lstDataTable.Where(i => i.Field<int>("ALLL_Snapshot_ID") == 20 &&
i.Field<int>("ALLL_Analysis_Segment_Group_Column_ID") == 5 &&
i.Field<DateTime>("OriginationDate") > startingSnapshotDate &&
i.Field<DateTime>("OriginationDate") <= endingSnapshotDate &&
snapshotDataWithDate.Select(j => j.Field<string>
("MaturityDateBorrowerIdNoteNumberKey")).Contains(i.Field<string>
("MaturityDateBorrowerIdNoteNumberKey")) &&
snapshotDataWithDate.Select(j => j.Field<string>
("OriginationDateBorrowerIdNoteNumberKey")).Contains(i.Field<string>
("OriginationDateBorrowerIdNoteNumberKey")))
.Select(i => i.Field<Decimal>("BalanceOutstanding") + i.Field<Decimal>
("UndisbursedCommitmentAvailability")).Sum();
where lstDataTable and snapshotDataWithDate are IEnumerable of DataRow.
I tried above query using join but it is not joining properly. The difference between the two results is way high. Below is the query I tried using join
(from p in lstDataTable
join t in snapshotDataWithDate on p.Field<string>
("MaturityDateBorrowerIdNoteNumberKey") equals t.Field<string>
("MaturityDateBorrowerIdNoteNumberKey") &&
p.Field<string>("OriginationDateBorrowerIdNoteNumberKey") equals
t.Field<string>("OriginationDateBorrowerIdNoteNumberKey")
where p.Field<int>("ALLL_Analysis_Segment_Group_Column_ID") ==
SegmentGroupCECLSurvivalRateObj.ALLL_Segment_Group_Column_ID &&
p.Field<DateTime>("OriginationDate") > startingSnapshotDate &&
p.Field<DateTime>("OriginationDate") <= endingSnapshotDate
select p.Field<Decimal>("BalanceOutstanding") + p.Field<Decimal>
("UndisbursedCommitmentAvailability")).Sum();
Try this query, I have changed some expressions in where clause.
lstDataTable.Where(i => i.Field<int>("ALLL_Snapshot_ID") == 20 &&
i.Field<int>("ALLL_Analysis_Segment_Group_Column_ID") == 5 &&
i.Field<DateTime>("OriginationDate") > startingSnapshotDate &&
i.Field<DateTime>("OriginationDate") <= endingSnapshotDate &&
snapshotDataWithDate.Any(j => j.Field<string>
("MaturityDateBorrowerIdNoteNumberKey") == i.Field<string>
("MaturityDateBorrowerIdNoteNumberKey")) &&
snapshotDataWithDate.Any(j => j.Field<string>
("OriginationDateBorrowerIdNoteNumberKey") == i.Field<string>
("OriginationDateBorrowerIdNoteNumberKey")))
.Select(i => i.Field<Decimal>("BalanceOutstanding") + i.Field<Decimal>
("UndisbursedCommitmentAvailability")).Sum();
Perhaps pulling out the Field accesses will provide a small amount of optimization?
var snapshotDataConvertedMDB = snapshotDataWithDate.Select(r => r.Field<string>("MaturityDateBorrowerIdNoteNumberKey")).ToList();
var snapshotDataConvertedODB = snapshotDataWithDate.Select(r => r.Field<string>("OriginationDateBorrowerIdNoteNumberKey")).ToList();
var ans = lstDataTable
.Select(r => new {
ALLL_Snapshot_ID = r.Field<int>("ALLL_Snapshot_ID"),
ALLL_Analysis_Segment_Group_Column_ID = r.Field<int>("ALLL_Analysis_Segment_Group_Column_ID"),
OriginationDate = r.Field<DateTime>("OriginationDate"),
MaturityDateBorrowerIdNoteNumberKey = r.Field<string>("MaturityDateBorrowerIdNoteNumberKey"),
OriginationDateBorrowerIdNoteNumberKey = r.Field<string>("OriginationDateBorrowerIdNoteNumberKey"),
BalanceOutstanding = r.Field<Decimal>("BalanceOutstanding"),
UndisbursedCommitmentAvailability = r.Field<Decimal>("UndisbursedCommitmentAvailability")
})
.Where(i => i.ALLL_Snapshot_ID == 20 &&
i.ALLL_Analysis_Segment_Group_Column_ID == 5 &&
i.OriginationDate > startingSnapshotDate &&
i.OriginationDate <= endingSnapshotDate &&
snapshotDataConvertedMDB.Contains(i.MaturityDateBorrowerIdNoteNumberKey) &&
snapshotDataConvertedODB.Contains(i.OriginationDateBorrowerIdNoteNumberKey))
.Select(i => i.BalanceOutstanding + i.UndisbursedCommitmentAvailability)
.Sum();

Error translating Linq expression to URI: The method 'Contains' is not supported

I am trying to filter a data service using a List, however, I get a few error messages. Is there a way to filter by a List with a DataServiceQuery?
DataServiceQuery<Order> ordersQuery = (from x in entities.Orders
.Expand<DataServiceCollection<Shipment>>(a => a.Shipments)
.Expand<DataServiceCollection<OrderItem>>(a => a.OrderItems)
.Expand<OrderStatus>(a => a.OrderStatus)
.Expand<Customer>(a => a.Customer)
.Expand<Store>(a => a.Store)
.Expand<Marketplace>(a => a.Marketplace)
.Expand("Shipments/Carrier")
where x.OrderStatusID != 4
select x) as DataServiceQuery<Order>;
if (beginDate != null && endDate != null)
ordersQuery = ordersQuery.Where(x => x.CreateDate >= beginDate && x.CreateDate <= endDate) as DataServiceQuery<Order>;
if (statuses != null && statuses.Count() > 0)
ordersQuery = ordersQuery.Where(a => stores.Contains(a.OrderStatus.Name)) as DataServiceQuery<Order>;
if (stores != null && stores.Count() > 0)
ordersQuery = ordersQuery.Where(a => stores.Contains(a.Store.StoreName)) as DataServiceQuery<Order>;
if (!String.IsNullOrWhiteSpace(orderNumber))
ordersQuery = ordersQuery.Where(a => a.OrderNumber == orderNumber) as DataServiceQuery<Order>;
That code fails with
Error translating Linq expression to URI: The method 'Contains' is not supported.
This code also fails:
if (statuses != null && statuses.Count() > 0)
{
var statusFilterList = statuses.Select(title => String.Format("(Name eq {0})", title));
var statusFilter = String.Join(" or ", statusFilterList);
ordersQuery.AddQueryOption("$filter", statusFilter).Execute().ToList();
}
Can't add query option '$filter' because it would conflict with the query options from the translated Linq expression.

Using only Linq or Lambda, how do I combine both pieces of code to return List<Entity> e?

This code works but it's super slow. I'm using the Entity Framework 5.0. So, Using only Linq or Lambda, how do I combine both pieces of code to return List<Entity> e?
CMSEntities c = new CMSEntities();
var p = c.Entities.Where(
x => x.Address != null
&& x.Address.AddressType != null
&& x.Address.AddressType.AddressTypeID == 1
&& x.Tags.FirstOrDefault() != null)
.ToList();
List<Entity> e = new List<Entity>();
foreach (var a in p)
{
var d = a.Tags.Where(y => y.Feature.FeatureID == 39).FirstOrDefault();
if (d != null)
{
e.Add(a);
}
}
var e = c.Entities.Where(
x => x.Address != null
&& x.Address.AddressType != null
&& x.Address.AddressType.AddressTypeID == 1
&& x.Tags.FirstOrDefault(y => y.Feature.FeatureID == 39) != null
).ToList();
You can directly do it in one query. But I don't know if its going to make big speed difference.
As per Jon's suggestion in comments, this reads better by using Any instead of FirstOrDefault. Like:
var e = c.Entities.Where(
x => x.Address != null
&& x.Address.AddressType != null
&& x.Address.AddressType.AddressTypeID == 1
&& x.Tags.Any(y => y.Feature.FeatureID == 39)
).ToList();
Just get rid of the .ToList() call, and the performance should improve significantly.
I'm guessing that the "null" checks are unnecessary but without looking at your EDM/DataModel this is pure speculation.
var e = c.Entities.Where(
x => x.Address.AddressType.AddressTypeID == 1
&& x.Tags.Any(y => y.Feature.FeatureID == 39)
);
foreach (var a in p)
{
var d = a.Tags.Where(y => y.Feature.FeatureID == 39).FirstOrDefault();
if (d != null)
{
e.Add(a);
}
}

Datetime check in Linq where statement

Please see the code below:
var pcPageList = db.PcPages
.Where(m =>
m.Quarter == exactQuarter &&
m.Url == pageUrl &&
m.UpdatedOn.ToDateTime().Date.ToString("dd/MMM").ToLower() == "02/nov")
.OrderBy(m => m.UpdatedOn)
.FirstOrDefault();
When I run this above, the application throws error says: "ToDateTime" is not implemented yet. Anyone please can advice ?
How about:
var updateStart = DateTime.ParseExact("02/nov", "dd/MMM", CultureInfo.InvariantCulture);
var updateEnd = updateStart.AddDays(1.0);
var pcPageList = db.PcPages
.Where(m =>
m.Quarter == exactQuarter &&
m.Url == pageUrl &&
m.UpdatedOn >= updateStart &&
m.UpdatedOn < updateEnd)
.OrderBy(m => m.UpdatedOn)
.FirstOrDefault();
I think, you should be calling ToDateTime using Convert class as:
Convert.ToDateTime(m.UpdatedOn).Date...
And remove the Date in between as:
Convert.ToDateTime(m.UpdatedOn).ToString("dd/MMM").ToLower() == "02/nov"
Rather than doing a string comparison, it would be more efficient to compare the date components directly. I haven't tested this, but something like the following may work:
var pcPageList = db.PcPages
.Where(m => m.Quarter == exactQuarter && m.Url == pageUrl)
// You may need to materialize the results of the query at this point
// or use Convert.ToDateTime(...) instead of ToDateTime()
.Select(m => new { Row = m, UpdatedOn = m.UpdatedOn.ToDateTime() })
.Where(a => a.UpdatedOn.Month == 11 && a.UpdatedOn.Day == 2)
.Select(a => a.Row)
.OrderBy(m => m.UpdatedOn)
.FirstOrDefault();

Nullable Parameter used in Lambda Query is being ignored

I'm trying to pass a null value from a RenderAction to another view. But in between, at the controller, my linq lambda expression is not loading the right field, despite the null value going through correctly..
SprintManager.cshtml
<div id="Global_Backlog_Board" class="Board_Panel">
#{Html.RenderAction("ListOfSingleCards", new
{
State_ID = 1
});}
</div>
HomeController.cs
public PartialViewResult ListOfSingleCards( int? Sprint_ID,
int State_ID = 1)
{
var Cards = db.Cards.Where(x => x.State_ID == State_ID &&
x.Sprint_ID == Sprint_ID &&
x.Deleted != 1 &&
x.Archive != 1).ToList();
return PartialView(Cards);
}
So Sprint_ID is being passed over and loaded as null here, but I can't get the query to load the rows correctly.
In fact, the following works:
var Cards = db.Cards.Where(x => x.State_ID == State_ID &&
x.Sprint_ID == null &&
x.Deleted != 1 &&
x.Archive != 1).ToList();
So I suppose I could check if Sprint_ID is null and depending on the result run one of the two seperate queries, but I'd like to understand why my original attempt is not working.
Thank you!
I don't know the correct answer but based on your solution you should be able to tidy it up:
var cards = new List<Card>();
var query = db.Cards.Where(x => x.State_ID == State_ID &&
x.Deleted != 1 &&
x.Archive != 1);
if (Sprint_ID.HasValue)
query = query.Where(x => x.Sprint_ID == Sprint_ID);
else
query = query.Where(x => x.Sprint_ID == null);
cards = query.ToList();
A nullable int won't return "null" in the way that you're thinking. You have to check the HasValue property of it to determine if there is a value, and if so then use it otherwise use null:
public PartialViewResult ListOfSingleCards( int? Sprint_ID,
int State_ID = 1)
{
var Cards = db.Cards.Where(x => x.State_ID == State_ID &&
x.Sprint_ID == Sprint_ID.HasValue ? Sprint_ID.Value : null &&
x.Deleted != 1 &&
x.Archive != 1).ToList();
return PartialView(Cards);
}
Until something better comes a long, I'm using this:
var Cards = new List<Card>();
if (Sprint_ID == null)
{
Cards = db.Cards.Where(x => x.State_ID == State_ID &&
x.Sprint_ID == null &&
x.Deleted != 1 &&
x.Archive != 1).ToList();
}
else
{
Cards = db.Cards.Where(x => x.State_ID == State_ID &&
x.Sprint_ID == Sprint_ID &&
x.Deleted != 1 &&
x.Archive != 1).ToList();
}

Categories