Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 外部联接在EF Core中未返回预期结果_C#_Tsql_Linq To Entities_Entity Framework Core_Outer Join - Fatal编程技术网

C# 外部联接在EF Core中未返回预期结果

C# 外部联接在EF Core中未返回预期结果,c#,tsql,linq-to-entities,entity-framework-core,outer-join,C#,Tsql,Linq To Entities,Entity Framework Core,Outer Join,在我的ASP.NET MVC核心应用程序中,POST操作方法Test未返回预期结果。该web应用程序是使用创建的,并进行了轻微修改。真正的应用程序可以并且正在使用最新版本的VS2015。该应用程序正在使用EF-Core。 如果下载项目,则需要执行以下步骤来测试上述意外结果: 注意:这些步骤的顺序很重要。这是一个非常小的测试项目。步骤2将创建一个名为ASPCore_Blogs的小型SQL Server数据库。因此,请确保SQL Server正在运行: 下载项目后,请确保在VS2015中打开项目之前

在我的ASP.NET MVC核心应用程序中,
POST
操作方法
Test
未返回预期结果。该web应用程序是使用创建的,并进行了轻微修改。真正的应用程序可以并且正在使用最新版本的
VS2015
。该应用程序正在使用
EF-Core
。 如果下载项目,则需要执行以下步骤来测试上述意外结果:

注意:这些步骤的顺序很重要。这是一个非常小的测试项目。步骤2将创建一个名为ASPCore_Blogs的小型SQL Server数据库。因此,请确保SQL Server正在运行:

  • 下载项目后,请确保在VS2015中打开项目之前从项目目录中删除
    .vs
    文件夹(如果项目挂起,您可能必须使用windows操作系统的
    任务管理器
    强制关闭它,然后重新打开它以使其正常工作。这是VS2015中的一个已知问题)
  • 打开
    startup.cs
    文件,在Configuration()方法中将数据库实例名称从
    MyComputer\SQLServerInstance
    更改为您正在使用的任何实例。在根目录中的
    appsettings.json
    文件中执行相同操作
  • 在VS2015 PM窗口中,运行PM>update database-context BloggingContext[确保SQL Server正在运行]
  • 然后运行:PM>updatedatabase-context-ApplicationDbContext
  • 运行web应用程序。通过输入登录/密码信息进行注册。登录需要在电子邮件中(test@test.com)形式。在主页的左侧:

  • 单击链接
    Blog Create
    ,创建4个Blog,如下所示:Blog1@test.com, Blog2@test.com, Blog3@test.com, Blog4@test.com

  • 单击链接
    Blogs Index
    ,验证上述4个blog是否已创建
  • 单击
    Test
    链接。此视图由
    GET
    操作方法
    Test
    调用。在相应的视图(
    Test.cshtml
    )中,您将看到页面上的
    Url
    列显示上述所有4个博客。和
    标题
    内容
    列为空白。将
    标题
    列填写为:标题1、标题2、标题3、标题4。将
    Content
    列填写为:Content1、Content2、Content3、Content4
  • 现在,转到名为
    ASPCore\u BlogsNAxis的相应SQL Server DB,并在
    Edit
    模式下打开
    Posts
    表,手动将年后列值分别更改为:1998199919982001(注:故意重复1998)
  • 现在,转到同一SQL Server数据库中的
    Blogs
    表,并输入一个额外的blog
    Blog5@test.com
  • 现在,运行web应用程序并再次单击
    Test
    链接(位于主页左侧)。您将看到,
    Get
    action方法
    Test
    使用left-outer-join来显示所有5个blog,但是右侧列(
    Title
    Content
    )的值在第5行中是空白的,这是因为left-outer-join不满足第5个blog的
    BlogId
    上的连接条件。到目前为止还不错
  • 现在,在
    Test.cshtml
    视图的
    Year
    下拉列表中,选择年份为1998年,然后单击
    GO
    按钮。根据
    POST
    操作方法
    Test
    的第一个
    if
    条件,应用程序应仅显示三条记录(两条记录用于1998年,第五条记录不满足加入条件):第一条、第三条和第五条记录
  • 但事实并非如此。当您通过从下拉列表中选择“不同年份”并单击“转到”按钮重复此操作时,您将看到输出不符合预期

    示例数据

    BlogId  Url        PostId  Content    PostYear  Title
      1     test1.com     1     Content1    1998    Title1
      3     test3com      3     Content2    1998    Title3
      5     test5.com     NULL  NULL        NULL    NULL
    
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
    
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int PostYear { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
    
    public class BlogsController : Controller
    {
        private readonly BloggingContext _context;
    
        public BlogsController(BloggingContext context)
        {
            _context = context;    
        }
    
        // GET: Blogs
        public async Task<IActionResult> Index()
        {
            return View(_context.Blogs.ToList());
        }
    
        // GET: /Blogs/Test
        [HttpGet]
        public async Task<IActionResult> Test(string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            //return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
            var qrVM = from b in _context.Blogs
                        join p in _context.Posts on b.BlogId equals p.BlogId into bp
                        from c in bp.DefaultIfEmpty()
                        select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
            return View(await qrVM.ToListAsync());
        }
    
        // POST: /Blogs/Test
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Test(List<BlogsWithRelatedPostsViewModel> list, string GO, int currentlySelectedIndex, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            if (!string.IsNullOrEmpty(GO))
            {
                var qrVM = from b in _context.Blogs
                            join p in _context.Posts on b.BlogId equals p.BlogId into bp
                            from c in bp.DefaultIfEmpty()
                            where c == null? true : c.PostYear.Equals(currentlySelectedIndex)
                            select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
                return View(await qrVM.ToListAsync());
            }
            else if (ModelState.IsValid)
            {
                foreach (var item in list)
                {
                    var oPost = _context.Posts.Where(r => r.PostId.Equals(item.PostID)).FirstOrDefault();
                    if (oPost != null)
                    {
                        oPost.Title = item.Title;
                        oPost.Content = item.Content;
                        oPost.PostYear = currentlySelectedIndex;
                        oPost.BlogId = item.BlogID; //according to new post below the blogId should exist for a newly created port - but just in case
                    }
                    else
                    {
                        if (item.PostID == 0)
                        {
                            Post oPostNew = new Post { BlogId = item.BlogID, Title = item.Title, Content = item.Content, PostYear = currentlySelectedIndex }; //need to use currentlySelectedIndex intead of item.FiscalYear in case of adding a record
                            _context.Add(oPostNew);
                        }
    
                    }
                }
                await _context.SaveChangesAsync();
                //return RedirectToLocal(returnUrl);
                return View(list);
            }
    
            // If we got this far, something failed, redisplay form
            return View();
        }
    
        // GET: Blogs/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // GET: Blogs/Create
        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }
    
        // POST: Blogs/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("BlogId,Url")] Blog blog)
        {
            if (ModelState.IsValid)
            {
                _context.Blogs.Add(blog);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
            return View(blog);
        }
    
        // POST: Blogs/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("BlogId,Url")] Blog blog)
        {
            if (id != blog.BlogId)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(blog);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!BlogExists(blog.BlogId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // POST: Blogs/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            _context.Blogs.Remove(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction("Index");
        }
    
        private bool BlogExists(int id)
        {
            return _context.Blogs.Any(e => e.BlogId == id);
        }
    }
    
    日志表数据:

    发布表格数据:

    Test
    Action GET方法中的左外部联接应返回:

    当您在下拉列表中选择1998年并单击Go按钮时,Test(…)Post action方法查询将返回但它会随机返回任何行

    BlogId  Url        PostId  Content    PostYear  Title
      1     test1.com     1     Content1    1998    Title1
      3     test3com      3     Content2    1998    Title3
      5     test5.com     NULL  NULL        NULL    NULL
    
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
    
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int PostYear { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
    
    public class BlogsController : Controller
    {
        private readonly BloggingContext _context;
    
        public BlogsController(BloggingContext context)
        {
            _context = context;    
        }
    
        // GET: Blogs
        public async Task<IActionResult> Index()
        {
            return View(_context.Blogs.ToList());
        }
    
        // GET: /Blogs/Test
        [HttpGet]
        public async Task<IActionResult> Test(string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            //return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
            var qrVM = from b in _context.Blogs
                        join p in _context.Posts on b.BlogId equals p.BlogId into bp
                        from c in bp.DefaultIfEmpty()
                        select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
            return View(await qrVM.ToListAsync());
        }
    
        // POST: /Blogs/Test
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Test(List<BlogsWithRelatedPostsViewModel> list, string GO, int currentlySelectedIndex, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            if (!string.IsNullOrEmpty(GO))
            {
                var qrVM = from b in _context.Blogs
                            join p in _context.Posts on b.BlogId equals p.BlogId into bp
                            from c in bp.DefaultIfEmpty()
                            where c == null? true : c.PostYear.Equals(currentlySelectedIndex)
                            select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
                return View(await qrVM.ToListAsync());
            }
            else if (ModelState.IsValid)
            {
                foreach (var item in list)
                {
                    var oPost = _context.Posts.Where(r => r.PostId.Equals(item.PostID)).FirstOrDefault();
                    if (oPost != null)
                    {
                        oPost.Title = item.Title;
                        oPost.Content = item.Content;
                        oPost.PostYear = currentlySelectedIndex;
                        oPost.BlogId = item.BlogID; //according to new post below the blogId should exist for a newly created port - but just in case
                    }
                    else
                    {
                        if (item.PostID == 0)
                        {
                            Post oPostNew = new Post { BlogId = item.BlogID, Title = item.Title, Content = item.Content, PostYear = currentlySelectedIndex }; //need to use currentlySelectedIndex intead of item.FiscalYear in case of adding a record
                            _context.Add(oPostNew);
                        }
    
                    }
                }
                await _context.SaveChangesAsync();
                //return RedirectToLocal(returnUrl);
                return View(list);
            }
    
            // If we got this far, something failed, redisplay form
            return View();
        }
    
        // GET: Blogs/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // GET: Blogs/Create
        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }
    
        // POST: Blogs/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("BlogId,Url")] Blog blog)
        {
            if (ModelState.IsValid)
            {
                _context.Blogs.Add(blog);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
            return View(blog);
        }
    
        // POST: Blogs/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("BlogId,Url")] Blog blog)
        {
            if (id != blog.BlogId)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(blog);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!BlogExists(blog.BlogId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // POST: Blogs/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            _context.Blogs.Remove(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction("Index");
        }
    
        private bool BlogExists(int id)
        {
            return _context.Blogs.Any(e => e.BlogId == id);
        }
    }
    
    型号

    BlogId  Url        PostId  Content    PostYear  Title
      1     test1.com     1     Content1    1998    Title1
      3     test3com      3     Content2    1998    Title3
      5     test5.com     NULL  NULL        NULL    NULL
    
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
    
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int PostYear { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
    
    public class BlogsController : Controller
    {
        private readonly BloggingContext _context;
    
        public BlogsController(BloggingContext context)
        {
            _context = context;    
        }
    
        // GET: Blogs
        public async Task<IActionResult> Index()
        {
            return View(_context.Blogs.ToList());
        }
    
        // GET: /Blogs/Test
        [HttpGet]
        public async Task<IActionResult> Test(string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            //return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
            var qrVM = from b in _context.Blogs
                        join p in _context.Posts on b.BlogId equals p.BlogId into bp
                        from c in bp.DefaultIfEmpty()
                        select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
            return View(await qrVM.ToListAsync());
        }
    
        // POST: /Blogs/Test
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Test(List<BlogsWithRelatedPostsViewModel> list, string GO, int currentlySelectedIndex, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            if (!string.IsNullOrEmpty(GO))
            {
                var qrVM = from b in _context.Blogs
                            join p in _context.Posts on b.BlogId equals p.BlogId into bp
                            from c in bp.DefaultIfEmpty()
                            where c == null? true : c.PostYear.Equals(currentlySelectedIndex)
                            select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
                return View(await qrVM.ToListAsync());
            }
            else if (ModelState.IsValid)
            {
                foreach (var item in list)
                {
                    var oPost = _context.Posts.Where(r => r.PostId.Equals(item.PostID)).FirstOrDefault();
                    if (oPost != null)
                    {
                        oPost.Title = item.Title;
                        oPost.Content = item.Content;
                        oPost.PostYear = currentlySelectedIndex;
                        oPost.BlogId = item.BlogID; //according to new post below the blogId should exist for a newly created port - but just in case
                    }
                    else
                    {
                        if (item.PostID == 0)
                        {
                            Post oPostNew = new Post { BlogId = item.BlogID, Title = item.Title, Content = item.Content, PostYear = currentlySelectedIndex }; //need to use currentlySelectedIndex intead of item.FiscalYear in case of adding a record
                            _context.Add(oPostNew);
                        }
    
                    }
                }
                await _context.SaveChangesAsync();
                //return RedirectToLocal(returnUrl);
                return View(list);
            }
    
            // If we got this far, something failed, redisplay form
            return View();
        }
    
        // GET: Blogs/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // GET: Blogs/Create
        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }
    
        // POST: Blogs/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("BlogId,Url")] Blog blog)
        {
            if (ModelState.IsValid)
            {
                _context.Blogs.Add(blog);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
            return View(blog);
        }
    
        // POST: Blogs/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("BlogId,Url")] Blog blog)
        {
            if (id != blog.BlogId)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(blog);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!BlogExists(blog.BlogId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // POST: Blogs/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            _context.Blogs.Remove(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction("Index");
        }
    
        private bool BlogExists(int id)
        {
            return _context.Blogs.Any(e => e.BlogId == id);
        }
    }
    
    公共类BloggingContext:DbContext
    {
    公共博客上下文(DbContextOptions)
    :基本(选项)
    { }
    公共数据库集博客{get;set;}
    公共DbSet Posts{get;set;}
    }
    公共类博客
    {
    public int BlogId{get;set;}
    公共字符串Url{get;set;}
    公共列表发布{get;set;}
    }
    公营职位
    {
    公共int PostId{get;set;}
    公共字符串标题{get;set;}
    公共字符串内容{get;set;}
    公共int年后{get;set;}
    public int BlogId{get;set;}
    公共博客Blog{get;set;}
    }
    
    博客控制器

    BlogId  Url        PostId  Content    PostYear  Title
      1     test1.com     1     Content1    1998    Title1
      3     test3com      3     Content2    1998    Title3
      5     test5.com     NULL  NULL        NULL    NULL
    
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
    
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public int PostYear { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
    
    public class BlogsController : Controller
    {
        private readonly BloggingContext _context;
    
        public BlogsController(BloggingContext context)
        {
            _context = context;    
        }
    
        // GET: Blogs
        public async Task<IActionResult> Index()
        {
            return View(_context.Blogs.ToList());
        }
    
        // GET: /Blogs/Test
        [HttpGet]
        public async Task<IActionResult> Test(string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            //return View(await _context.Blogs.Include(p => p.Posts).ToListAsync());
            var qrVM = from b in _context.Blogs
                        join p in _context.Posts on b.BlogId equals p.BlogId into bp
                        from c in bp.DefaultIfEmpty()
                        select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
            return View(await qrVM.ToListAsync());
        }
    
        // POST: /Blogs/Test
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Test(List<BlogsWithRelatedPostsViewModel> list, string GO, int currentlySelectedIndex, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
    
            if (!string.IsNullOrEmpty(GO))
            {
                var qrVM = from b in _context.Blogs
                            join p in _context.Posts on b.BlogId equals p.BlogId into bp
                            from c in bp.DefaultIfEmpty()
                            where c == null? true : c.PostYear.Equals(currentlySelectedIndex)
                            select new BlogsWithRelatedPostsViewModel { BlogID = b.BlogId, PostID = (c == null ? 0 : c.PostId), Url = b.Url, Title = (c == null ? string.Empty : c.Title), Content = (c == null ? string.Empty : c.Content) };
                return View(await qrVM.ToListAsync());
            }
            else if (ModelState.IsValid)
            {
                foreach (var item in list)
                {
                    var oPost = _context.Posts.Where(r => r.PostId.Equals(item.PostID)).FirstOrDefault();
                    if (oPost != null)
                    {
                        oPost.Title = item.Title;
                        oPost.Content = item.Content;
                        oPost.PostYear = currentlySelectedIndex;
                        oPost.BlogId = item.BlogID; //according to new post below the blogId should exist for a newly created port - but just in case
                    }
                    else
                    {
                        if (item.PostID == 0)
                        {
                            Post oPostNew = new Post { BlogId = item.BlogID, Title = item.Title, Content = item.Content, PostYear = currentlySelectedIndex }; //need to use currentlySelectedIndex intead of item.FiscalYear in case of adding a record
                            _context.Add(oPostNew);
                        }
    
                    }
                }
                await _context.SaveChangesAsync();
                //return RedirectToLocal(returnUrl);
                return View(list);
            }
    
            // If we got this far, something failed, redisplay form
            return View();
        }
    
        // GET: Blogs/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // GET: Blogs/Create
        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }
    
        // POST: Blogs/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("BlogId,Url")] Blog blog)
        {
            if (ModelState.IsValid)
            {
                _context.Blogs.Add(blog);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
            return View(blog);
        }
    
        // POST: Blogs/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("BlogId,Url")] Blog blog)
        {
            if (id != blog.BlogId)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(blog);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!BlogExists(blog.BlogId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    
        // GET: Blogs/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
    
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }
    
            return View(blog);
        }
    
        // POST: Blogs/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var blog = await _context.Blogs.SingleOrDefaultAsync(m => m.BlogId == id);
            _context.Blogs.Remove(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction("Index");
        }
    
        private bool BlogExists(int id)
        {
            return _context.Blogs.Any(e => e.BlogId == id);
        }
    }
    
    public类BlogsController:Controller
    {
    私有只读BloggingContext_context;
    公共博客控制器(博客上下文)
    {
    _上下文=上下文;
    }
    //获取:博客
    公共异步任务索引()
    {
    返回视图(_context.Blogs.ToList());
    }
    //获取:/Blogs/Test
    [HttpGet]
    公共异步任务测试(字符串returnUrl=null)
    {
    ViewData[“ReturnUrl”]=ReturnUrl;
    ViewBag.YearsList=Enumerable.Range(1996,29).Select(g=>new SelectListItem{Value=g.ToString(),Text=g.ToString()).ToList();
    //返回视图(wait_context.Blogs.Include(p=>p.Posts.ToListAsync());
    var qrVM=来自_context.Blogs中的b
    将b.BlogId上的p in_context.Posts等于p.BlogId加入bp
    来自bp.DefaultIfEmpty()中的c
    选择new BlogsWithRelatedPostsViewModel{BlogID=b.BlogID,PostID=(c==null?0:c.PostID),Url=b.Url,Title=(c==null?string.Empty:c.Title),Content=(c==null?string.Empty:c.Content)};
    返回视图(等待qrVM.ToListAsync());
    }
    //POST:/Blogs/测试
    [HttpPost]
    [ValidateAntiForgeryToken]
    公共异步任务测试(列表、字符串GO、int current