Moet een repository IEnumerable <T>, IQueryable <T> of List <T> retourneren?

Ik zou mijn applicatie zo flexibel mogelijk willen maken, maar mezelf niet in een gat graven door mijn interface te specifiek te maken.

Wat is het beste objecttype voor een repository? IEnumerable, IQueryable of List?

De technologieën die ik overweeg om te gebruiken zijn

  • Azure App Fabric Caching

  • Entiteitskader 4.1

  • Mogelijk Windows Server AppFabric

13

4 antwoord

Het hangt ervan af of u toekomstige vragen op de entiteit wilt laten uitvoeren en of deze in het geheugen moeten staan ​​of niet:

  • Als er toekomstige query's moeten zijn en de DB het werk moet doen, moet IQueryable worden geretourneerd.
  • Als er toekomstige query's moeten zijn en dit moet worden gedaan in het geheugen, retourneer IEnumerable.
  • Als er geen verdere vragen meer zijn en alle gegevens moeten worden gelezen, retourneert u een IList, ICollection enz.
8
toegevoegd
Waar kan ik vragen stellen op basis van dit verleden van de DAL? Moet ik IQueryable doorgeven aan de controller? Het uitzicht?
toegevoegd de auteur CHI Coder 007, de bron
Dit soort vragen heeft ofwel betrekking op het afstemmen van prestaties, in welk geval de prestaties moeten worden gemeten met alle overwogen opties (en de meest performante die worden gekozen) of met architecturale overwegingen. In het laatste geval zou ik willen voorstellen om een ​​IQueryable aan de controller bloot te stellen (aangezien controller-kennis van domeinobjecten in orde is), maar het view/view-model moet dom zijn en geen kennis hebben van dergelijke dingen ... Maar dit is alleen mijn mening!
toegevoegd de auteur Rich O'Kelly, de bron
U kunt nog steeds zorgen dat query's in de DB worden uitgevoerd als u IEnumerable gebruikt. De documentatie voor IQueryable zegt dat deze is bedoeld voor het implementeren van query-providers, niet om dit onderscheid te maken. Ik gebruik IEnumerable liever niet als het doorzoeken van de database is voltooid, omdat het gebruik van een collectietype ervoor zorgt dat u de gematerialiseerde resultaten retourneert en niet de ongeëvalueerde query per vergissing.
toegevoegd de auteur millimoose, de bron
@ makerofthings7: in een strikte gelaagde architectuur is het niet OK om DB-ondersteunde query's ergens buiten de DAL uit te voeren. Het is prima om query's in het geheugen uit te voeren om gegevensstructuren overal te transformeren en je kunt het toch niet voorkomen, omdat alle collecties in het geheugen kunnen worden opgevraagd.
toegevoegd de auteur millimoose, de bron
Ik denk niet dat IQueryable ooit moet worden geretourneerd en ik heb een antwoord geplaatst dat behandelt waarom.
toegevoegd de auteur Neo, de bron

Ik zou zeggen: bouw je DAL met behulp van IQueryable, en geef het door, zorg ervoor dat je object het leven in de gaten houdt, is het verzoek. Op deze manier profiteert u van vertraagde uitvoering, maar loopt u het risico dat de database inefficiënt wordt bevraagd.

Zorg er vervolgens voor dat u de prestaties test van uw toepassing (of ten minste de onderdelen die waarschijnlijk verkeer zullen genereren) en bekijk de gegevensaccessiepatronen. Maak gespecialiseerde methoden in uw DAL om volledig uitgewerkte onjects te achterhalen en deze query's te maken als vooraf gecompileerde query's.

een voorbeeld van de repository-interface zou eruit zien

  public interface IDataContext
  {
        void Add(T entity) where T : BaseEntity;
        void Delete(T entity) where T : BaseEntity;
        IQueryable Find(Expression> where) where T : BaseEntity;
   }

waar BaseEntity de basisklasse is voor al onze klassen, ziet het er naar uit dat deze klasse niet is toegewezen aan een tabel in db

public abstract class BaseEntity
{
        public int Id { get; set; }
        public DateTime CreateDateTime { get; set; }
        public string CreateUser { get; set; }
        public DateTime ModDateTime { get; set; }
        public string ModUser { get; set; }
        public byte[] RowVersion { get; set; }
}

Expression> would pass the whole expression to your repository instead of just Func, since EF works on expression to generate sql query, a typical use would be

ICollection wgGroups = this.dataContext.Find((w) => true).ToList();

waar WFGroup een klasse is die is afgeleid van BaseEntity, gebruik ik gewoonlijk lui laden en proxy en ontkoppel/koppel objecten niet aan een context.

5
toegevoegd
Ik gebruik een caching-service waarvoor een gematerialiseerde query vereist is. IEnumerable is de beste gemene deler die ik kan vinden. Betekent dit dat ik een laag bovenop mijn IQueryable DAL heb die alles plat maakt naar IEUNmerable ? Zo ja, wat is ? Entiteitsmodel, weergave of viewmodel?
toegevoegd de auteur CHI Coder 007, de bron
Interessant, ik zie hoe het kan werken voor een 1: 1-tabel voor het toewijzen van entiteiten, maar ik ben benieuwd hoe je omgaat met meer complexe dingen zoals 1: veel relaties ... stackoverflow.com/q/8095728/328397
toegevoegd de auteur CHI Coder 007, de bron
Bedankt, dat lijkt op de richting die ik zou moeten inslaan. Ik heb alleen gelezen over Expression > maar weet niet goed hoe ik het moet gebruiken. Kun je nog een sample toevoegen? Ik begrijp ook dat BaseEntity iets als studenten of EnrolledClasses kan zijn?
toegevoegd de auteur CHI Coder 007, de bron
IEnumerable is geen garantie voor gematerialiseerd object IQueryable is ook IEnumerable. T in mijn geval is Entity, we hebben een interface IDataContext die door de repository wordt geïmplementeerd en elke methode is generiek T waarbij T is beperkt tot onze basisentiteit.
toegevoegd de auteur np-hard, de bron
ik heb het antwoord bijgewerkt met een voorbeeldinterface, 1: n wordt afgehandeld in het entiteitsmodel. Dit was een beetje moeilijk te implementeren in EF 4 met een aangepaste t4, maar met EF 4.2 is het een koud kunstje
toegevoegd de auteur np-hard, de bron
je hebt gelijk, ik heb het antwoord bijgewerkt met meer codevoorbeeld
toegevoegd de auteur np-hard, de bron
Ik denk dat dit antwoord nu achterhaald is en een verkeerd advies is. IQueryable moet niet slechts om een ​​aantal redenen worden doorgegeven, een dat het gevaarlijk is in termen van mogelijk DB-misbruik, maar meer te maken heeft met SoC. Ik heb een antwoord geplaatst waarin wordt uitgelegd wat ik bedoel, met verwijzing naar een recent artikel over de kwestie.
toegevoegd de auteur Neo, de bron

Hoe waarschijnlijk is het dat u ooit een aangepaste implementatie van IEnumerable (niet een verzameling) van uw DAL moet retourneren? (Om deze vraag te beantwoorden, bekijk je eerdere projecten en tel je hoeveel daarvan of van rendementterugkeer s je hebt.)

Als het antwoord "niet erg" is, zou ik alleen ICollection of zelfs arrays teruggeven (als u wilt voorkomen dat de queryresultaten per ongeluk worden gewijzigd.) In een mum van tijd, als u ooit moet veranderen een zoekopdracht om resultaten te "streamen" met een aangepaste IEnumerable , u kunt altijd de oude methode laten gebruiken om de nieuwe aan te roepen en de resultaten materialiseren om de compatibiliteit met oudere clients te behouden.

4
toegevoegd

Er is een zeer goed artikel hier dat dekt dit, namelijk onder de kop "Opslagplaatsen die IQueryable teruggeven". Dit is wat het zegt:

One of the reasons we use the repository pattern is to encapsulate fat queries. These queries make it hard to read, understand and test actions in ASP.NET MVC controllers. Also, as your application grows, the chances of you repeating a fat query in multiple places increases. With the repository pattern, we encapsulate these queries inside repository classes. The result is slimmer, cleaner, more maintainable and easier-to-test actions. Consider this example:

var orders = context.Orders
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Here we are directly using a DbContext without the repository pattern. When your repository methods return IQueryable, someone else is going to get that IQueryable and compose a query on top of it. Here’s the result:

var orders = repository.GetOrders()
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

Can you see the difference between these two code snippets? The only difference is in the first line. In the first example, we use context.Orders, in the second we use repository.GetOrders(). So, what problem is this repository solving? Nothing!

Your repositories should return domain objects. So, the GetOrders() method should return an IEnumerable. With this, the second example can be re-written as:

var orders = repository.GetOrders(1234);

See the difference?

Als gevolg hiervan heb ik de volgende coderingsconventie toegevoegd aan mijn team:

Retourneer nooit een IQueryable -object voor methoden in de repository-klasse. Altijd eerst het nummer opsommen of converteren (bijvoorbeeld ToArray , ToList , AsEnumerable ).

De redenering is dat IQueryable de beller toelaat om hierop voort te bouwen en uiteindelijk de SQL-query die in de database wordt uitgevoerd, te wijzigen. Dit kan potentieel gevaarlijk zijn in termen van DB-prestaties, maar het gaat meer om SoC. De beller geeft niet om de gegevensbron; het wil alleen de gegevens.

1
toegevoegd