Convert if else to linq - c#

I have following code in c# in which I am searching for lowest price flight .Now I want to convert it to Linq
for (; count < _flightSearchController.ListOfContracts.Count; count++)
{
contract = (DTContract)_flightSearchController.ListOfContracts[count];
if (contract.CurrentStatus == AvailabilityStatus.AVAILABLE)
{
if (CheckContractCitiesWithSearchCriteria(contract, originAirports, destinationAirports))
{
//if fare is lower than selected contract.
if (lowestPriceContract == null || lowestPriceContract.FareDetails.PriceForDefaultFlightSelection > contract.FareDetails.PriceForDefaultFlightSelection)
{
lowestPriceContract = contract;
}
else if (lowestPriceContract.FareDetails.PriceForDefaultFlightSelection == contract.FareDetails.PriceForDefaultFlightSelection)
{
if (lowestPriceContract.FareDetails.PriceAdult > 0 && (lowestPriceContract.FareDetails.PriceAdult + lowestPriceContract.FareDetails.FareTaxAdult) > (contract.FareDetails.PriceAdult + contract.FareDetails.FareTaxAdult))
{
lowestPriceContract = contract;
}
else if (lowestPriceContract.FareDetails.PriceSenior > 0 && (lowestPriceContract.FareDetails.PriceSenior + lowestPriceContract.FareDetails.FareTaxSenior) > (contract.FareDetails.PriceSenior + contract.FareDetails.FareTaxSenior))
{
lowestPriceContract = contract;
}
}
}
}
I tried it to convert but stuck in if else if section.
var q = _flightSearchController.ListOfContracts.ToList<DTContract>()
.Where(cont => cont.CurrentStatus == AvailabilityStatus.AVAILABLE);
if (lowestPriceContract == null || lowestPriceContract.FareDetails.PriceForDefaultFlightSelection > contract.FareDetails.PriceForDefaultFlightSelection)
{
}

Use the Min extension method:
var q = _flightSearchController.ListOfContracts
.Where(cont => cont.CurrentStatus == AvailabilityStatus.AVAILABLE
&& CheckContractCitiesWithSearchCriteria(cont, originAirports, destinationAirports))
.Min(cont=> cont.FareDetails.PriceForDefaultFlightSelection)
Edit I had glossed over the tie-breaker part, which makes it a bit more complicated. You can do it with sorting, but this will be slower when there are a lot of contracts:
var q = _flightSearchController.ListOfContracts
.Where(cont => cont.CurrentStatus == AvailabilityStatus.AVAILABLE)
&& CheckContractCitiesWithSearchCriteria(cont, originAirports, destinationAirports))
.OrderBy(cont => FareDetails.PriceForDefaultFlightSelection)
.ThenBy(cont => cont.FareDetails.PriceAdult + lowestPriceContract.FareDetails.FareTaxAdult)
.ThenBy(cont => cont.FareDetails.PriceSenior + lowestPriceContract.FareDetails.FareTaxSenior)
.First();
You could implement the IComparable interface for the FareDetails object to compare the prices, which would allow you to do this:
var q = _flightSearchController.ListOfContracts
.Where(cont => cont.CurrentStatus == AvailabilityStatus.AVAILABLE
&& CheckContractCitiesWithSearchCriteria(cont, originAirports, destinationAirports))
.Min(cont=> cont.FareDetails)

Related

To convert if condition to linq in cshtml

Code
if(Model.CurrentStatus == 1 || Model.CurrentStatus == 2)
{
//can display those records..
}
else if((Model.CurrentStatus == 3 || Model.CurrentStatus == 4) && Model.Date != null)
{
if(Model.Date <= 30 days)
{
//can display those records..
}
}
I have tried the following code and unable to complete it fully as expected
#Html.Partial("Filter", new IndexModel()
{
Id = Model.Id,
Collection = Model.Collection.Where((a => a.CurrentStatus == 1 || a.CurrentStatus == 2)
&& )
})
How to convert the above if condition to linq in cshtml. Thanks
the else-if relationship is an OR relationship. So simply combine the two lines. the inner nested if inside the else if is an AND relationship. This would go into the second set of parentheses
Collection = Model.Collection.Where
(
(a => a.CurrentStatus == 1 || a.CurrentStatus == 2) ||
((a.CurrentStatus == 3 || a.CurrentStatus == 4) && a.Date != null && a.Date <= 30)
)
EDIT:
Here is another suggestion: extract the readable code into an own method that evaluates the condition and returns the boolean result. This way you can make a predicate that can be accepted by the Where method:
private bool IsForDisplay( ModelDataType Model )
{
if(Model.CurrentStatus == 1 || Model.CurrentStatus == 2)
{
//can display those records..
return true;
}
else if((Model.CurrentStatus == 3 || Model.CurrentStatus == 4) && Model.Date != null)
{
if(Model.Date <= 30 days)
{
//can display those records..
return true;
}
}
return false;
}
now you can use it simply in the linq expression:
#Html.Partial("Filter", new IndexModel()
{
Id = Model.Id,
Collection = Model.Collection.Where(a => IsForDisplay(a))
});

Entity Framework: Count() very slow on large DbSet and complex WHERE clause

I need to perform a count operation on this Entity Framework (EF6) data set using a relatively complex expression as a WHERE clause and expecting it to return about 100k records.
The count operation is obviously where records are materialized and therefore the slowest of operations to take place. The count operation is taking about 10 seconds in our production environment which is unacceptable.
Note that the operation is performed on the DbSet directly (db being the Context class), so no lazy loading should be taking place.
How can I further optimize this query in order to speed up the process?
The main use case is displaying an index page with multiple filter criteria but the the function is also used for writing generic queries to the ParcelOrderstable as required for other operations in the service classes which might be a bad idea resulting in very complex queries resulting from laziness and might potentially be a future problem.
The count is later used for pagination, and a much smaller number of records (e.g. 500) is actually displayed. This is a database-first project using SQL Server.
ParcelOrderSearchModel is a C#-class that serves to encapsualte query parameters and is used exclusively by service classes in order to call the GetMatchingOrdersfunction.
Note that on the majority of calls, the majority of the parameters of ParcelOrderSearchModel will be null.
public List<ParcelOrderDto> GetMatchingOrders(ParcelOrderSearchModel searchModel)
{
// cryptic id known --> allow public access without login
if (String.IsNullOrEmpty(searchModel.KeyApplicationUserId) && searchModel.ExactKey_CrypticID == null)
throw new UnableToCheckPrivilegesException();
Func<ParcelOrder, bool> userPrivilegeValidation = (x => false);
if (searchModel.ExactKey_CrypticID != null)
{
userPrivilegeValidation = (x => true);
}
else if (searchModel.KeyApplicationUserId != null)
userPrivilegeValidation = privilegeService.UserPrivilegeValdationExpression(searchModel.KeyApplicationUserId);
var criteriaMatchValidation = CriteriaMatchValidationExpression(searchModel);
var parcelOrdersWithNoteHistoryPoints = db.HistoryPoint.Where(hp => hp.Type == (int)HistoryPointType.Note)
.Select(hp => hp.ParcelOrderID)
.Distinct();
Func<ParcelOrder, bool> completeExpression = order => userPrivilegeValidation(order) && criteriaMatchValidation(order);
searchModel.PaginationTotalCount = db.ParcelOrder.Count(completeExpression);
// todo: use this count for pagination
}
public Func<ParcelOrder, bool> CriteriaMatchValidationExpression(ParcelOrderSearchModel searchModel)
{
Func<ParcelOrder, bool> expression =
po => po.ID == 1;
expression =
po =>
(searchModel.KeyUploadID == null || po.UploadID == searchModel.KeyUploadID)
&& (searchModel.KeyCustomerID == null || po.CustomerID == searchModel.KeyCustomerID)
&& (searchModel.KeyContainingVendorProvidedId == null || (po.VendorProvidedID != null && searchModel.KeyContainingVendorProvidedId.Contains(po.VendorProvidedID)))
&& (searchModel.ExactKeyReferenceNumber == null || (po.CustomerID + "-" + po.ReferenceNumber) == searchModel.ExactKeyReferenceNumber)
&& (searchModel.ExactKey_CrypticID == null || po.CrypticID == searchModel.ExactKey_CrypticID)
&& (searchModel.ContainsKey_ReferenceNumber == null || (po.CustomerID + "-" + po.ReferenceNumber).Contains(searchModel.ContainsKey_ReferenceNumber))
&& (searchModel.OrKey_Referencenumber_ConsignmentID == null ||
((po.CustomerID + "-" + po.ReferenceNumber).Contains(searchModel.OrKey_Referencenumber_ConsignmentID)
|| (po.VendorProvidedID != null && po.VendorProvidedID.Contains(searchModel.OrKey_Referencenumber_ConsignmentID))))
&& (searchModel.KeyClientName == null || po.Parcel.Name.ToUpper().Contains(searchModel.KeyClientName.ToUpper()))
&& (searchModel.KeyCountries == null || searchModel.KeyCountries.Contains(po.Parcel.City.Country))
&& (searchModel.KeyOrderStates == null || searchModel.KeyOrderStates.Contains(po.State.Value))
&& (searchModel.KeyFromDateRegisteredToOTS == null || po.DateRegisteredToOTS > searchModel.KeyFromDateRegisteredToOTS)
&& (searchModel.KeyToDateRegisteredToOTS == null || po.DateRegisteredToOTS < searchModel.KeyToDateRegisteredToOTS)
&& (searchModel.KeyFromDateDeliveredToVendor == null || po.DateRegisteredToVendor > searchModel.KeyFromDateDeliveredToVendor)
&& (searchModel.KeyToDateDeliveredToVendor == null || po.DateRegisteredToVendor < searchModel.KeyToDateDeliveredToVendor);
return expression;
}
public Func<ParcelOrder, bool> UserPrivilegeValdationExpression(string userId)
{
var roles = GetRolesForUser(userId);
Func<ParcelOrder, bool> expression =
po => po.ID == 1;
if (roles != null)
{
if (roles.Contains("ParcelAdministrator"))
expression =
po => true;
else if (roles.Contains("RegionalAdministrator"))
{
var user = db.AspNetUsers.First(u => u.Id == userId);
if (user.RegionalAdministrator != null)
{
expression =
po => po.HubID == user.RegionalAdministrator.HubID;
}
}
else if (roles.Contains("Customer"))
{
var customerID = db.AspNetUsers.First(u => u.Id == userId).CustomerID;
expression =
po => po.CustomerID == customerID;
}
else
{
expression =
po => false;
}
}
return expression;
}
If you can possibly avoid it, don't count for pagination. Just return the first page. It's always expensive to count and adds little to the user experience.
And in any case you're building the dynamic search wrong.
You're calling IEnumerable.Count(Func<ParcelOrder,bool>), which will force client-side evaluation where you should be calling IQueryable.Count(Expression<Func<ParcelOrder,bool>>). Here:
Func<ParcelOrder, bool> completeExpression = order => userPrivilegeValidation(order) && criteriaMatchValidation(order);
searchModel.PaginationTotalCount = db.ParcelOrder.Count(completeExpression);
But there's a simpler, better pattern for this in EF: just conditionally add criteria to your IQueryable.
eg put a method on your DbContext like this:
public IQueryable<ParcelOrder> SearchParcels(ParcelOrderSearchModel searchModel)
{
var q = this.ParcelOrders();
if (searchModel.KeyUploadID != null)
{
q = q.Where( po => po.UploadID == searchModel.KeyUploadID );
}
if (searchModel.KeyCustomerID != null)
{
q = q.Where( po.CustomerID == searchModel.KeyCustomerID );
}
//. . .
return q;
}

LINQ XML C# remove where condition in loop

Trying to delete nodes under certain conditions. Basically if certain checkboxes are checked I give an extra query with WHERE statement to my IENumerable named upit. After the queries has been set im trying to delete them iterating through every one, but nothing gets deleted everytime.
XDocument X = XDocument.Load(#"Financije.xml");
var upit = X.Element("POPIS").Elements("PODACI");
if (mjesec.Checked) { upit = upit.Where(E => (Convert.ToInt32(E.Element("MJESEC").Value) == Convert.ToInt32(mjesecbox.Text))); }
if (godina.Checked) { upit = upit.Where(E => (Convert.ToInt32(E.Element("GODINA").Value) == Convert.ToInt32(godinabox.Text))); }
if (ime.Checked) { upit = upit.Where(E => (E.Element("IME").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower()))); }
if (opis.Checked) { upit = upit.Where(E => (E.Element("OPIS").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower()))); }
if (veceod.Checked) { upit = upit.Where(E => (Convert.ToInt32(E.Element("CIJENA").Value.ToString()) > Convert.ToInt32(iznos.Text.ToString()))); }
if (manjeod.Checked) { upit = upit.Where(E => (Convert.ToInt32(E.Element("CIJENA").Value.ToString()) < Convert.ToInt32(iznos.Text.ToString()))); }
foreach (var item in upit)
{
upit.Remove();
}
and this is my XML file
<?xml version="1.0" encoding="utf-8"?>
<POPIS>
<PODACI>
<IME>test</IME>
<CIJENA>200</CIJENA>
<DATUM>12.1.2019</DATUM>
<MJESEC>1</MJESEC>
<GODINA>2019</GODINA>
<OPIS>test123333</OPIS>
</PODACI>
<PODACI>
<IME>voda</IME>
<CIJENA>230</CIJENA>
<DATUM>12.4.2018</DATUM>
<MJESEC>4</MJESEC>
<GODINA>2018</GODINA>
<OPIS>yes123no</OPIS>
</PODACI>
<PODACI>
<IME>oops</IME>
<OPIS>nice</OPIS>
<CIJENA>3</CIJENA>
<MJESEC>5</MJESEC>
<GODINA>2018<GODINA/>
<DATUM>24.02.2019</DATUM>
</PODACI>
<PODACI>
<IME>test</IME>
<OPIS>123</OPIS>
<CIJENA>1</CIJENA>
<MJESEC>12</MJESEC>
<GODINA>2019<GODINA/>
<DATUM>24.02.2019</DATUM>
</PODACI>
</POPIS>
It's a bit unclear what you want to end up with, but I assume you want to exclude items where they fail at least one case where the checkbox is checked and that comparison you are doing fails. So below achieves it in one query:
var X = XDocument.Load("Financije.xml");
var upit = X.Element("POPIS").Elements("PODACI");
var toRemove = upit.Where(u =>
// If mjesec is not checked, skip the predicate, otherwise evaluate it.
(!mjesec.Checked || (Convert.ToInt32(u.Element("MJESEC").Value) == Convert.ToInt32(mjesecbox.Text)))
// *And* do the same for godina and the rest of checkboxes...
&& (!godina.Checked || (Convert.ToInt32(u.Element("GODINA").Value) == Convert.ToInt32(godinabox.Text)))
&& (!ime.Checked || (u.Element("IME").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower())))
&& (!opis.Checked || (u.Element("OPIS").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower())))
&& (!veceod.Checked || (Convert.ToInt32(u.Element("CIJENA").Value.ToString()) > Convert.ToInt32(iznos.Text.ToString())))
&& (!manjeod.Checked || (Convert.ToInt32(u.Element("CIJENA").Value.ToString()) < Convert.ToInt32(iznos.Text.ToString()))));
X.Element("POPIS").ReplaceAll(upit.Except(toRemove));
X.Save("Financije.xml");
Give it a try
XDocument X = XDocument.Load(#"Financije.xml");
var upit = X.Element("POPIS").Elements("PODACI");
//I‘m only on phone so please excuse, if I take the wrong Type. Assign to null, to prevent Compiler-Error
IEnumerable<XElement> upitPart= null;
if (mjesec.Checked) { upitPart = upit.Where(E => (Convert.ToInt32(E.Element("MJESEC").Value) == Convert.ToInt32(mjesecbox.Text))); }
if (godina.Checked) { upitPart = upitPart.Where(E => (Convert.ToInt32(E.Element("GODINA").Value) == Convert.ToInt32(godinabox.Text))); }
if (ime.Checked) { upitPart = upitPart.Where(E => (E.Element("IME").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower()))); }
if (opis.Checked) { upitPart = upitPart.Where(E => (E.Element("OPIS").Value.ToString().ToLower().Contains(search.Text.ToString().ToLower()))); }
if (veceod.Checked) { upitPart = upitPart.Where(E => (Convert.ToInt32(E.Element("CIJENA").Value.ToString()) > Convert.ToInt32(iznos.Text.ToString()))); }
if (manjeod.Checked) { upitPart = upitPart.Where(E => (Convert.ToInt32(E.Element("CIJENA").Value.ToString()) < Convert.ToInt32(iznos.Text.ToString()))); }
if(upitPart != null)
{
foreach (var item in upitPart)
{
upit.Remove(item);
}
}

The best way to filter a list based on multiple check-boxes selected

At the moment I get the number of possible combinations (Factorial of the number of check-boxes) and write that many if statements, something like:
Assuming I have 3 check-boxes:
if (IncludeIncomingCalls && !IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && !x.ExternalCall);
}
if (!IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
}
if (!IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
}
if (IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
}
if (IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
}
if (!IncludeIncomingCalls && IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && x.ExternalCall);
}
Even though this will meet the requirement I don't see it as an optimal solution considering that the number of check-boxes might increase in the future and the number of the combinations could get massive.
I was wandering if there is a known pattern when it comes to filtering lists based on selected check-boxes?
Compare the Boolean value with each field.
Try this:
return _callsData.Where(x => x.IncomingCall == IncludeIncomingCalls && x.OutgoingCall == IncludeOutgoingCalls && x.ExternalCall== IncludeExternalCalls);
Try this :
return _callsData.Where(x => x.IncomingCall==IncludeIncomingCalls && x.OutgoingCall==IncludeOutgoingCalls && x.ExternalCall==IncludeExternalCalls);
Note that you can compose the IQueryable. You can add additional Where clauses as needed.
var result = callsData.Select(x => x);
if (IncludeIncomingCalls) {
result = result.Where(x => x.IncomingCall);
}
else {
result = result.Where(x => !x.IncomingCall);
}
if (IncludeOutgoingCalls) {
result = result.Where(x => x.OutgoingCall);
}
else {
result = result.Where(x => !x.OutgoingCall);
}
if (IncludeExternalCalls) {
result = result.Where(x => x.ExternalCall);
}
else {
result = result.Where(x => !x.ExternalCall);
}
return result;
I just show this as a general pattern. For your usecase, the solution of Ubiquitous Developers is easier to read and understand.
But if the condition is more complex than just a bit flag, this pattern might come in handy. Just as an example:
if (ShowOnlyActive) {
result = result.Where(x => x.State == CallState.Active);
}
else {
result = result.Where(x => x.State == CallState.Deleted || x.State == CallState.Inactive);
}
Off topic, just to further illustrate this general concept: adding additional clauses to an IQueryable can be used to refactor parts of a query to helper or extension methods, for example to implement paging.
public interface IPageableQuery {
// The page size (i.e. the number of elements to be displayed).
// The method processing the Query will Take() this number of elements.
int DisplayLength { get; set; }
// The number of elements that have already been displayed.
// The method processing the Query will Skip() over these elements.
int DisplayStart { get; set; }
}
public static IQueryable<T> ApplyPaging<T>(this IQueryable<T> entries, IPageableQuery query)
where T : class {
if (query.DisplayStart >= 0 && query.DisplayLength > 0) {
return entries.Skip(query.DisplayStart).Take(query.DisplayLength);
}
return entries;
}
You could use the following pattern:
//some checkboxes
CheckBox chkA = ...
CheckBox chkB = ...
CheckBox chkC = ...
//build up your filters
var filters = new List<Predicate<SomeEntity>>();
filters.Add(e => chkA.Checked && e.IsA);
filters.Add(e => chkB.Checked && e.IsB);
filters.Add(e => chkC.Checked && e.IsC);
//And now simply apply the filters
var entities = ... //some enumerable of SomeEntity
var filteredEntities = entities.Where(e => filters.All(filter => filter(e)));
Note that this will only work correctly if IsA, IsB and IsC are excluding conditions, but this seems to be the set up you currently have.

How would I optimize a nested for loop with linq [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
How would i write this with linq?
foreach (var to in allCurrentTradeObjects)
{
foreach (var ro in theseWantMe)
{
if (ro.Type != to.Type
|| ro.MaxRent < to.Rent
|| ro.MinRooms > to.Rooms
|| ro.MinSquareMeters > to.SquareMeters
|| ro.MaxPrice < to.Price
|| ro.MinFloors > to.Floors
|| ro.TradeObjectId == to.TradeObjectId
|| ro.TradeObjectId == myTradeObject.TradeObjectId)
{
continue;
}
RatingListTriangleModel rlt = new RatingListTriangleModel
{
To1Id = myTradeObject.TradeObjectId,
To2Id = to.TradeObjectId,
To3Id = ro.TradeObjectId,
T1OnT2Rating = 0,
T2OnT3Rating = 0,
T3OnT1Rating = 0,
TotalRating = 0
};
//_context.RatingListTriangle.Add(rlt);
this.InsertOrUpdate(rlt);
}
}
this.Save();
var query = from to in allCurrentTradeObjects
from ro in theseWantMe
where ro.Type == to.Type &&
ro.MaxRent >= to.Rent &&
ro.MinRooms <= to.Rooms &&
ro.MinSquareMeters <= to.SquareMeters &&
ro.MaxPrice >= to.Price &&
ro.MinFloors <= to.Floors &&
ro.TradeObjectId != to.TradeObjectId &&
ro.TradeObjectId != myTradeObject.TradeObjectId
select new RatingListTriangleModel
{
To1Id = myTradeObject.TradeObjectId,
To2Id = to.TradeObjectId,
To3Id = ro.TradeObjectId,
T1OnT2Rating = 0,
T2OnT3Rating = 0,
T3OnT1Rating = 0,
TotalRating = 0
};
foreach(var rlt in query)
this.InsertOrUpdate(rlt);
this.Save();
Start by converting the skeleton of the nested loops to LINQ:
var rtls = allCurrentTradeObjects
.SelectMany(to => theseWantMe.Select(ro => new {to, ro}));
This gives you a list of pairs {to, ro}. Now add filtering by inverting the continue condition:
var rtls = allCurrentTradeObjects
.SelectMany(to => theseWantMe.Select(ro => new {to, ro}));
.Where(p => p.ro.Type == p.to.Typpe && p.ro.MaxRent >= p.to.Rent && ...)
Finally, add a Select to call `new:
var rtls = allCurrentTradeObjects
.SelectMany(to => theseWantMe.Select(ro => new {to, ro}));
.Where(p => p.ro.Type == p.to.Typpe && p.ro.MaxRent >= p.to.Rent && ...)
.Select(p => new RatingListTriangleModel {
To1Id = myTradeObject.TradeObjectId,
To2Id = p.to.TradeObjectId,
To3Id = p.ro.TradeObjectId,
...
});
With rtls list in hand, you can call InsertOrUpdate in a loop.
Following is the method syntax.
allCurrentTradeObjects.Select (
to => to.theseWantMe.Where ( ro => !(ro.Type != to.Type
|| ro.MaxRent < to.Rent
|| ro.MinRooms > to.Rooms
|| ro.MinSquareMeters > to.SquareMeters
|| ro.MaxPrice < to.Price
|| ro.MinFloors > to.Floors
|| ro.TradeObjectId == to.TradeObjectId
|| ro.TradeObjectId == myTradeObject.TradeObjectId))
.Select({
var rlt = new RatingListTriangleModel
{
To1Id = myTradeObject.TradeObjectId,
To2Id = to.TradeObjectId,
To3Id = ro.TradeObjectId,
T1OnT2Rating = 0,
T2OnT3Rating = 0,
T3OnT1Rating = 0,
TotalRating = 0
};
this.InsertOrUpdate(rlt);
return rlt;
} ).ToArray();
this.Save();
There are many answers here advocating SelectMany (or double from). These are "optimize for readability" answers in that they do not change the N*M nested loop performance of this operation.
You shouldn't use that approach if both collections are large. Instead, you should take advantage of the well defined relationship between your two collections, and the hashing in Enumerable.Join to reduce the operation to N+M.
var myTradeObject = GetThatOneObject();
IEnumerable<RatingListTriangleModel> query =
from to in allCurrentTradeObjects
//pre-emptively filter to the interesting objects in the first collection
where to.TradeObjectId == myTradeObject.TradeObjectId
//take advantage of hashing in Enumerable.Join - theseWantMe is enumerated once
join ro in theseWantMe
on to.Type equals ro.Type
//remaining matching criteria
where to.Rent <= ro.MaxRent //rent is lower than max
&& ro.MinRooms <= to.Rooms //rooms are higher than min
&& ro.MinSquareMeters <= to.SquareMeters //area is higher than min
&& to.Price <= ro.MaxPrice //price is lower than max
&& ro.MinFloors <= to.Floors // floors are higher than min
&& to.TradeObjectId != ro.TradeObjectId //not same trade object
select CreateRatingListTriangleModel(myTradeObject, to, ro);
foreach(RatingListTriangleModel row in query)
{
this.InsertOrUpdate(row);
}
this.Save();
To increase readability I would start by refactoring the complicated condition and move it to a neat little function (It can also be a method of the object)
private bool IsMatchingTradeObject (TradeObject to, SomeOtherObject ro, int TradeObjectId)
{
return ro.Type == to.Type &&
ro.MaxRent >= to.Rent &&
ro.MinRooms <= to.Rooms &&
ro.MinSquareMeters <= to.SquareMeters &&
ro.MaxPrice >= to.Price &&
ro.MinFloors <= to.Floors &&
ro.TradeObjectId != to.TradeObjectId &&
ro.TradeObjectId != TradeObjectId;
}
Second I would do the same with the creation and initialization of the RatingListTriangleModel, i.e. move it to a small method and give it a meaningful name.
private RatingListTriangleModel CreateModel(TradeObject to, SomeOtherObject ro, int TradeObjectId)
{
return new RatingListTriangleModel
{
To1Id = myTradeObject.TradeObjectId,
To2Id = to.TradeObjectId,
To3Id = ro.TradeObjectId,
T1OnT2Rating = 0,
T2OnT3Rating = 0,
T3OnT1Rating = 0,
TotalRating = 0
};
The remaining code is much easier to read
foreach (var to in allCurrentTradeObjects)
foreach (var ro in theseWantMe)
if (IsMatchingTradeObject(to, ro, myTradeObject.TradeObjectId))
this.InsertOrUpdate(CreateModel(to, ro, myTradeObject.TradeObjectId));
this.Save();
Converting this to LINQ is easy:
allCurrentTradeObjects.Select (
to => to.Where (
ro => IsMatchingTradeObject (to, ro, myTradeObject.TradeObjectId)
)
).Select(
{
this.InsertOrUpdate(CreateModel(to, ro, myTradeObject.TradeObjectId));
return null;
}
);
this.Save();
However, the foreach-loops seem easier to read.

Categories