Avatar billede Droa Seniormester
11. marts 2018 - 12:47 Der er 6 kommentarer

Entitiy Framework kun hent første række fra en 1 til mange relation

Hej Eksperter

Jeg bruger Entity Framework til og køre min side.

Jeg har et artikel system, som deles op i artikel fragmenter, som henter sit inhold fra en artikelfragmentversion.

relationerne ser omkring sådan her ud.

Artikel.Id = 1
ArtikelFragment.Id = 1
ArtikelFragment.ArtikelId = Mange
ArtikelFragmentVersion.Id = 1
ArtikelFragmentVersion.ArtikelFragmentId = Mange

Artikel Indeholder data der tracker selve artiklen, men ikke rigtigt noget Text, da det først bliver styret af ArtikelFragmentVersion

ArtikelFragment er en form for statisk holdeplads for et specifikt ArtikelFragmentVersion, som der kan være utroligt mange af.

f.eks kan ArtikelFragmentVersion have et andet sprog, men kan også være en editeret og bedre version end den forrige på samme sprog.

min bekymring er at når jeg siger at EF skal loade denne struktur, at EF så vil laode alle Versioner af min Artikel, og dermed bruge mere CPU tid end nødvendigt.

hvordan vil EF behandle denne sage, hvis jeg f.eks gøre følgende.


        public void OnGet(int id)
        {
            Article article = Context.Articles.Find(id);

            foreach (ArticleFragment af in article.ArticleFragments)
            {
                ArticleFragmentVersion afv = af.FragmentVersions.Where(v => v.Language == Language).OrderBy(o => o.Score).FirstOrDefault();
                // Brug ArticleFragmentVersion her
            }
        }


min DbContext er følgende


  public class ArticleDbContext : DbContext
    {
        public DbSet<Article> Articles { get; set; }

        public ArticleDbContext(DbContextOptions<ArticleDbContext> options) : base(options)
        {

        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }
    }


er der en måde og optimere dette på, så den ikke skal kigge alle DB rækker igennem for dette?
Avatar billede Syska Mester
11. marts 2018 - 14:24 #1
Først, så har du i hvert fald N + 1 problemet i forhold til performance: https://stackoverflow.com/questions/97197/what-is-n1-select-query-issue

Har du eager loaded dine ArticleFragments? Eller får du igen N + 1 problemet.

Hvad version af EF? Der er ved at være utroligt mange versioner.

Jeg vil sige ... så installeret "SQL profiler": https://docs.microsoft.com/en-us/sql/tools/sql-server-profiler/sql-server-profiler

En ORM er black magic ... ja, den kan hjælpe dig, men den kan også lave utroligt dumt og langsomt SQL ... så ... på med profiler ...
Avatar billede Droa Seniormester
11. marts 2018 - 14:33 #2
Jeg køre med EF Core, som jeg kan se ikke har Lazy Loading desværre :(
så jeg har prøvet lidt med noget Include, men jeg fårstår ikke rigtigt hvordan man includere noget der ikke er direkte forbundet til DbSet, som f.eks ArticleFragmentVersion, som kun er forbundet igennem ArticleFragment
Avatar billede Syska Mester
11. marts 2018 - 15:44 #3
Hvis du ikke har Lazy, så vil overstående jo slet ikke virke.

Lazy er med i 2.1 ... eller ... det er planen. Du kan læse mere her: https://docs.microsoft.com/en-us/ef/core/querying/related-data

Men ... hvor meget bekymre du dig om performance? Er det noget du tænker kan blive et problem eller måler du dig frem til det?

Du kan bruge din dbcontext igen og hente hvad du mangler ...
Avatar billede Droa Seniormester
11. marts 2018 - 16:22 #4
jeg regner med ca 5.000-6.000 artikel loading pr. minut.

Hver artikel har i gennemsnit 7 fragmenter.
Hver fragment har i gennemsnit 48 versioner.

så det vil betyder der vil være 1.680.000 til 2.016.000 læsninger, i minuttet hvis den skal læse alle versioner (selv dem den ikke bruger)

jeg ved så ikke om det er bedre og gøre det manuelt, så alle Typer har deres egen DbSet, også linke dem sammen uden om EF.
Avatar billede Syska Mester
11. marts 2018 - 16:43 #5
For lige at slå over i noget jeg i hvert fald kan forholde mig til.

Forum.

Threads og Posts.

Hvis man vil have en post, så gør man jo sådan her.

context.FirstOrDefault(x => x.PostId == 10).Include(x => x.Thread)

Da der er en navigation property ... ergo 1 hit til databasen

Det andet ... fra Thread til Post ... jamen, det er bare ikke noget der gøres i Linq.

Men ... hvordan ved du hvad for en version der skal hentes? Hvad er "Score" beregnet ud fra?

Man kan tænke dit design sådan her:

Article har paragrapgs ... de paragraphs har så versions ... en paragraph har altid kun en aktiv version.

I så fald, så ville jeg skriver "ActiveVersionId" ned på Paragraph og lave det som en navigation property over til Version.

Så kan du jo include alt i et hug fra databasen.
Avatar billede Droa Seniormester
11. marts 2018 - 18:01 #6
Så altså jeg laver min relation om til et 1 til 1, men også beholder min 1 til mange relation?

En Score er er kun en protype variabel, da jeg forsøger og få det til og virke, men det skal senere udregnes fra en masse fælleskabs data :)

Så hvis jeg f.eks laver alle mine Type om så de ikke har en direkte Type i dem, men istedet et foreign id, også bruger laver en Methode i min DbContext Type. f.eks


undskyld den underlige kode, men blev skrevet hurtigt i notepad, så har ikke valideret den endnu.. men skulle bare lige se om dette var vejen frem?

Jeg regner med og lave en GroupBy Language, så jeg får mulighed for at give i alternativt sprog, hvis ens eget sprog ikke findes.

der er vist noget med at EF Core bruger Queryable, som laver Linq om til SQL sætninger, så den ikke laver alt for meget, men jeg ved ikke om jeg er korrekt der.


class ArticleDbContext : DbContext{
public DbSet<Article> Articles {get;set;}
public DbSet<ArticleFragment> ArticleFragments {get;set;}
public DbSet<ArticleFragmentVersion> ArticleFragmentVersions {get;set;}
GetArticleFragmentsFor(Article article){
return ArticleFragments.Where(fragment => fragment.ArticleId == article.Id);
}
GetArticleFragmentVersionsFor(ArticleFragment fragment){
return ArticleFragmentVersions.Where(version => version.FragmentId == fragment.Id).OrderByDescending(version => version.Score).FirstOrDefault();
}
}
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester