Foreach Loop to linq - linq

This might be very simple but I am not able to see data after converting
public void SearchGrid(int id, ObservableCollection<TestModel> msgField)
{
foreach (var c in msgField)
DisplayColor = c.Id == id ? Brushes.Blue : Brushes.Black;
}
Linq:
Enumerable.FirstOrDefault(msgField.Where(x => x.Id == id), x => { DisplayColor = Brushes.CadetBlue; });

In general, it's a bad idea to create queries that produce side effects. I would personally rewrite this as:
DisplayColor = msgField.Any(x => x.Id == id) ? Brushes.Blue : Brushes.Black;
This produces the output I believe you are attempting to generate. It will set the DisplayColor to blue or black based on whether there is a matching "msgField" element.
To get the same result as your current looping code, you could use:
if (msgField.Any())
DisplayColor = msgField.Last().Id == id ? Brushes.Blue : Brushes.Black;
This will produce the same output as your loop, as your loop will overwrite DisplayColor with each iteration, so only the last item matters.

Related

Using Linq, How can I properly append multiple Where clauses so that they appear in the appropriate order?

The order I would like the end result to appear in is Exact Matches first given a input string, followed by other matches that are Contains for a given field. I tried to approach this in a very rudimentary way as shown here in this example:
var raw = Model.SearchResults.Where(m => m.EffectiveDateTime != null).OrderBy(m => m.EffectiveDateTime).ToList();
var exact = raw.Where(m => m.IssueNumber.ToLower() == Model.SearchText.ToLower());
var contains = raw.Where(m => m.IssueNumber.ToLower().Contains(Model.SearchText.ToLower()));
var list = exact.Union(contains);
This approach seems like it'd be a really bad way to do this. In fact, the Union portion seems to effectively crash my application. Is there an opposite to Intersection which would give me the remaining results outside the Exact matches that I could then append to a final list so that the order would be Exact Matches followed by StartsWith matches followed finally by Contains matches in that descending order?
To answer your original question, you can use a temporary expression to classify the match types, then order by the match type and other criteria, and it will translate to SQL as well:
var st = Model.SearchText.ToLower();
var list = Model.SearchResults.Where(m => m.EffectiveDateTime != null)
.Select(m => new {
m,
im = m.IssueNumber.ToLower()
})
.Select(mim => new {
mim.m,
Rank = mim.im == st ? 1 : mim.im.StartsWith(st) ? 2 : mim.im.Contains(st) ? 3 : 4
})
.Where(mr => mr.Rank < 4)
.OrderBy(mr => mr.Rank)
.ThenBy(mr => mr.m.EffectiveDateTime)
.Select(mr => mr.m)
.ToList();
I did the double Select to emulate let from fluent syntax, which I think is a bit clearer than lambda syntax in this case:
var lisx = (from m in Model.SearchResults
where m.EffectiveDateTime != null
let im = m.IssueNumber.ToLower()
let Rank = im == st ? 1 : im.StartsWith(st) ? 2 : im.Contains(st) ? 3 : 4
where Rank < 4
orderby Rank, m.EffectiveDateTime
select m)
.ToList();
Also, if you do the whole query in the database, the ToLower is likely unnecessary, as the default for SQL is probably to be case-insensitive anyway.
Actually, I went back to the drawing board and figured it out. This is a little bit better for me and returns the results I needed.
var list = Model.SearchResults
.Where(e => e.A.ToLower().Contains(Model.SearchText.ToLower()))
.GroupBy(d => new { d.A, d.B, d.C})
.OrderBy(x => x.Key.A)
.ThenBy(x => x.Key.B)
.ThenBy(x => x.Key.C)
.Select(x => new
{
A= x.Key.A,
B= x.Key.B,
C= x.Key.C
})
.ToList();

LINQ to Entities does not recognize the method 'Char get_Chars(Int32)' method, and this method cannot be translated into a store expression

I would like to select only the records that have the field "1" from the string eventTriggers (that looks something like this : "00100010" )
I've tried and succesfully done so with more than 1 calls .. but i doubt its efficient. Basically I would want something like this ... but apprently LINQ does not support this.
(LINQ to Entities does not recognize the method 'Char get_Chars(Int32)' method, and this method cannot be translated into a store expression.)
using (var service = new dB.Business.Service.BaseBusinessService<memo>())
{
List<memo> result = service.Repository.GetQuery().Where(p => p.ID == ID && p.eventTriggers[index] == '1').ToList();
}
Any hints towards the correct solution ? Thank you !
I Had the same problem and solved It with substring.
ervice.Repository.GetQuery().Where(p => p.ID == ID && p.eventTriggers.Substring(index,1) == "1").ToList();
EF can't convert the char array operation into a valid query. How about
IEnumerable<Memo> memos
using (var service = new dB.Business.Service.BaseBusinessService<Memo>())
{
memos = service.Repository.GetQuery()
.Where(p => p.ID == ID).AsEnumerable();
}
var result = memos.Where(m => m.eventTriggers[index] == '1').ToList();
This gets all the memos with a matching ID locally then filters on the eventTriggers array.
Alternatively you could convert eventTriggers into a numeric value and use a bit mask, this would probably be a much faster query.
Linq looking like this,
using (var service = new dB.Business.Service.BaseBusinessService<Memo>())
{
result = service.Repository.GetQuery()
.Where(p =>
p.ID == ID
&&
m.eventTriggers & mask != 0).ToList();
}
more exapmles here
Solved:
using (var service = new dB.Business.Service.BaseBusinessService<memo>())
{
List<memo> result = service.Repository.GetQuery().Where(p => p.ID == ID && p.eventTriggers.Contains('1')).ToList();
}

Linq to Entities performance problem with many columns

I am having an issue with getting linq to entities to perform well. The query I have (not mine, maintaining someone's code :-)), has several includes that I've determined are all necessary for the WPF screen that consumes the results of this query.
Now, the SQL generated executes very fast and only returns one row of data. But it is returning 570 columns, and i think the performance hit is in the overhead of creating all the objects and all of those fields.
I've tried using lazy loading, but that doesn't seem to have any effect on performance.
I've tried removing any of the "include" statements that aren't necessary, but it appears that they all are needed.
here's the linq query:
var myQuery =
from appt in ctx.Appointments
.Include("ScheduleColumnProfile")
.Include("EncounterReason")
.Include("Visit")
.Include("Visit.Patient")
.Include("Visit.Patient.PatientInsurances")
.Include("Visit.Patient.PatientInsurances.InsuranceType")
.Include("Visit.Patient.PatientInsurances.InsuranceCarrier")
.Include("MasterLookup")
.Include("User1")
.Include("User2")
.Include("Site")
.Include("Visit.Patient_CoPay")
.Include("Visit.Patient_CoPay.User")
.Include("Visit.VisitInstructions.InstructionSheet")
where appt.VisitId == visitId
&& appt.MasterLookup.LookupDescription.ToUpper() != Rescheduled
&& appt.Site.PracticeID == practiceId
&& appt.MasterLookup.LookupDescription.ToUpper() != Cancelled
orderby appt.AppointmentId descending
select appt;
The SQL generate is 4000 lines long with 570 columns in the select statment and 3 or 4 Union ALLs, so I'm not going to paste it here unless someone REALLY wants to see it. Basically, i'm looking for a way to get rid of the unions if possible, and trim down the columns to only what's needed.
Help!
:-)
if anyone is keeping track, this is the solution that ended up working for me. Thanks to everyone who commented and made suggestions... it eventually lead me to what i have below.
ctx.ContextOptions.LazyLoadingEnabled = true;
var myQuery =
from appt in ctx.Appointments
where appt.VisitId == visitId
&& appt.MasterLookup.LookupDescription.ToUpper() != Rescheduled
&& appt.Site.PracticeID == practiceId
&& appt.MasterLookup.LookupDescription.ToUpper() != Cancelled
orderby appt.AppointmentId descending
select appt;
var myAppt = myQuery.FirstOrDefault();
ctx.LoadProperty(myAppt, a => a.EncounterReason);
ctx.LoadProperty(myAppt, a => a.ScheduleColumnProfile);
ctx.LoadProperty(myAppt, a => a.Visit);
ctx.LoadProperty(myAppt, a => a.MasterLookup);
ctx.LoadProperty(myAppt, a => a.User1);
ctx.LoadProperty(myAppt, a => a.User2);
ctx.LoadProperty(myAppt, a => a.PatientReferredProvider);
var myVisit = myAppt.Visit;
ctx.LoadProperty(myVisit, v => v.Patient);
ctx.LoadProperty(myVisit, v => v.Patient_CoPay);
ctx.LoadProperty(myVisit, v => v.VisitInstructions);
ctx.LoadProperty(myVisit, v => v.EligibilityChecks);
var pat = myVisit.Patient;
ctx.LoadProperty(pat, p => p.PatientInsurances);
//load child insurances
foreach (PatientInsurance patIns in myAppt.Visit.Patient.PatientInsurances)
{
ctx.LoadProperty(patIns, p => p.InsuranceType);
ctx.LoadProperty(patIns, p => p.InsuranceCarrier);
}
//load child instruction sheets
foreach (VisitInstruction vi in myAppt.Visit.VisitInstructions)
{
ctx.LoadProperty(vi, i => i.InstructionSheet);
}
//load child copays
foreach (Patient_CoPay coPay in myAppt.Visit.Patient_CoPay)
{
ctx.LoadProperty(coPay, c => c.User);
}
//load child eligibility checks
foreach (EligibilityCheck ec in myAppt.Visit.EligibilityChecks)
{
ctx.LoadProperty(ec, e => ec.MasterLookup);
ctx.LoadProperty(ec, e => ec.EligibilityResponse);
}
I would recommend creating a new Class that contains only the properties that you need to display. When you project to a new type you don't need to have Include statements, but you can still access the navigation properties of the entity.
var myQuery = from appt in ctx.Appointments
where appt.VisitId == visitId
&& appt.MasterLookup.LookupDescription.ToUpper() != Rescheduled
&& appt.Site.PracticeID == practiceId
&& appt.MasterLookup.LookupDescription.ToUpper() != Cancelled
orderby appt.AppointmentId descending
select new DisplayClass
{
Property1 = appt.Prop1,
Proeprty2 = appt.Visit.Prop1,
.
.
.
};

Calling a function within a Linq query

If I want to iterate through a collection, and call a function on each element in the collection, I could go with :
foreach (var obj in objColl)
{
MyFunction(obj);
}
Should I want to do this with linq, I can use either of those :
//#1
var unused = (from var obj in objColl select MyFunction(obj)).ToList();
//#2
var unused = objColl.Select(obj => MyFunction(obj)).ToList();
I know this works, but it doesn't seem right. Of course, my actual cases are more complex queries that that, but it comes down to this since I could build my IQueryable with Linq and iterate through it and call the function.
Edit:
Here is one example of what I did. (Item# are things I can't disclose)
var dummyValue = (from
Item7 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item1" && age.TargetName == "Item2")
join Item6 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item2" && age.TargetName == "Item3") on Item6.TargetValue equals Item7.SourceValue
join agency in dal.GetAgencies() on Item7.SourceValue equals agency.Agency
orderby Item7.TargetValue
select vl.ValueListItems.Add(agency.ID, Item7.TargetValue)).ToList();
Go with the simple foreach, as you are clearly wanting to perform an action on (and/or using) the objects in your collection as opposed to wishing to project/filter/group/etc. the items in the sequence. LINQ is about the latter set of operations.
Edit: In the case of your update, I would simply create a query, and then iterate over the query in the foreach to perform the action.
var query = from Item7 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item1" && age.TargetName == "Item2")
join Item6 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item2" && age.TargetName == "Item3") on Item6.TargetValue equals Item7.SourceValue
join agency in dal.GetAgencies() on Item7.SourceValue equals agency.Agency
orderby Item7.TargetValue
select new { ID = agency.ID, Value = Item7.TargetValue };
foreach (var item in query)
vl.ValueListItems.Add(item.ID, item.Value);
To be frank, you have the same loop happening in your code, you merely mask it by using the ToList() extension method. As a byproduct, you are creating a list of values that you have no intention of using, while somewhat obfuscating the true intention of the code, all to save maybe a few characters.
Typically, a query shouldn't have any side effects (i.e. it shouldn't modify the state of the data or other data in your application) which raises the question, does MyFunction modify the state of your application? If it does, then you should use a foreach loop.
How about an Each() extension method?
public static void Each<T>(this IEnumerable<T> target, Action<T> action)
{
if (target == null) return;
foreach (T obj in target)
action(obj);
}

How to join results from two different sets in LINQ?

I get some data about customers in my database with this method:
public List<KlientViewModel> GetListOfKlientViewModel()
{
List<KlientViewModel> list = _klientRepository.List().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
}).ToList();
return list;
}
but also I have another method which counts value for extra field in KlientViewModel - field called 'Naleznosci'.
I have another method which counts value for this field based on customers ids, it looks like this:
public Dictionary<int, decimal> GetNaleznosc(List<int> klientIds)
{
return klientIds.ToDictionary(klientId => klientId, klientId => (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
So what I want to do is to join data from method GetNaleznosc with data generated in method GetListOfKlientViewModel. I call GetNaleznosc like this:
GetNaleznosc(list.Select(k => k.Id).ToList())
but don't know what to do next.
Having obtained the dictionary:
var dict = GetNaleznosc(list.Select(k => k.Id).ToList());
You can now efficiently look up the decimal value of Naleznosci for a given client:
foreach (var k in list)
k.Naleznosci = dict[k.Id];
Now you have merged the values into the main list. Is this what you mean?
By the way, in your function that builds the dictionary, you make it accept a List<int>, but then all you do is call ToDictionary on it, which only requires IEnumerable<int>. So change the parameter type to that, and then you can call it:
var dict = GetNaleznosc(list.Select(k => k.Id));
This removes the call to ToList, which avoids making an unnecessary intermediate copy of the whole list of Ids. Probably won't make much difference in this case if you're hitting a database and then building up a large set of results in memory, but maybe worth bearing in mind for other uses of these operations.
Also, looking again at the helper function, there is no apparent advantage in building up the set of results in a dictionary for a list of ids, because each one is handled independently. You could simply put:
public decimal GetNaleznosc(int klientId)
{
return (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
That is, provide a function that discovers just one value. Now you can directly build the right list:
public List<KlientViewModel> GetListOfKlientViewModel()
{
return _klientRepository.List().AsEnumerable().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
Naleznosci = GetNaleznosc(k.Id)
}).ToList();
}

Resources