Das Problem ist beschrieben, die theoretischen Grundlagen sind aus den Füßen, kommen wir also zur ersten Empfehlung bzgl. des Schreibens von LINQ-Ausdrücken…
EmpfehlungArtikel 1 des Grundgesetzes zur LINQ-Programmierung:
”null” ist kein zulässiger Wert für Enumerationen! Zuwiderhandlungen werden mit Exceptions in Produktion bestraft.
Die null hat im Universum funktionaler Programmierung schlicht und einfach keinen Platz.
“Code written in the functional style is often safer and easier to reason about. For example, functional languages avoid using the null value.” (“F# and Functional Programming”)
“The null value is not normally used in F# for values or variables.” (Null Values (F#))
“I think the succinct summary of why null is undesirable is that meaningless states should not be representable.” (“Best explanation for languages without null”)
Unter anderem ist diese Garantie die Grundlage dafür, dass eine Verkettung von Aufrufen, wie sie zu LINQ gehören, überhaupt erst valide ist:
1: IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
Ohne diese Garantie müsste der defensive Entwickler jeden Aufruf in dieser Kette einzeln machen und auf null prüfen:
1: IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0);
2: if (numQuery2 != null)
3: numQuery2 = numQuery2 .OrderBy(n => n);
Das würde dem Ganzen etwas die Eleganz nehmen…
Meistens bekommt man Enumerationen und verarbeitet diese weiter. In diesem Fall profitiert man einfach nur und muss das schon mutwillig kaputt machen. Manchmal muss man jedoch die Enumeration initial produzieren, z.B.:
1: public IEnumerable<Orderbook> GetAll()
2: {
3: IEnumerable<Orderbook> result = null;
4: if (User.IsAdmin(user))
5: result = _context.OrderbookSet;
6: // [...]
7: return result;
8: }
Genau hier fällt das Kind in den Brunnen wenn die Berechtigungen nicht passen. Ein einfacher Aufruf wie…
1: var candidates= Orderbook.GetAll().Where(ob => ob.Location == "Frankfurt");
… ist damit nicht mehr machbar.
Korrekt wäre hingegen:
1: public IEnumerable<Orderbook> GetAll()
2: {
3: IEnumerable<Orderbook> result = Enumerable.Empty<Orderbook>();
4: if (User.IsAdmin(user))
5: result = _context.OrderbookSet;
6: // [...]
7: return result;
8: }
Wenn der Aufrufer keine Berechtigung hat die Daten zu sehen, bekommt er immer noch eine Enumeration, nur enthält diese eben keine Elemente.
Etwas umständlicher wird das, wenn man mit dem Entity Framework gegen IQueryable arbeitet, denn Queryable bietet keine Pendant zu Empty() an. Wohl aber eine Methode AsQueryable(), die ich verwenden kann, um ein IEnumerable zu “casten”:
1: public IQueryable<Orderbook> GetAll(string userName)
2: {
3: IQueryable<Orderbook> result = Enumerable.Empty<Orderbook>().AsQueryable();
4: if (User.IsAdmin(user))
5: result = _context.OrderbookSet;
6: // [...]
7: }
Es ist also nicht notwendig, extra eine Collection anzulegen, wie ich das auch schon gesehen habe:
1: IQueryable<Orderbook> result = new List<Orderbook>().AsQueryable();
Da der C#-Compiler nicht wissen kann, dass eine Methode funktionalen Erfordernissen Rechnung tragen muss, kann er den Entwickler hier leider nicht unterstützen.
Daher liegt diese Anforderung nur in Form einer Konvention vor, die der Entwickler selbst einzuhalten hat – unter Androhung von Strafe. 😉
Sie sehen gerade einen Platzhalterinhalt von Facebook. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr InformationenSie sehen gerade einen Platzhalterinhalt von Instagram. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr InformationenSie sehen gerade einen Platzhalterinhalt von X. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr Informationen