Aracılığıyla paylaş


Öğretici: İlgili verileri güncelleştirme - MVC'ASP.NET ile EF Core

Önceki öğreticide ilgili verileri görüntülemişsiniz; bu öğreticide yabancı anahtar alanlarını ve gezinti özelliklerini güncelleştirerek ilgili verileri güncelleştireceksiniz.

Aşağıdaki çizimlerde, çalışacağınız bazı sayfalar gösterilmektedir.

Course Edit page

Edit Instructor page

Bu öğreticide şunları yaptınız:

  • Kurs sayfalarını özelleştirme
  • Eğitmen Ekleme Düzenleme sayfası
  • Düzenleme sayfasına kurs ekleme
  • Silme sayfasını güncelleştir
  • Oluştur sayfasına ofis konumu ve kursları ekleme

Ön koşullar

Kurs sayfalarını özelleştirme

Yeni Course bir varlık oluşturulduğunda, var olan bir departmanla ilişkisi olmalıdır. Bunu kolaylaştırmak için, yapı iskelesi oluşturulmuş kod denetleyici yöntemlerini ve bölümü seçmek için açılan liste içeren Oluşturma ve Düzenleme görünümlerini içerir. Açılan liste yabancı anahtar özelliğini ayarlar Course.DepartmentID ve gezinti özelliğini uygun Department varlığa yüklemek Department için Entity Framework'ün tüm ihtiyacı bu kadardır. İskeleli kodu kullanacaksınız, ancak hata işleme eklemek ve açılan listeyi sıralamak için biraz değiştireceksiniz.

içinde CoursesController.csdört Create and Edit yöntemini silin ve bunları aşağıdaki kodla değiştirin:

public IActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
{
    if (ModelState.IsValid)
    {
        _context.Add(course);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var courseToUpdate = await _context.Courses
        .FirstOrDefaultAsync(c => c.CourseID == id);

    if (await TryUpdateModelAsync<Course>(courseToUpdate,
        "",
        c => c.Credits, c => c.DepartmentID, c => c.Title))
    {
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
    return View(courseToUpdate);
}

HttpPost yönteminden Edit sonra, açılan liste için bölüm bilgilerini yükleyen yeni bir yöntem oluşturun.

private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
    var departmentsQuery = from d in _context.Departments
                           orderby d.Name
                           select d;
    ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
}

PopulateDepartmentsDropDownList yöntemi, ada göre sıralanmış tüm bölümlerin listesini alır, açılan liste için bir SelectList koleksiyon oluşturur ve koleksiyonu içindeki ViewBaggörünüme geçirir. yöntemi, arama kodunun açılan liste işlenirken seçilecek öğeyi belirtmesine izin veren isteğe bağlı selectedDepartment parametreyi kabul eder. Görünüm, etiket yardımcısına <select> "DepartmentID" adını geçirir ve yardımcı daha sonra "DepartmentID" adlı bir SelectList nesneye ViewBag bakacağını bilir.

HttpGet Create yöntemi, seçilen öğeyi ayarlamadan yöntemini çağırır PopulateDepartmentsDropDownList çünkü yeni bir kurs için bölüm henüz oluşturulmadı:

public IActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}

HttpGet Edit yöntemi, düzenlenen kursa zaten atanmış olan bölümün kimliğine göre seçili öğeyi ayarlar:

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

Hem hem de Create Edit için HttpPost yöntemleri, bir hatadan sonra sayfayı yeniden dağıttığında seçili öğeyi ayarlayan kodu içerir. Bu, sayfa yeniden görüntülendiğinde hata iletisini gösterecek şekilde görüntülendiğinde hangi departmanın seçildiğinin seçili kalmasını sağlar.

Ekle. Ayrıntılara AsNoTracking ve Delete yöntemleri

Kurs Ayrıntıları ve Silme sayfalarının performansını iyileştirmek için ve HttpGet Delete yöntemlerine Details çağrılar ekleyinAsNoTracking.

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .Include(c => c.Department)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }

    return View(course);
}
public async Task<IActionResult> Delete(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .Include(c => c.Department)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }

    return View(course);
}

Kurs görünümlerini değiştirme

içindeViews/Courses/Create.cshtml, Departman açılan listesine bir "Bölüm Seç" seçeneği ekleyin, başlık DepartmentID yerine Department olarak değiştirin ve bir doğrulama iletisi ekleyin.

<div class="form-group">
    <label asp-for="Department" class="control-label"></label>
    <select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
        <option value="">-- Select Department --</option>
    </select>
    <span asp-validation-for="DepartmentID" class="text-danger" />
</div>

içinde Views/Courses/Edit.cshtml, bölümünde yaptığınız Bölümün alanı için de aynı değişikliği yapın Create.cshtml.

Ayrıca içindeViews/Courses/Edit.cshtml, Başlık alanından önce bir kurs numarası alanı ekleyin. Kurs numarası birincil anahtar olduğundan görüntülenir, ancak değiştirilemez.

<div class="form-group">
    <label asp-for="CourseID" class="control-label"></label>
    <div>@Html.DisplayFor(model => model.CourseID)</div>
</div>

Düzenleme görünümünde kurs numarası için zaten gizli bir alan (<input type="hidden">) var. <label> Etiket yardımcısının eklenmesi, kullanıcı Düzenle sayfasında Kaydet'e tıkladığında kurs numarasının gönderilen verilere eklenmesine neden olmadığından gizli alan gereksinimini ortadan kaldırmaz.

içinde Views/Courses/Delete.cshtml, en üste bir kurs numarası alanı ekleyin ve bölüm kimliğini bölüm adıyla değiştirin.

@model ContosoUniversity.Models.Course

@{
    ViewData["Title"] = "Delete";
}

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Course</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Department.Name)
        </dd>
    </dl>
    
    <form asp-action="Delete">
        <div class="form-actions no-color">
            <input type="submit" value="Delete" class="btn btn-default" /> |
            <a asp-action="Index">Back to List</a>
        </div>
    </form>
</div>

içinde Views/Courses/Details.cshtml, için yaptığınız Delete.cshtmldeğişikliğin aynısını yapın.

Kurs sayfalarını test edin

Uygulamayı çalıştırın, Kurslar sekmesini seçin, Yeni Oluştur'a tıklayın ve yeni bir kursun verilerini girin:

Course Create page

Oluştur’a tıklayın. Kurs dizini sayfası, listeye eklenen yeni kursla birlikte görüntülenir. Dizin sayfası listesindeki bölüm adı, ilişkinin doğru kurulduğunu gösteren gezinti özelliğinden gelir.

Kurs dizini sayfasında bir kursta Düzenle'ye tıklayın.

Course Edit page

Sayfadaki verileri değiştirin ve Kaydet'e tıklayın. Kurs Dizini sayfası, güncelleştirilmiş kurs verileriyle birlikte görüntülenir.

Eğitmen Ekleme Düzenleme sayfası

Eğitmen kaydını düzenlerken, eğitmenin ofis ödevini güncelleştirebilmek istersiniz. Varlığın Instructor varlıkla OfficeAssignment bire sıfır veya bir ilişkisi vardır, yani kodunuzun aşağıdaki durumları işlemesi gerekir:

  • Kullanıcı office atamasını temizlerse ve başlangıçta bir değeri varsa varlığı silin OfficeAssignment .

  • Kullanıcı bir office atama değeri girerse ve başlangıçta boşsa yeni OfficeAssignment bir varlık oluşturun.

  • Kullanıcı bir office atamasının değerini değiştirirse, var olan OfficeAssignment bir varlıktaki değeri değiştirin.

Eğitmenler denetleyicisini güncelleştirme

içindeInstructorsController.cs, Eğitmen varlığının OfficeAssignment gezinti özelliğini yükleyip çağırabilmesi AsNoTrackingiçin HttpGet Edit yöntemindeki kodu değiştirin:

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructor = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (instructor == null)
    {
        return NotFound();
    }
    return View(instructor);
}

HttpPost Edit yöntemini office atama güncelleştirmelerini işlemek için aşağıdaki kodla değiştirin:

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructorToUpdate = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .FirstOrDefaultAsync(s => s.ID == id);

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
    {
        if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
        {
            instructorToUpdate.OfficeAssignment = null;
        }
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    return View(instructorToUpdate);
}

Kod aşağıdakileri yapar:

  • İmza artık HttpGet Edit yöntemiyle aynı olduğundan yöntem adını EditPost olarak değiştirir (ActionNameözniteliği URL'nin /Edit/ hala kullanıldığını belirtir).

  • Gezinti özelliği için istekli yükleme kullanarak veritabanından OfficeAssignment geçerli Instructor varlığı alır. Bu, HttpGet Edit yönteminde yaptığınız işlemle aynıdır.

  • Alınan Instructor varlığı model bağlayıcısından alınan değerlerle Güncelleştirmeler. Aşırı TryUpdateModel yükleme, eklemek istediğiniz özellikleri bildirmenizi sağlar. Bu, ikinci öğreticide açıklandığı gibi fazla göndermeyi önler.

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
    
  • Ofis konumu boşsa, tablodaki ilgili satırın Instructor.OfficeAssignment OfficeAssignment silinmesi için özelliği null olarak ayarlar.

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • Değişiklikleri veritabanına kaydeder.

Eğitmen Düzenleme görünümünü güncelleştirme

içindeViews/Instructors/Edit.cshtml, Kaydet düğmesinin sonuna ofis konumunu düzenlemek için yeni bir alan ekleyin:

<div class="form-group">
    <label asp-for="OfficeAssignment.Location" class="control-label"></label>
    <input asp-for="OfficeAssignment.Location" class="form-control" />
    <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>

Uygulamayı çalıştırın, Eğitmenler sekmesini seçin ve ardından bir eğitmende Düzenle'ye tıklayın. Office Konumunu değiştirin ve Kaydet'e tıklayın.

Instructor Edit page

Düzenleme sayfasına kurs ekleme

Eğitmenler herhangi bir sayıda ders verebilir. Şimdi, aşağıdaki ekran görüntüsünde gösterildiği gibi, bir onay kutusu grubu kullanarak kurs ödevlerini değiştirme özelliğini ekleyerek Eğitmen Düzenleme sayfasını geliştireceksiniz:

Instructor Edit page with courses

ve Instructor varlıkları arasındaki Course ilişki çoka çok şeklindedir. İlişki eklemek ve kaldırmak için birleştirme varlık kümesine varlık CourseAssignments ekleyip kaldırırsınız.

Eğitmenin hangi kurslara atandığını değiştirmenizi sağlayan kullanıcı arabirimi, bir onay kutuları grubudur. Veritabanındaki her kurs için bir onay kutusu görüntülenir ve eğitmenin şu anda atanmış olduğu kurslar seçilir. Kullanıcı, kurs ödevlerini değiştirmek için onay kutularını seçebilir veya temizleyebilir. Kurs sayısı çok daha fazlaysa, büyük olasılıkla verileri görünümde sunmak için farklı bir yöntem kullanmak istersiniz, ancak ilişkileri oluşturmak veya silmek için birleştirme varlığını işlemek için aynı yöntemi kullanırsınız.

Eğitmenler denetleyicisini güncelleştirme

Onay kutuları listesinin görünümüne veri sağlamak için bir görünüm modeli sınıfı kullanacaksınız.

SchoolViewModels klasöründe oluşturun AssignedCourseData.cs ve mevcut kodu aşağıdaki kodla değiştirin:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

içinde InstructorsController.csHttpGet Edit yöntemini aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructor = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (instructor == null)
    {
        return NotFound();
    }
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

private void PopulateAssignedCourseData(Instructor instructor)
{
    var allCourses = _context.Courses;
    var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
    var viewModel = new List<AssignedCourseData>();
    foreach (var course in allCourses)
    {
        viewModel.Add(new AssignedCourseData
        {
            CourseID = course.CourseID,
            Title = course.Title,
            Assigned = instructorCourses.Contains(course.CourseID)
        });
    }
    ViewData["Courses"] = viewModel;
}

Kod, gezinti özelliği için Courses hızlı yükleme ekler ve görünüm modeli sınıfını kullanarak AssignedCourseData onay kutusu dizisi için bilgi sağlamak üzere yeni PopulateAssignedCourseData yöntemi çağırır.

Yöntemindeki PopulateAssignedCourseData kod, görünüm modeli sınıfını kullanarak bir kurs listesi yüklemek için tüm Course varlıklarda okur. Her kurs için kod, kursun eğitmenin Courses gezinti özelliğinde mevcut olup olmadığını denetler. Eğitmene bir kursun atanıp atanmadığını denetlerken verimli bir arama oluşturmak için, eğitmene atanan kurslar bir HashSet koleksiyona alınır. Assigned Özellik, eğitmenin atandığı kurslar için true olarak ayarlanmıştır. Görünüm, hangi onay kutularının seçili olarak görüntülenmesi gerektiğini belirlemek için bu özelliği kullanır. Son olarak, liste içindeki ViewDatagörünüme geçirilir.

Ardından, kullanıcı Kaydet'e tıkladığında yürütülen kodu ekleyin. EditPost yöntemini aşağıdaki kodla değiştirin ve Instructor varlığının Courses gezinti özelliğini güncelleştiren yeni bir yöntem ekleyin.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructorToUpdate = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
        .FirstOrDefaultAsync(m => m.ID == id);

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
    {
        if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
        {
            instructorToUpdate.OfficeAssignment = null;
        }
        UpdateInstructorCourses(selectedCourses, instructorToUpdate);
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    UpdateInstructorCourses(selectedCourses, instructorToUpdate);
    PopulateAssignedCourseData(instructorToUpdate);
    return View(instructorToUpdate);
}
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Yöntem imzası artık HttpGet Edit yönteminden farklıdır, bu nedenle yöntem adı geriden EditPost olarak Editdeğişir.

Görünümde Kurs varlıkları koleksiyonu olmadığından, model bağlayıcısı gezinti özelliğini otomatik olarak güncelleştiremez CourseAssignments . Gezinti özelliğini güncelleştirmek için model bağlayıcısını CourseAssignments kullanmak yerine bunu yeni UpdateInstructorCourses yöntemde yaparsınız. Bu nedenle, özelliğini model bağlamasının CourseAssignments dışında tutmanız gerekir. Açık onay gerektiren ve CourseAssignments ekleme listesinde olmayan aşırı yüklemeyi kullandığınızdan bu, çağıran TryUpdateModel kodda herhangi bir değişiklik gerektirmez.

Onay kutusu seçilmediyse içindeki kod UpdateInstructorCourses , gezinti özelliğini boş bir koleksiyonla başlatır CourseAssignments ve şunu döndürür:

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Kod daha sonra veritabanındaki tüm kurslar arasında döngü oluşturur ve her kursu şu anda eğitmene atanmış olanlarla görünümde seçilenler arasında denetler. Verimli aramaları kolaylaştırmak için, ikinci iki koleksiyon nesnelerde HashSet depolanır.

Kursun onay kutusu seçiliyse ancak kurs gezinti özelliğinde Instructor.CourseAssignments değilse, kurs gezinti özelliğindeki koleksiyona eklenir.

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Kursun onay kutusu seçili değilse ancak kurs gezinti özelliğindeyse Instructor.CourseAssignments , kurs gezinti özelliğinden kaldırılır.

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Eğitmen görünümlerini güncelleştirme

içindeViews/Instructors/Edit.cshtml, Office alanının öğelerinin hemen arkasına div ve Kaydet düğmesinin öğesinden önce aşağıdaki kodu ekleyerek onay kutuları dizisi içeren div bir Kurslar alanı ekleyin.

Dekont

Kodu Visual Studio'ya yapıştırdığınızda satır sonları kodu bozacak şekilde değiştirilebilir. Yapıştırdıktan sonra kod farklı görünüyorsa, otomatik biçimlendirmeyi geri almak için bir kez Ctrl+Z tuşlarına basın. Bu, satır sonlarını burada gördüğünüz gibi görünecek şekilde düzeltir. Girintinin mükemmel olması gerekmez, ancak @:</tr><tr>, @:<td>, @:</td>ve @:</tr> satırlarının her birinin gösterildiği gibi tek bir satırda olması gerekir, aksi halde çalışma zamanı hatası alırsınız. Yeni kod bloğu seçiliyken, yeni kodu var olan kodla aynı hizaya getirmek için Sekme tuşuna üç kez basın. Bu sorun Visual Studio 2019'da düzeltildi.

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;

                    foreach (var course in courses)
                    {
                        if (cnt++ % 3 == 0)
                        {
                            @:</tr><tr>
                        }
                        @:<td>
                            <input type="checkbox"
                                   name="selectedCourses"
                                   value="@course.CourseID"
                                   @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
                                   @course.CourseID @:  @course.Title
                        @:</td>
                    }
                    @:</tr>
                }
        </table>
    </div>
</div>

Bu kod, üç sütunu olan bir HTML tablosu oluşturur. Her sütunda bir onay kutusu ve ardından kurs numarası ve başlığından oluşan bir başlık bulunur. Onay kutularının tümü aynı ada ("selectedCourses") sahiptir ve bu da model bağlayıcısına grup olarak ele alınacaklarını bildirir. Her onay kutusunun value özniteliği değerine CourseIDayarlanır. Sayfa gönderildiğinde, model bağlayıcısı denetleyiciye yalnızca seçilen onay kutularının değerlerinden CourseID oluşan bir dizi geçirir.

Onay kutuları başlangıçta işlendiğinde, eğitmene atanan kurslar için olanlar öznitelikleri denetlemiş olur ve bu öznitelikleri seçer (işaretli olarak görüntüler).

Uygulamayı çalıştırın, Eğitmenler sekmesini seçin ve Düzenle sayfasını görmek için eğitmende Düzenle'ye tıklayın.

Instructor Edit page with courses

Bazı kurs ödevlerini değiştirin ve Kaydet'e tıklayın. Yaptığınız değişiklikler Dizin sayfasına yansıtılır.

Dekont

Burada eğitmen kurs verilerini düzenlemek için uygulanan yaklaşım, sınırlı sayıda kurs olduğunda iyi sonuç verir. Çok daha büyük koleksiyonlar için farklı bir kullanıcı arabirimi ve farklı bir güncelleştirme yöntemi gerekir.

Silme sayfasını güncelleştir

içinde InstructorsController.csyöntemini silin DeleteConfirmed ve aşağıdaki kodu yerine ekleyin.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    Instructor instructor = await _context.Instructors
        .Include(i => i.CourseAssignments)
        .SingleAsync(i => i.ID == id);

    var departments = await _context.Departments
        .Where(d => d.InstructorID == id)
        .ToListAsync();
    departments.ForEach(d => d.InstructorID = null);

    _context.Instructors.Remove(instructor);

    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

Bu kod aşağıdaki değişiklikleri yapar:

  • Gezinti özelliği için hevesle CourseAssignments yükleme yapar. Bunu eklemeniz gerekir, aksi durumda EF ilgili CourseAssignment varlıklar hakkında bilgi sahibi olmaz ve bunları silmez. Bunları burada okumak zorunda kalmamak için veritabanında art arda silmeyi yapılandırabilirsiniz.

  • Silinecek eğitmen herhangi bir bölümün yöneticisi olarak atanırsa, bu departmanlardan eğitmen atamasını kaldırır.

Oluştur sayfasına ofis konumu ve kursları ekleme

içinde InstructorsController.cs, HttpGet ve HttpPost Create yöntemlerini silin ve sonra yerlerine aşağıdaki kodu ekleyin:

public IActionResult Create()
{
    var instructor = new Instructor();
    instructor.CourseAssignments = new List<CourseAssignment>();
    PopulateAssignedCourseData(instructor);
    return View();
}

// POST: Instructors/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
{
    if (selectedCourses != null)
    {
        instructor.CourseAssignments = new List<CourseAssignment>();
        foreach (var course in selectedCourses)
        {
            var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
            instructor.CourseAssignments.Add(courseToAdd);
        }
    }
    if (ModelState.IsValid)
    {
        _context.Add(instructor);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

Bu kod, yöntemler için Edit gördüğünüze benzer, ancak başlangıçta hiçbir kurs seçilmez. HttpGet Create yöntemi, seçilen kurslar olabileceğinden değil, görünümde döngü için boş bir koleksiyon sağlamak için foreach yöntemini çağırır PopulateAssignedCourseData (aksi takdirde görünüm kodu null başvuru özel durumu oluşturur).

HttpPost Create yöntemi, doğrulama hatalarını denetlemeden önce seçilen her kursu CourseAssignments gezinti özelliğine ekler ve yeni eğitmeni veritabanına ekler. Model hataları olsa bile kurslar eklenir, böylece model hataları olduğunda (örneğin, kullanıcı geçersiz bir tarihi anahtarladıysa) ve sayfa bir hata iletisiyle yeniden görüntülendiğinde, yapılan tüm kurs seçimleri otomatik olarak geri yüklenir.

Gezinti özelliğine kurs ekleyebilmek için CourseAssignments özelliği boş bir koleksiyon olarak başlatmanız gerektiğini fark edin:

instructor.CourseAssignments = new List<CourseAssignment>();

Bunu denetleyici kodunda yapmaya alternatif olarak, aşağıdaki örnekte gösterildiği gibi, özellik alıcısını koleksiyon yoksa otomatik olarak oluşturacak şekilde değiştirerek modelde yapabilirsiniz Instructor :

private ICollection<CourseAssignment> _courseAssignments;
public ICollection<CourseAssignment> CourseAssignments
{
    get
    {
        return _courseAssignments ?? (_courseAssignments = new List<CourseAssignment>());
    }
    set
    {
        _courseAssignments = value;
    }
}

Özelliğini bu şekilde değiştirirseniz CourseAssignments , denetleyicideki açık özellik başlatma kodunu kaldırabilirsiniz.

içinde Views/Instructor/Create.cshtml, Gönder düğmesinden önce kurslar için bir ofis konumu metin kutusu ve onay kutuları ekleyin. Düzenle sayfasında olduğu gibi, Visual Studio kodu yapıştırdığınızda yeniden biçimlendirirse biçimlendirmeyi düzeltin.

<div class="form-group">
    <label asp-for="OfficeAssignment.Location" class="control-label"></label>
    <input asp-for="OfficeAssignment.Location" class="form-control" />
    <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;

                    foreach (var course in courses)
                    {
                        if (cnt++ % 3 == 0)
                        {
                            @:</tr><tr>
                        }
                        @:<td>
                            <input type="checkbox"
                                   name="selectedCourses"
                                   value="@course.CourseID"
                                   @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
                                   @course.CourseID @:  @course.Title
                            @:</td>
                    }
                    @:</tr>
                }
        </table>
    </div>
</div>

Uygulamayı çalıştırıp eğitmen oluşturarak test edin.

İşlemleri İşleme

CRUD öğreticisinde açıklandığı gibi, Entity Framework işlemleri örtük olarak uygular. Daha fazla denetime ihtiyacınız olan senaryolar için (örneğin, bir işleme Entity Framework dışında yapılan işlemleri dahil etmek istiyorsanız) bkz . İşlemler.

Kodu alma

Tamamlanan uygulamayı indirin veya görüntüleyin.

Sonraki adımlar

Bu öğreticide şunları yaptınız:

  • Özelleştirilmiş Kurs sayfaları
  • Eğitmenler Düzenleme sayfası eklendi
  • Düzenleme sayfasına kurslar eklendi
  • Güncelleştirilmiş Silme sayfası
  • Oluştur sayfasına ofis konumu ve kursları eklendi

Eşzamanlılık çakışmalarını işlemeyi öğrenmek için sonraki öğreticiye geçin.