创建新的数据库条目并使用 viewbags 显示

creating new database entries and displaying using viewbags

我不确定我的代码哪里出了问题。 cshtml 页面未显示 ViewBag 列表。不知道是在添加新参与者到列表中还是调用列表显示时是否中断

基本上我想做的是列出篮球运动员的名单。如果某个年龄组和性别的列表不存在,我的代码应该创建列表,然后将参与者添加到其中。如果年龄组和性别的列表已经存在,它会简单地将参与者添加到该列表中。

代码的作用:

控制器:

[HttpPost]
[Route("postNewBasketballPlayer")]
public IActionResult PostNewBasketballPlayer(int newParticipantId)
{
    ViewModel.Participant newP = db.Participants.FirstOrDefault(i=>i.ParticipantId == newParticipantId);

    if (newP != null)
    {    
        var timespan = DateTime.Now - newP.ParticipantDOB;

        // this block of code is also repeated for newP.ParticipantGender == "Female"
        if (newP.ParticipantGender == "Male")
        {
             Console.WriteLine("Male basketball player");
             // this block of code repeats for different age groups
             if (timespan.TotalDays >= (7 * 365) && timespan.TotalDays < (9 * 365))
             {
                 League LeaguebbM7and8 = db.Leagues.FirstOrDefault(i=>i.gender == "Male" && i.ageRange == "7and8" && i.sport == "Basketball");

                 // if the League doesn't exist it creates it
                 if (LeaguebbM7and8 == null)
                 {
                     LeaguebbM7and8 = new League()
                     {
                         sport = "Basketball",
                         gender = "Male",
                         ageRange = "7and8"
                     };

                     db.Add(LeaguebbM7and8);
                     db.SaveChanges();

                     Console.WriteLine("made new league LeaguebbM7and8");

                     MMLeagueParticipant MMbbM7and8 = new MMLeagueParticipant()
                     {
                         ParticipantId = newParticipantId,
                         LeagueId = LeaguebbM7and8.LeagueId
                     };

                     // adds a row to table with the id's
                     db.Add(MMbbM7and8);
                     db.SaveChanges();

                     Console.WriteLine("added to LeaguebbM7and8");
                 }
                 // if the league exists; it simply adds it. 
                 else
                 {
                     // each time I add a child, it needs its own middle table (reason for new)
                     MMLeagueParticipant MMbbM7and8 = new MMLeagueParticipant()
                     {
                         ParticipantId = newParticipantId,
                         LeagueId = LeaguebbM7and8.LeagueId
                     };

                     db.Add(MMbbM7and8);
                     db.SaveChanges();

                     Console.WriteLine("added to LeaguebbM7and8");
                 }

                 Console.WriteLine("returning to dashboard (LeaguebbM7and8)");
                 return RedirectToAction("Dashboard", "Dashboard");
             }

             // this code is repeated for multiple age checks as above.
             // else if (timespan.TotalDays >= (9 * 365) && timespan.TotalDays < (11 * 365)){...}
             // else if (timespan.TotalDays >= (11 * 365) && timespan.TotalDays < (13 * 365)){...}
             // else if (timespan.TotalDays >= (13 * 365) && timespan.TotalDays < (15 * 365)){...}
             // else if (timespan.TotalDays >= (15 * 365) && timespan.TotalDays < (17 * 365)){...}
             // else if (timespan.TotalDays >= (17 * 365) && timespan.TotalDays < (19 * 365)){...}
         }

         // repeats the block of code above for all the age check if female.
         // if (newP.ParticipantGender == "Female)...
     }

     if (newP == null)
     {
         Console.WriteLine("id passed in was null");
         return RedirectToAction("Dashboard", "Dashboard");
     }

     Console.WriteLine("skips all if checks because newP was   null");
     return RedirectToAction("Dashboard", "Dashboard");
}

LeaguebbM7and8 为空时我的终端读取的内容:

    newParticipantId
    15
    newP
    LeagueProject.Models.ViewModel+Participant
    Male basketball player
    made new league LeaguebbM7and8
    added to LeaguebbM7and8
    returning to dashboard (LeaguebbM7and8)

当该性别和年龄组的列表已经存在时我的终端读取的内容(LeageuebbM7and8 不为空):

newParticipantId
15
newP
LeagueProject.Models.ViewModel+Participant
Male basketball player
added to LeaguebbM7and8
returning to dashboard (LeaguebbM7and8)

我的联盟模型:

int LeagueId { get; set; }
string sport { get; set; }
string gender { get; set; }
string ageRange { get; set; }
​List<MMLeagueParticipant> allParticipants  { get; set; }        //(a many to many relationship for league and participant)

我的 ViewModel 模型:

public List<Participant> allParticipants { get; set; }
public ViewModel.Participant participant { get; set; }

public class Participant
{
    int ParticipantId { get; set; }
    string ParticipantFirstName { get; set; }
    string ParticipantLastName { get; set; }
    string ParticipantGender { get; set; }
    SystemDateTime ParticipantDOB { get; set; }
    List<MMLeagueParticipant> all Leagues { get; set; }
}

我的 MMLeagueParticipant 中间 table 为联赛模型和 ViewModel.Participant:

int MMLeaugeParticipantId { get; set; }
int ParticipantId { get; set; }
int LeaugeId { get; set; }
leauge sport { get; set; }
ViewModel.Participant child { get; set; }

使用上面列出的模型;我正在尝试通过在调用我要显示的页面时执行以下操作来查询并获取我的参与者列表:

[HttpGet]
[Route("roster/basketball")]
public IActionResult BasketballRoster()
{
    //the reason im using viewbag is because its easier to work with since I can't pass multiple models in. and everything is in tabs. just makes life easier

    ViewBag.bbM7and8 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "7and8").ToList();
    Console.WriteLine(ViewBag.bbM7and8);

    ViewBag.bbM9and10 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "9and10").ToList();
    Console.WriteLine(ViewBag.bbM9and10);

    ViewBag.bbM11and12 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "11and12").ToList();
    Console.WriteLine(ViewBag.bbM11and12);

    ViewBag.bbM13and14 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "13and14").ToList();
    Console.WriteLine(ViewBag.bbM13and14);

    ViewBag.bbM15and16 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "15and16").ToList();
    Console.WriteLine(ViewBag.bbM15and16);

    ViewBag.bbM17and18 = db.Leagues.Where(i=>i.sport == "Basketball" && i.ageRange == "17and18").ToList();
    Console.WriteLine(ViewBag.bbM17and18);

    return View("RosterPageBasketball");
}

console.WriteLine(ViewBag.bbM7and8) 在终端中显示的内容:

System.Collections.Generic.List`1[LeagueProject.Models.League]

我的名册页包含不同的选项卡。每个选项卡显示“ViewBag.bbM#and#”(以及他们尊重的年龄组)和“测试 1”

            //cshtml

            <div class="tab-content" id="nav-tabContent">
                <div class="tab-pane fade" id="bbM7and8" role="tabpanel" aria-labelledby="bbM7and8-tab">
                    7-8
                    @{
                        var hasData=false;
                        //checks if list is null
                        if(ViewBag.bbM7and8 != null)
                        {
                            <p>test: ViewBag.bbM7and8 is not null</p>
                            @foreach(League j in ViewBag.bbM7and8)
                            {
                                <p>test 1: foreach</p>
                                @if( j.allParticipants != null) 
                                {
                                    <p>test 2: null</p>
                                    @foreach(MMLeagueParticipant mmLp in j.allParticipants)
                                    {
                                        <p>test 3: foreach</p>
                                        @if( mmLp.child != null) 
                                        {
                                            p>test 4: null</p>
                                            hasData=true;
                                            <p>@mmLp.child.ParticipantFirstName @mmLp.child.ParticipantLastName</p>
                                        }
                                    }
                                }
                                @if( j.allParticipants == null)
                                {
                                    <p>league model is null</p>
                                }
                            }
                        }
                        else
                        {
                            @if(!hasData)
                            {
                                <p>the list is empty</p>
                            }
                        }
                    }
                </div>
                //the above code is repeated for each tab
                <div class="tab-pane fade" id="bbM9and10" role="tabpanel" aria-labelledby="bbM9and10-tab"></div>
                <div class="tab-pane fade" id="bbM11and12" role="tabpanel" aria-labelledby="bbM11and12-tab"></div>
                <div class="tab-pane fade" id="bbM13and14" role="tabpanel" aria-labelledby="bbM13and14-tab"></div>
                <div class="tab-pane fade" id="bbM15and16" role="tabpanel" aria-labelledby="bbM15and16-tab"></div>
                <div class="tab-pane fade" id="bbM17and18" role="tabpanel" aria-labelledby="bbM17and18-tab"></div>

页面显示内容:

test b
7-8
test: ViewBag.bbM7and8 is not null

test 1: foreach
league model is null

无法测试 3。这意味着 ViewBag 中的每个联赛都是空的。这让我相信问题可能出在我如何创建和/或添加到联盟模型上。我觉得一切都很好。只需要第二双眼睛。

注意:这个viewbag我应该有2个参与者。

我尝试了什么:

问题是您的代码没有显式加载 allParticipants

Entity Framework 和 Linq-to-Entities 不会神奇地 从数据库加载整个对象图 - 您需要告诉它 何时 要加载的内容

此外,您应该始终更喜欢显式加载并禁用 EF 的延迟加载功能 - 这是因为 现代(和正确的)做事方式意味着您不能执行任何 IO在 属性 getters 中(因为副作用很糟糕,IO 通常涉及 async/await 并且你不能在 属性-getter).

您还可以极大地简化您的逻辑,以在给定 AgeRange 值列表的情况下加载所有 Basketball League 实体,除了显式加载的代码外,我在下面也这样做了所有相关 MMLeagueParticipantsParticipants 实体:

将您的加载代码更改为:

using System.Data.Entity;

[HttpGet]
[Route("roster/basketball")]
public async Task<IActionResult> GetBasketballRoster()
{
    String[] leagueNames = new[]
    {
        "7and8",
        "9and10",
        "11and12",
        "13and14",
        "15and16",
        "17and18"
    };

#if NEVER_DO_THIS

    // The *bad* way is to load the Leagues then manually load each League's participants individually - this will result in `1 + M` queries, or even `1 + M + ( M * P )` queries.

    List<League> bbLeagues = await db.Leagues
        .Where( l => l.Sport == "Basketball" )
        .Where( l => leagueNames.Contains( l.AgeRange ) )
        .ToListAsync();    

    
    foreach( League league in bbLeagues )
    {
        /*
        await db.Entry( league )
            .Collection( l => l.AllParticipants )
            .LoadAsync(); // Note this will not load `Participant`.
        */

        // This code does load `Participant` but we need to use `Query()`, but when you use `Query` EF isn't smart enough to know how to track the entities in-memory, so you need to manually add everything:

        List<MMLeagueParticipants> leagueParticipants = await db.Entry( league )
            .Collection( l => l.AllParticipants )
            .Query() // <-- This is needed to allow for `Include()`
            .Include( mmp => mmp.Participant )
            .ToListAsync();

        // Need to do this because EF won't do it for us when using `Query()`:
        league.AllParticipants.AddRange( leagueParticipants );
    }

#else

    // A better approach is to batch-load each entity (Leagues, MMLeagueParticipants, and Participants), resulting in only 3 queries:
    
    List<League> bbLeagues = await db.Leagues
        .Where( l => l.Sport == "Basketball" )
        .Where( l => leagueNames.Contains( l.AgeRange ) )
        .ToListAsync();    

    List<MMLeagueParticipants> links;
    if( true /* Using a manual JOIN: */ )
    {
        
        List<Int32> leagueIds = bbLeagues.Select( l => l.LeagueId ).ToList();

        links = await db.MMLeagueParticipants
            .Where( mmp => leagueIds.Contains( mmp.LeagueId ) )
            .ToListAsync();
    }
    else // Using a DB JOIN, assuming your Navigation Properties are configured correctly:
    {
        links = await db.MMLeagueParticipants
            .Where( mmp => mmp.League.Sport == "Basketball" )
            .Where( mmp => leagueNames.Contains( mmp.League.AgeRange ) )
            .ToListAsync();
    }

    List<Int32> participantIds = participants
        .Select( p => p.ParticipantId )
        .Distinct() // <-- This step prevents duplicate results.
        .ToList();

    List<Participant> participants = await db.Participants
        .Where( p => participantIds.Contains( p => p.ParticipantId ) )
        .ToListAsync();

    //

#endif

    // NOTE: Entity Framework automagically "loads" entities it reads from the database directly into the DbContext *and other loaded entities*, so after the above manual loading then all of the `League` objects will have correctly-populated `Participants` collections - but here's an assertion anyway:
    if( participants.Count > 0 && bbLeagues.All( l => l.AllParticipants == null || l.AllParticipants.Count() == 0 )
    {
        throw new InvalidOperationException( "No participants collections were updated by Entity Framework after loading participants. Is your model configured correctly?" );
    }

    IReadOnlyDictionary< String, League > leaguesDict = bbLeagues.ToDictionary( l => l.AgeRange );

    RosterPageViewModel pageViewModel = new RosterPageViewModel( leaguesDict );

    return View( "RosterPageBasketball", pageViewModel );
}

public class RosterPageViewModel
{
    public RosterPageViewModel(
        IReadOnlyDictionary< String, League > leagues
    )
    {
        this.Leagues = leagues ?? throw new ArgumentNullException(nameof(leagues));
    }

    public IReadOnlyDictionary< String, League > Leagues { get; }
}