วิธีที่ถูกต้องในการใช้คุณสมบัติขั้นสูงใน MVC EF

เอาเป็นว่าผมมีโมเดลฟุตบอล Team มันเล่น Matches ในกลุ่มกับทีมอื่น ตอนนี้ผมขอคัดทีม 2 อันดับแรกจากรายการ คะแนนจะถูกนับตามปกติ: ชนะ 3, เสมอ 1, แพ้ 0

โมเดลสำหรับ Match มีลักษณะดังนี้:

[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; }

ฉันได้ทดสอบวิธีแก้ปัญหา 5 ข้อนี้แล้ว:

1) มีมุมมองแทนตารางเพื่อใช้คอลัมน์คะแนน แต่มันทำให้ส่วนการเขียนโปรแกรมซับซ้อนขึ้น เนื่องจากฉันจะต้องบอก EF ให้ใช้ตารางในการแทรก แต่มุมมองสำหรับการแสดงข้อมูล

2) ให้คอลัมน์ Score ไม่ถูกแมป จากนั้นนำทุกทีม นับคะแนนดังนี้:

var list = db.Teams.ToList();

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

จากนั้นเพียงเรียงลำดับรายการภายใน Score และเอา 2 ตัวแรก

3) อีกวิธีหนึ่งคือการมี

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

ผมจะต้องทำการตรวจสอบหาค่าว่างมากมาย รวมถึงเช็คด้วยว่าทีมไหนชนะหรือเสมอ ไม่ว่าทีมที่ผมมองหาจะเล่นเป็นเหย้าหรือเยือน เป็นต้น

4) อีกทางเลือกหนึ่งคือการนับ Score ของทีมทุกครั้งที่ฉันเพิ่ม/แก้ไขการแข่งขัน แต่ฉันรู้สึกว่านี่เป็นแนวทางที่ไม่เป็นมืออาชีพมาก

ดังที่ฉันได้กล่าวไว้ แต่ละวิธีการเหล่านี้เป็นวิธีแก้ปัญหาที่จะนำฉันไปสู่การแก้ปัญหา แต่... ฉันรู้สึกได้ถึงความรู้สึกที่หกว่าฉันพลาดบางสิ่งบางอย่างที่ชัดเจนโดยสิ้นเชิงว่าฉันจะทำสิ่งนี้โดยใช้ความพยายามน้อยที่สุดได้อย่างไร ใครสามารถแนะนำสิ่งที่ฉันหายไป?

ป.ล. ถ้ามันส่งผลต่อคำตอบ สมมติว่าฉันใช้ทุกอย่างเป็นเวอร์ชันล่าสุด


person Andrius Naruševičius    schedule 08.06.2014    source แหล่งที่มา
comment
แสดงสิ่งที่คุณได้นำไปใช้ และอธิบายว่าเหตุใดจึงไม่ทำให้คุณพึงพอใจ หรือจะปรับปรุงได้อย่างไร แต่ขอร้องเถอะ คุณช่วยได้ไหม อย่าขอให้คนอื่นมาทำงานของคุณและทดลองด้วยตัวเอง ถ้าไม่เช่นนั้น คุณจะไม่มีวันได้เรียนรู้   -  person JotaBe    schedule 09.06.2014
comment
@JotaBe ค่อนข้างแน่ใจว่าคุณได้ข้ามการอ่านคำถาม (a.k.a. tldr; มาคุยโวเกี่ยวกับคุณภาพของคำถามเพราะฉันไม่สนใจที่จะอ่านมัน) ฉันได้ปรับใช้แต่ละวิธีที่ฉันได้พูดถึงไปแล้ว (ไม่ต้องพูดถึงสิ่งเดียวที่ขาดหายไปคือสูตรที่ไม่เกี่ยวข้องกับสิ่งนี้ด้วยซ้ำ นั่นคือสิ่งที่คุณขอใช่ไหม) โปรดแสดงส่วนที่ฉันกำลังขอให้ใครสักคนมาทำงานของฉัน แสดงคำถามที่ดีพอๆ กันอีกคำถามหนึ่งใน SO ซึ่งแสดงวิธีแก้ไขปัญหาห้าข้อในคำถามนั้น และเพียงถามว่ามีวิธีที่ดีกว่านี้หรือไม่   -  person Andrius Naruševičius    schedule 09.06.2014
comment
แม้ว่าฉันจะมีเวลาค่อนข้างน้อย แต่ฉันได้แก้ไขคำตอบของคุณในรูปแบบที่ช่วยให้อ่าน เข้าใจ และคำตอบได้รวดเร็วยิ่งขึ้น (แต่การแสดงโค้ดของคุณจะทำให้เร็วขึ้นอีก ทำไมไม่แสดงหากคุณนำไปใช้ มัน?). และฉันจะโพสต์คำตอบ ซึ่งคุณจะเห็นว่าทำไมคำถามของคุณถึงไม่ค่อยดีนัก แม้ว่าจะเรียงลำดับใหม่แล้วก็ตาม: มีโค้ดหายไปในคำถามของคุณ และมีเพียงสี่วิธีเท่านั้น เท่าที่ฉันเห็นหลังจากนั้น การแก้ไข (หากฉบับได้รับการอนุมัติ)   -  person JotaBe    schedule 09.06.2014


คำตอบ (2)


เมื่อความซ้ำซ้อนของข้อมูลปรากฏขึ้น การทำให้เป็นมาตรฐานมักจะเป็นวิธีแก้ปัญหา ฉันคิดว่าในกรณีของคุณ คุณต้องมีทั้งสองอย่าง การทำให้เป็นมาตรฐาน และความซ้ำซ้อนเล็กน้อย

คุณสมบัติที่เกิดซ้ำใน Match คือ "มีกลิ่นเหม็น" ดูเหมือนพวกเขาจะเรียกร้องให้มีการฟื้นฟู เมื่อพิจารณาให้ละเอียดยิ่งขึ้นเผยให้เห็นว่าสิ่งนี้ไม่เป็นความจริงสำหรับทุกคน การแข่งขันประกอบด้วยสองทีมเสมอ ดังนั้น TeamId ทั้งสองจึงใช้ได้ (และข้อมูลอ้างอิงที่แนบมาด้วย) แต่คุณสามารถเก็บคะแนนแตกต่างออกไปได้

ดูโมเดลที่เป็นไปได้นี้:

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 เป็นเอนทิตีที่เก็บความสำเร็จของ 1 ทีมใน 1 นัด คุณสมบัติ Scored เป็นผลลัพธ์ที่ทำให้เป็นมาตรฐานของ HomeTeamScored และ AwayTeamScored ข้อดีคือ: คุณสมบัติไม่เป็นค่าว่าง: รายการ MatchTeam จะถูกสร้างขึ้นเมื่อผลลัพธ์เป็นข้อเท็จจริง

ความซ้ำซ้อนอยู่ในคุณสมบัติ RankingScore สิ่งนี้จะต้องได้รับการพิจารณาเมื่อมีการเข้าหรือแก้ไขการแข่งขัน และขึ้นอยู่กับ (และควรสอดคล้องกับ) คะแนน เช่นเดียวกับที่เคยมีความซ้ำซ้อน อาจมีความเสี่ยงที่ข้อมูลไม่สอดคล้องกัน แต่มันเป็นอันตรายใหญ่หรือไม่? หากมีวิธีการบริการเพียงวิธีเดียวที่ป้อนหรือแก้ไขข้อมูล MatchTeam อันตรายจะถูกจำกัดอย่างเพียงพอ

ข้อดีคือตอนนี้สามารถรวบรวมคะแนนรวมของแต่ละทีมในขณะรันไทม์ได้แล้ว:

var topTeams = context.Teams
              .OrderByDescending(t => t.MatchTeams.Sum(mt => mt.RankingScore))
              .Take(2);
person Gert Arnold    schedule 08.06.2014
comment
จริงๆ แล้วนี่ดูเหมือนเป็นวิธีที่เหมาะสมที่สุดในการทำเช่นนี้ ขอบคุณมากสำหรับความพยายามในการตอบ :) - person Andrius Naruševičius; 09.06.2014

1) ฉันไม่เข้าใจว่าทำไมการใช้มุมมองจึงทำให้การเขียนโปรแกรมของคุณซับซ้อนเลย นั่นเป็นทางออกที่ดี การแทรกผลการแข่งขันและการได้ทีมชั้นนำเป็นการดำเนินการที่เป็นอิสระจากกันโดยสิ้นเชิง โปรดให้ฉันดูโค้ดบางส่วนเพื่อทำความเข้าใจว่าทำไมคุณถึงมีความสัมพันธ์ที่แน่นแฟ้นระหว่างสิ่งเหล่านั้นเช่นคะแนนการแข่งขันและคะแนนรวมของทีม

2) นี่เป็นตัวเลือกที่ไม่ดี: คุณต้องสร้างแบบสอบถามให้กับทุกทีม และถามคำถามเพิ่มเติมสำหรับแต่ละทีม ผลงานแย่!

3) เป็นการดีที่จะเห็นโค้ดของคุณเพื่อแสดงว่าคุณสามารถปรับปรุงการค้นหาของคุณได้อย่างไร ตัวอย่างเช่นการตรวจสอบค่าว่างก็ไม่ได้แย่ขนาดนั้น ง่ายพอๆ กับการใช้ตัวดำเนินการ ?? กล่าวคือ mt => mt.HomeTeamSocred ?? 0 จะแปลงค่าว่างเป็น 0 ได้อย่างง่ายดาย หากคุณแสดงสำนวนที่ใช้แล้ว ก็เป็นไปได้ที่จะดูว่าสามารถปรับปรุงและทำให้ง่ายขึ้นได้หรือไม่ อย่างไรก็ตาม ฉันสามารถเสนอสิ่งนี้ได้ ซึ่งไม่ได้ซับซ้อนขนาดนั้น:

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);

อย่างไรก็ตามมีบางอย่างที่ฉันไม่เข้าใจ เหตุใด Home/AwayTeamScored จึงเป็นโมฆะได้ เนื่องจากก่อนที่การแข่งขันจะเริ่มต้น Score จะต้องเป็นศูนย์สำหรับทั้งสองทีม ค่าว่างนั้นไม่สมเหตุสมผล วิธีนี้จะหลีกเลี่ยงปัญหาในการตรวจสอบค่าว่าง

4) ตัวเลือกนี้หมายความว่าอย่างไร

person JotaBe    schedule 09.06.2014