Cara yang benar untuk menggunakan properti tingkat lanjut di MVC EF

Katakanlah saya memiliki model sepak bola Team. Ia memainkan Matches dalam grup dengan tim lain. Sekarang saya ingin memilih 2 tim teratas dari daftar. Skor dihitung seperti biasa: 3 menang, 1 seri, 0 kalah.

Model untuk Match terlihat seperti ini:

[Key]
public int MatchId{get;set;}
public int HomeTeamId { get; set; }
public int AwayTeamId { get; set; }
[ForeignKey("HomeTeamId")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("AwayTeamId")]
public virtual Team AwayTeam { get; set; }
public int? HomeTeamScored { get; set; }
public int? AwayTeamScored { get; set; }

Saya telah menguji 5 solusi ini:

1) memiliki tampilan alih-alih tabel untuk mengambil kolom skor, tetapi ini mempersulit bagian pemrograman karena saya harus memberitahu EF untuk menggunakan tabel untuk penyisipan tetapi tampilan untuk menampilkan data

2) Biarkan kolom Score tidak dipetakan, lalu ambil semua tim, hitung skornya seperti ini:

var list = db.Teams.ToList();

foreach(var team in list)
{
    team.Score = db.Matches.Where(...).Sum();
}

lalu pesan saja daftarnya paling lambat Score dan ambil 2 yang pertama.

3) Cara lainnya adalah dengan memiliki

var list = db.Teams.OrderByDesc(t => db.Matches.Where(...).Sum()).Take(2).ToList();

Saya harus melakukan banyak pengecekan untuk null, juga memeriksa tim mana yang menang atau seri, apakah tim yang saya cari bermain sebagai kandang atau tandang, dll.

4) Pilihan lainnya adalah menghitung ulang Score untuk tim setiap kali saya menambahkan/mengedit pertandingan, namun menurut saya ini adalah pendekatan yang sangat tidak profesional.

Seperti yang saya katakan, masing-masing metode ini adalah solusi yang akan mengarahkan saya untuk menyelesaikan tugas, tapi... Saya memiliki indra keenam bahwa saya kehilangan sesuatu yang sangat jelas tentang bagaimana saya dapat menyelesaikannya dengan sedikit usaha. Adakah yang bisa menyarankan apa yang saya lewatkan?

P.S. Jika itu memengaruhi jawabannya, anggap saja saya menggunakan semuanya versi terbaru.


person Andrius Naruševičius    schedule 08.06.2014    source sumber
comment
Tunjukkan sesuatu yang telah Anda terapkan, dan jelaskan mengapa hal tersebut tidak memuaskan Anda, atau bagaimana hal tersebut dapat ditingkatkan. Tapi tolong, bantulah Anda, jangan meminta orang lain melakukan pekerjaan Anda dan bereksperimen sendiri. Jika tidak, Anda tidak akan pernah belajar.   -  person JotaBe    schedule 09.06.2014
comment
@JotaBe Cukup yakin Anda melewatkan membaca pertanyaan (a.k.a. tldr; mari kita mengoceh tentang kualitas pertanyaan karena saya tidak mau membacanya). Saya sudah menerapkan setiap metode yang saya bicarakan (belum lagi satu-satunya hal yang hilang adalah rumus yang bahkan tidak relevan dengan ini, apakah itu yang Anda minta?). Tolong tunjukkan saya bagian saya meminta seseorang untuk melakukan pekerjaan SAYA. Tunjukkan pada saya pertanyaan lain yang sama bagusnya di SO yang menunjukkan lima solusi untuk masalah dalam pertanyaan itu sendiri dan hanya menanyakan apakah ada cara yang lebih baik.   -  person Andrius Naruševičius    schedule 09.06.2014
comment
Meskipun waktu saya cukup singkat, saya telah mengedit jawaban Anda dalam format yang memungkinkan untuk membaca, memahami, menjawabnya dengan lebih cepat (tetapi menunjukkan kode Anda akan membuatnya lebih cepat. Mengapa tidak menunjukkannya jika Anda menerapkannya dia?). Dan saya bahkan akan memposting jawaban, di mana Anda akan melihat mengapa pertanyaan Anda tidak begitu bagus, bahkan setelah menyusun ulang: ada kode yang hilang dalam pertanyaan Anda, dan hanya ada empat solusi, sejauh yang saya bisa lihat setelahnya pengeditan (jika edisi disetujui).   -  person JotaBe    schedule 09.06.2014


Jawaban (2)


Ketika redundansi data muncul, sering kali normalisasi adalah solusinya. Saya pikir dalam kasus Anda, Anda memerlukan keduanya, normalisasi dan sedikit redundansi.

Properti yang diulang di Match adalah "bau". Mereka sepertinya menyerukan normalisasi. Jika dikaji lebih dekat, ternyata hal ini tidak berlaku untuk semuanya. Sebuah pertandingan selalu terdiri dari dua tim. Jadi kedua TeamIds OK (dan referensi yang menyertainya). Namun Anda dapat menyimpan skor secara berbeda.

Lihatlah model yang mungkin ini:

class Team
{
    public int TeamId { get; set; }
    // ...
    public ICollection<MatchTeam> MatchTeams { get; set; }
}

class Match
{
    public int MatchId { get; set; }
    public int HomeTeamId { get; set; }
    public int AwayTeamId { get; set; }
    public virtual Team HomeTeam { get; set; }
    public virtual Team AwayTeam { get; set; }
}

class MatchTeam
{
    public int MatchId { get; set; }
    public int TeamId { get; set; }
    public int Scored { get; set; } // Number of goals/points whatever
    public int RankingScore { get; set; } // 0, 1, or 3
}

MatchTeam merupakan entitas yang menyimpan pencapaian 1 tim dalam 1 pertandingan. Properti Scored merupakan hasil normalisasi dari HomeTeamScored dan AwayTeamScored. Keuntungannya adalah: properti tidak dapat dibatalkan: entri MatchTeam dibuat jika hasilnya adalah fakta.

Redundansi ada di properti RankingScore. Hal ini harus ditentukan ketika pertandingan dimasukkan atau diubah dan itu bergantung pada (dan harus konsisten dengan) skornya. Seperti biasa dengan redundansi, ada bahaya ketidakkonsistenan data. Tapi apakah itu bahaya besar? Jika hanya ada satu metode layanan dimana MatchTeam data dimasukkan atau diubah, bahayanya cukup terbatas.

Keuntungannya adalah sekarang pengumpulan skor total untuk masing-masing tim saat runtime dapat dilakukan:

var topTeams = context.Teams
              .OrderByDescending(t => t.MatchTeams.Sum(mt => mt.RankingScore))
              .Take(2);
person Gert Arnold    schedule 08.06.2014
comment
Faktanya, ini sepertinya cara yang paling tepat untuk melakukan hal ini. Terima kasih banyak atas upaya yang dilakukan untuk menjawab :) - person Andrius Naruševičius; 09.06.2014

1) Saya tidak mengerti mengapa penerapan tampilan mempersulit pemrograman Anda. Itu solusi yang bagus. Memasukkan hasil pertandingan dan mendapatkan tim teratas adalah dua operasi yang sepenuhnya independen. Tolong, izinkan saya melihat beberapa kode untuk memahami mengapa Anda memiliki hubungan yang kuat di antara keduanya, seperti hal-hal independen seperti skor pertandingan dan skor total sebuah tim.

2) Ini adalah opsi yang buruk: Anda perlu membuat kueri untuk semua tim, dan kueri tambahan untuk setiap tim. Performa buruk!

3) Sebaiknya lihat kode Anda untuk menunjukkan bagaimana Anda dapat meningkatkan kueri Anda. Misalnya membuat pengecekan nol tidaklah seburuk itu. Sesederhana menggunakan operator ??, yaitu mt => mt.HomeTeamSocred ?? 0 akan mengkonversi null menjadi 0 dengan cukup mudah. Jika Anda menunjukkan ekspresi yang digunakan, akan mungkin untuk melihat apakah ekspresi tersebut dapat ditingkatkan dan disederhanakan. Namun, saya dapat mengusulkan yang ini, yang tidak terlalu rumit:

ctx.Match.Select(m => new
{ // Score for HomeTeam
    TeamScore = (m.HomeTeamScored ?? 0) > (m.AwayTeamScored ?? 0)
        ? 3 : (m.HomeTeamScored ?? 0) < (m.AwayTeamScored ?? 0)
        ? 0 : 1,
    TeamId = m.HomeTeamId,
})
.Concat(
    ctx.Match.Select(m => new
    { // Score for away Team
        TeamScore = (m.HomeTeamScored ?? 0) > (m.AwayTeamScored ?? 0)
            ? 0 : (m.HomeTeamScored ?? 0) < (m.AwayTeamScored ?? 0)
            ? 3 : 1,
        TeamId = m.AwayTeamId,
    })
).GroupBy(mr => mr.TeamId) // Group match scores by TeamId's
.Select(mrs=> new
{
    TeamId = mrs.Key,
    TotalScore = mrs.Sum(m => m.TeamScore)
})
.OrderByDescending(ts => ts.TotalScore)
.Take(2);

Namun ada sesuatu yang saya tidak mengerti. Mengapa Home/AwayTeamScored bisa menjadi nol? Karena sebelum pertandingan dimulai, Score harus nol untuk kedua tim. Nol itu tidak masuk akal. Ini akan menghindari masalah dalam memeriksa null.

4) Apa arti opsi ini?

person JotaBe    schedule 09.06.2014