C# yield
Tjenare.
Jag har ett icke-kritiskt problem som jag försöker lösa.
Jag har gjort en funktion som "yieldar" lediga tider som man ska kunna boka i ett bokningsprogram.
Den används på detta vis:
//Lazy load bookings
var bookableObjects = getBookableObjects(DateTime.Now);
Då det är en lazy load så får man applicera valfritt filter. Om inget filter appliceras och man kör ToList() så genererar den tider okontrollerat 200 dagar. Ett exempel på ett enkelt filter:
//Hämtar endast bokningsbara tider från och med idag och fem dagar framåt.
var bookings = bookableObjects.TakeWhile(x => x.Appointment <= DateTime.Now.AddDays(5)).ToList();
Så långt är allt bra.
På detta vis fungerar den i stort.
public IEnumerable<BookingObjects> getBookableObjects(DateTime startDate)
{
//Skapa index över bokningsbara objekt, etc. Halvdyr operation, något vi vill helst göra EN gång.
//Det sker bara en gång i det första fallet med (x => x.Appointment <= DateTime.Now.AddDays(5))
//Problemet blir när jag applicerar annat filter... mer om det i problembeskrivningen.
//Loopa dagens datum och öka med en dag varje steg. Failsafe här på max x antal dagar om man glömmer att applicera ett filter.
//Öppna databas och ladda in schemat för alla objekt.
//kalla på och yielda getBookableObjectsForDate(schema);
}
Då till problemet:
var bookableObjects = getBookableObjects(DateTime.Now);
var bookings = bookableObjects.TakeWhile(x => x.Appointment <= bookableObjects.First().Appointment.AddDays(5).ToList();
Det första filtret kommer inte att presentera några objekt om det är fullt mer än fyra dagar framåt, vilket inte är önskvärt.
Detta är ett mycket trevligare filter då den hämtar den senaste bokningsbara tiden samt alla tider 5 dagar efter denna tid.
Ex: Säg att första lediga tiden är om 7 dagar, då får vi den tiden + 5 dagar till.
Med detta filter så kör den hela getBookableObjects() för varje gång den (troligtvis) kör First().
Detta gör alltså att alla bokningsbara tider jämförs med en HEL laddning från första dagen. Praktiskt taget så blir det att First() genererar en full laddning varje gång.
Detta problem kommer jag runt om jag gör på detta vis
var bookableObjects = getBookableObjects(DateTime.Now);
var firstBooking = bookableObjects.First();
var bookings = bookableObjects.TakeWhile(x => x.Appointment <= firstBooking.Appointment.AddDays(5).ToList();
Med detta filter så görs det bara en extra laddning av första dagen, för att kunna hämta första lediga tiden.
1. Hur kommer det sig att logiken blir annorlunda för att jag sparar undan variabeln? Jag gissar på att det är för om man kör First() i en lambda/linq så blir det en lazy evaluation varje gång. När jag sparar till en variabel så blir det en lazy evaluation en gång och den gör en vanlig evaluering så jag får resultatet till variabeln.
1.1 Planen längre fram är att jag ska spara undan vissa Querys som man kan applicera på listan. Så punkt 1 blir inte riktig hållbar då om man måste spara undan vissa värden först.
2. Finns det något sätt jag kan komma bort från den extra laddningen helt och hållet? Om man ser till logiken så är det första objektet och First() identiska. Enligt punkt 1 så verkar ju det "logiskt", men kan man komma bort från det? Att den första tiden blir en riktig laddning och ingen lazy evaluation körs?
Det blev ganska mycket text. Fråga på om det är några oklarheter. Det är som sagt inte ett superkritiskt problem då jag kan komma runt det på olika sätt. Men det hade varit snyggt om man kan applicera hela querys för att filtrera resultatet.
ηλί, ηλί, λαμά σαβαχθανί!?