Ik ben bezig met een web applicatie in ASP.NET Core 2.1 (C#), in combinatie met Entity Framework Core en een MySQL database.
In de database heb ik een lijst met producten (Products). Gebruikers kunnen een rating geven aan een product welke opgeslagen worden in ProductRatings. Een Product heeft dus een lijst met ratings, en een rating heeft een referentie naar het product en welke gebruiker de rating heeft gegeven.
De database modellen zijn als volgt:
Ik wil nu een overzicht pagina bouwen waarop een lijstje met een aantal producten te zien zijn. In dit overzicht wil ik voor elk product de hoogste en de een-na-hoogste rating tonen, zowel het cijfer als de naam van de gebruiker die de rating heeft gegeven.
In de Product class heb ik momenteel de properties HighestRating en SecondHighestRating, hier kom ik later op terug.
Hoewel ik dit allemaal wel voor elkaar krijg loop ik tegen dingen aan waarvan ik denk dat het beter zou moeten. Ik zie echter nog niet zo snel in hoe precies, dus ik zou graag de discussie starten, wat denken jullie?
Probleem 1: hoe haal ik zo efficient mogelijk de producten en alleen de hoogste en een-na-hoogste rating uit de database?
Momenteel haal ik voor elk product ook alle ratings op, en filter ik later in een loop tot alleen de twee hoogste ratings. Dit is natuurlijk niet optimaal. Hoewel ik niet zoveel ratings verwacht dat dit echt problemen gaat leveren zou ik toch graag willen weten hoe het beter kan, mocht het ooit voorkomen op een tabel die wel enorm veel records heeft.
Huidige code die de objecten uit de database haalt:
Een alternatief zou zijn om eerst de producten op te halen, en dan per product een nieuwe query te doen om de twee hoogste ratings op te halen. Afhankelijk van hoeveel producten en ratings er verwacht worden kan ik me voorstellen dat dit efficienter is (bijv: heel weinig producten met enorm veel ratings).
Mijn gevoel zegt echter dat dit in één query moet kunnen, in ieder geval in SQL (of het ook vertaald kan worden naar een LINQ query is nog de vraag).
Weet iemand hoe dit beter kan?
Probleem 2: hoe hou ik de database en de app logica gescheiden?
Momenteel heb ik de twee properties HighestRating en SecondHighestRating in de Product class gestopt, met een NotMapped zodat het database model geen rare dingen gaat doen. Ik vind dit niet zo netjes, aangezien de HighestRating alleen nuttig is voor de applicatie zelf en weinig met de database te maken heeft.
Een alternatief zou zijn om een apart model (viewmodel) te maken met Product en de ratings apart:
Dit viewmodel zou ik dan gebruiken om de koppeling te maken tussen Product en de twee ratings die alleen voor m'n applicatie relevant zijn. Echter, wil ik alle informatie in een query halen (zie probleem 1) dan zal de repository / data laag nog steeds toegang moeten hebben tot deze viewmodel. Opnieuw ben ik dus een koppeling aan het maken tussen database en applicatie logic.
Ook dit is geen enorm probleem maar het is volgens mij niet netjes, en het is een fundamenteel "probleem" waar ik vaker tegenaan loop. Ik denk dat er een standaard oplossing is maar ik zie hem niet.
In de database heb ik een lijst met producten (Products). Gebruikers kunnen een rating geven aan een product welke opgeslagen worden in ProductRatings. Een Product heeft dus een lijst met ratings, en een rating heeft een referentie naar het product en welke gebruiker de rating heeft gegeven.
De database modellen zijn als volgt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| public class Product { public int Id {get;set;} public string Name {get;set;} public int Type {get;set;} public ICollection<ProductRating> Ratings {get;set;} [NotMapped] public ProductRating HighestRating {get;set;} [NotMapped] public ProductRating SecondHighestRating {get;set;} } public class ProductRating { public int Id {get;set;} public int Rating {get;set;} public int UserId {get;set;} public User User {get;set;} public int ProductId {get;set;} public Product Product {get;set;} } |
Ik wil nu een overzicht pagina bouwen waarop een lijstje met een aantal producten te zien zijn. In dit overzicht wil ik voor elk product de hoogste en de een-na-hoogste rating tonen, zowel het cijfer als de naam van de gebruiker die de rating heeft gegeven.
In de Product class heb ik momenteel de properties HighestRating en SecondHighestRating, hier kom ik later op terug.
Hoewel ik dit allemaal wel voor elkaar krijg loop ik tegen dingen aan waarvan ik denk dat het beter zou moeten. Ik zie echter nog niet zo snel in hoe precies, dus ik zou graag de discussie starten, wat denken jullie?
Probleem 1: hoe haal ik zo efficient mogelijk de producten en alleen de hoogste en een-na-hoogste rating uit de database?
Momenteel haal ik voor elk product ook alle ratings op, en filter ik later in een loop tot alleen de twee hoogste ratings. Dit is natuurlijk niet optimaal. Hoewel ik niet zoveel ratings verwacht dat dit echt problemen gaat leveren zou ik toch graag willen weten hoe het beter kan, mocht het ooit voorkomen op een tabel die wel enorm veel records heeft.
Huidige code die de objecten uit de database haalt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public async Task<List<Product>> GetProducts(int type) { // Get products with all ratings var products = await Database.Products .Include(p => p.Ratings).ThenInclude(r => r.User) .Where(p => p.Type == type) .ToListAsync(); // Take only relevant ratings and store in Product object foreach (var product in products) { var orderedRatings = product.Ratings.OrderByDescending(r => r.Rating); product.HighestRating = orderedRatings.FirstOrDefault(); product.SecondHighestRating = orderedRatings.Skip(1).FirstOrDefault(); } } |
Een alternatief zou zijn om eerst de producten op te halen, en dan per product een nieuwe query te doen om de twee hoogste ratings op te halen. Afhankelijk van hoeveel producten en ratings er verwacht worden kan ik me voorstellen dat dit efficienter is (bijv: heel weinig producten met enorm veel ratings).
Mijn gevoel zegt echter dat dit in één query moet kunnen, in ieder geval in SQL (of het ook vertaald kan worden naar een LINQ query is nog de vraag).
Weet iemand hoe dit beter kan?
Probleem 2: hoe hou ik de database en de app logica gescheiden?
Momenteel heb ik de twee properties HighestRating en SecondHighestRating in de Product class gestopt, met een NotMapped zodat het database model geen rare dingen gaat doen. Ik vind dit niet zo netjes, aangezien de HighestRating alleen nuttig is voor de applicatie zelf en weinig met de database te maken heeft.
Een alternatief zou zijn om een apart model (viewmodel) te maken met Product en de ratings apart:
C#:
1
2
3
4
5
6
| public class ProductViewModel { public Product Product {get;set;} public ProductRating HighestRating {get;set;} public ProductRating SecondHighestRating {get;set;} } |
Dit viewmodel zou ik dan gebruiken om de koppeling te maken tussen Product en de twee ratings die alleen voor m'n applicatie relevant zijn. Echter, wil ik alle informatie in een query halen (zie probleem 1) dan zal de repository / data laag nog steeds toegang moeten hebben tot deze viewmodel. Opnieuw ben ik dus een koppeling aan het maken tussen database en applicatie logic.
Ook dit is geen enorm probleem maar het is volgens mij niet netjes, en het is een fundamenteel "probleem" waar ik vaker tegenaan loop. Ik denk dat er een standaard oplossing is maar ik zie hem niet.