2016-10-31 4 views
4

Мой EF-запрос занимает около 3 секунд, чтобы получить 10 игроков, потому что он извлекает все 500k + строки другой таблицы, а не те, которые мне нужны.Entity Framework Отношения во многих отношениях

Это PlayerEntity:

namespace RocketLeagueStats.Database.Entities 
{ 
    [Table("players", Schema = "public")] 
    public class PlayerEntity 
    { 

     [Key] 
     [Column("id")] 
     public int Id { get; set; } 

     [Column("unique_id")] 
     public string UniqueId { get; set; } 

     [Column("display_name")] 
     public string DiplayName { get; set; } 

     [Column("platform_id")] 
     [JsonIgnore] 
     public int PlatformId { get; set; } 

     [ForeignKey("PlatformId")] 
     public PlatformEntity Platform { get; set; } 

     [Column("avatar")] 
     public string Avatar { get; set; } 

     public PlayerStatsEntity Stats { get; set; } 

     public List<PlayerRankedEntity> Ranks { get; set; } 

     [Column("last_requested")] 
     public DateTime LastRequested { get; set; } 

     [Column("created_at")] 
     public DateTime CreatedAt { get; set; } 

     [Column("updated_at")] 
     public DateTime UpdatedAt { get; set; } 

    } 
} 

Это PlayerRankedEntity:

namespace RocketLeagueStats.Database.Entities 
{ 
    [Table("player_ranked", Schema = "public")] 
    public class PlayerRankedEntity 
    { 

     [ForeignKey("Player")] 
     [Column("player_id")] 
     [JsonIgnore] 
     public int PlayerId { get; set; } 

     [Column("season_id")] 
     [JsonIgnore] 
     public int SeasonId { get; set; } 

     [Column("playlist_id")] 
     [JsonIgnore] 
     public int PlaylistId { get; set; } 

     [Column("matches_played")] 
     public int MatchesPlayed { get; set; } 

     [Column("rank_points")] 
     public int RankPoints { get; set; } 

     [Column("tier")] 
     public int Tier { get; set; } 

     [Column("division")] 
     public int Division { get; set; } 

     public PlayerEntity Player { get; set; } 

    } 
} 

Это PlayerStatsEntity:

namespace RocketLeagueStats.Database.Entities 
{ 
    [Table("player_stats", Schema = "public")] 
    public class PlayerStatsEntity 
    { 

     [Key, ForeignKey("Player")] 
     [Column("player_id")] 
     [JsonIgnore] 
     public int PlayerId { get; set; } 

     [Column("wins")] 
     public int Wins { get; set; } 

     [Column("goals")] 
     public int Goals { get; set; } 

     [Column("mvps")] 
     public int Mvps { get; set; } 

     [Column("saves")] 
     public int Saves { get; set; } 

     [Column("shots")] 
     public int Shots { get; set; } 

     [Column("assists")] 
     public int Assists { get; set; } 

     public PlayerEntity Player { get; set; } 

    } 
} 

Это мой метод DatabaseContext.OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<PlayerRankedEntity>() 
     .HasKey(k => new { k.PlayerId, k.SeasonId, k.PlaylistId }); 
} 

Это код, который занимает более 3 секунд, чтобы завершить (Если я удалю «.INCLUDE (х => x.Ranks)» это просто занимает несколько мс):

using (var database = new DatabaseContext()) 
{ 
    var serviceProvider = database.GetInfrastructure(); 
    var loggerFactory = serviceProvider.GetService<ILoggerFactory>(); 
    loggerFactory.AddNLog(); 

    var stopwatch = Stopwatch.StartNew(); 

    var players = database.Players 
     .Include(x => x.Ranks) 
     .Take(10) 
     .ToArray(); 

    Console.WriteLine($"Took {stopwatch.ElapsedMilliseconds}ms to fetch {players.Length} players"); 
} 

Это выход Обратите внимание, что он генерирует два запроса вместо одного:

2016-10-31 22:50:18.6416 INFO Executed DbCommand (8ms) [Parameters=[@__p_0='?'], CommandType='Text', CommandTimeout='30'] 
SELECT "x"."id", "x"."avatar", "x"."created_at", "x"."display_name", "x"."last_requested", "x"."platform_id", "x"."unique_id", "x"."updated_at" 
FROM "public"."players" AS "x" 
ORDER BY "x"."id" 
LIMIT @__p_0 
2016-10-31 22:50:18.7128 INFO Executed DbCommand (0ms) [Parameters=[@__p_0='?'], CommandType='Text', CommandTimeout='30'] 
SELECT "p"."player_id", "p"."season_id", "p"."playlist_id", "p"."division", "p"."matches_played", "p"."rank_points", "p"."tier" 
FROM "public"."player_ranked" AS "p" 
WHERE EXISTS (
    SELECT 1 
    FROM "public"."players" AS "x" 
    WHERE "p"."player_id" = "x"."id" 
    LIMIT @__p_0) 
ORDER BY "p"."player_id" 
Took 3991ms to fetch 10 players 

Я думаю, что я испортил на отношения где-то, в результате чего она, чтобы выбрать все строки. Но я не знаю, что я испортил.

Как это исправить, и есть ли какие-либо другие проблемы с моими атрибутами?

Я использую Microsoft.EntityFrameworkCore v1.0.1.

Edit: Если я использую .OrderBy(x => x.CreatedAt) или .Where(x => x.DiplayName.Contains("mike")) в запросе, он идет гораздо быстрее.

Сформирован запрос:

2016-11-01 00:14:15.9638 INFO Executed DbCommand (24ms) [Parameters=[@__p_0='?'], CommandType='Text', CommandTimeout='30'] 
SELECT "x"."id", "x"."avatar", "x"."created_at", "x"."display_name", "x"."last_requested", "x"."platform_id", "x"."unique_id", "x"."updated_at" 
FROM "public"."players" AS "x" 
ORDER BY "x"."created_at", "x"."id" 
LIMIT @__p_0 
2016-11-01 00:14:16.0972 INFO Executed DbCommand (44ms) [Parameters=[@__p_0='?'], CommandType='Text', CommandTimeout='30'] 
SELECT "p"."player_id", "p"."season_id", "p"."playlist_id", "p"."division", "p"."matches_played", "p"."rank_points", "p"."tier" 
FROM "public"."player_ranked" AS "p" 
INNER JOIN (
    SELECT DISTINCT "x"."created_at", "x"."id" 
    FROM "public"."players" AS "x" 
    ORDER BY "x"."created_at", "x"."id" 
    LIMIT @__p_0 
) AS "x0" ON "p"."player_id" = "x0"."id" 
ORDER BY "x0"."created_at", "x0"."id" 
Took 314ms to fetch 10 players 
+0

Добро пожаловать в EF Core hell :(Хотя кажется, что загружает только 10 игроков (оба предложения LIMIT)? –

+0

:) Попробуйте отключить режим отслеживания и проверьте, не помогает ли он – CodeNotFound

+0

Фактически второй запрос выглядит неправильно - '' EXISTS' с 'LIMIT', что ?! –

ответ

1

EF Ядро в настоящее время это кошмар.

Вы можете попробовать следующее обходное решение (но если вы спросите меня, лучше переключитесь на EF6).

Вместо того, чтобы:

var players = database.Players 
    .Include(x => x.Ranks) 
    .Take(10) 
    .ToArray(); 

использования:

var players = database.Players 
    .Take(10) 
    .ToArray(); 
var playerIds = players.Select(p => p.Id); 
database.PlayerRanks.Where(r => playerIds.Contains(r.PlayerId)).Load(); 

, который должен производить тот же эффект, как Include.

+0

Это сократило время от ~ 3000 мс до ~ 120 мс, что довольно хорошо. Я не могу пойти в EF6, потому что мне нужно использовать netstandard 1.6. Разве это не удалит возможность использовать ГДЕ в рядах? – AeonLucid

+0

Привет @ Иван Стоев сделать основную команду EF об этих проблемах? – Sampath

+0

@Sampath Привет, конечно. Просто заходите в их GitHub, они полны вопросов :) –

Смежные вопросы